🚀. Socket Launch Week Day 3:Socket Firewall Now Blocks Malicious VS Code and Open VSX Extensions.Learn more
Sign In

typeui.sh

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

typeui.sh - npm Package Compare versions

Comparing version
0.5.0
to
0.6.0
+20
dist/skillMetadata.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SKILL_AUTHOR = void 0;
exports.slugifySkillName = slugifySkillName;
exports.buildDefaultSkillMetadata = buildDefaultSkillMetadata;
const designSystemSchema_1 = require("./domain/designSystemSchema");
exports.SKILL_AUTHOR = "typeui.sh";
function slugifySkillName(value) {
const slug = value
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
return slug || "design-system";
}
function buildDefaultSkillMetadata(productName) {
return designSystemSchema_1.SkillMetadataSchema.parse({
name: slugifySkillName(productName),
description: `${productName} style guide for AI coding agents.`
});
}
+11
-27

@@ -9,3 +9,2 @@ #!/usr/bin/env node

const commander_1 = require("commander");
const licenseService_1 = require("./licensing/licenseService");
const designSystem_1 = require("./prompts/designSystem");

@@ -20,2 +19,3 @@ const registry_1 = require("./prompts/registry");

const banner_1 = require("./ui/banner");
const skillMetadata_1 = require("./skillMetadata");
function parseProviderOption(raw) {

@@ -45,7 +45,8 @@ if (!raw) {

}
async function generateLike(mode, options) {
async function generateLike(action, mode, options) {
const selectedProviders = parseProviderOption(options.providers) ?? (await (0, designSystem_1.promptProviders)());
const providers = [...new Set([...types_1.ALWAYS_INCLUDED_PROVIDERS, ...selectedProviders])];
let designSystem;
if (mode === "updated") {
let metadata = (0, skillMetadata_1.buildDefaultSkillMetadata)("typeui.sh");
if (action === "update") {
const existing = await (0, existingDesignSystem_1.loadExistingDesignSystem)(process.cwd(), providers);

@@ -55,8 +56,10 @@ if (!existing) {

}
metadata = await (0, designSystem_1.promptSkillMetadata)(existing.metadata);
const fields = await (0, designSystem_1.promptDesignSystemFields)();
const updates = await (0, designSystem_1.promptDesignSystemUpdates)(existing, fields);
designSystem = { ...existing, ...updates };
const updates = await (0, designSystem_1.promptDesignSystemUpdates)(existing.designSystem, fields);
designSystem = { ...existing.designSystem, ...updates };
}
else {
designSystem = await (0, designSystem_1.promptDesignSystem)("typeui.sh");
metadata = await (0, designSystem_1.promptSkillMetadata)((0, skillMetadata_1.buildDefaultSkillMetadata)(designSystem.productName));
}

@@ -67,2 +70,3 @@ const results = await (0, runGeneration_1.runGeneration)({

designSystem,
metadata,
dryRun: Boolean(options.dryRun)

@@ -126,3 +130,3 @@ });

.action(async (options) => {
await generateLike(options.dryRun ? "preview" : "generated", options);
await generateLike("generate", options.dryRun ? "preview" : "generated", options);
});

@@ -135,3 +139,3 @@ program

.action(async (options) => {
await generateLike(options.dryRun ? "preview" : "updated", options);
await generateLike("update", options.dryRun ? "preview" : "updated", options);
});

@@ -154,22 +158,2 @@ program

});
program
.command("verify")
.description("Verify your license key and cache local license status.")
.action(async () => {
const record = await (0, licenseService_1.verifyAndCacheLicenseFromPrompt)();
console.log(`License cached (${record.licenseKeyFingerprint}) until ${record.expiresAt}`);
});
program
.command("license")
.description("Show local cached license status.")
.action(async () => {
console.log(await (0, licenseService_1.getCachedLicenseSummary)());
});
program
.command("clear-cache")
.description("Clear all local typeui.sh cache state.")
.action(async () => {
await (0, licenseService_1.clearCachedLicenseState)();
console.log("Cleared local cache state.");
});
program.parseAsync().catch((error) => {

@@ -176,0 +160,0 @@ const message = error instanceof Error ? error.message : String(error);

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LICENSE_CACHE_PATH = exports.LICENSE_CACHE_DIR = exports.REGISTRY_SPECS_URL = exports.POLAR_VERIFY_URL = exports.PRICING_URL = exports.GITHUB_REGISTRY_RAW_BASE_URL = exports.GITHUB_REGISTRY_REPO_URL = exports.API_DOMAIN = exports.MANAGED_BLOCK_END = exports.MANAGED_BLOCK_START = exports.PRODUCT_ID = void 0;
exports.getPolarVerifyUrl = getPolarVerifyUrl;
exports.REGISTRY_SPECS_URL = exports.GITHUB_REGISTRY_RAW_BASE_URL = exports.GITHUB_REGISTRY_REPO_URL = exports.API_DOMAIN = exports.MANAGED_BLOCK_END = exports.MANAGED_BLOCK_START = void 0;
exports.getRegistryPullUrl = getRegistryPullUrl;

@@ -12,5 +8,2 @@ exports.getRegistrySpecsUrl = getRegistrySpecsUrl;

exports.getDesignSystemPreviewUrl = getDesignSystemPreviewUrl;
const node_path_1 = __importDefault(require("node:path"));
const node_os_1 = __importDefault(require("node:os"));
exports.PRODUCT_ID = "typeui.sh";
exports.MANAGED_BLOCK_START = "<!-- TYPEUI_SH_MANAGED_START -->";

@@ -21,10 +14,3 @@ exports.MANAGED_BLOCK_END = "<!-- TYPEUI_SH_MANAGED_END -->";

exports.GITHUB_REGISTRY_RAW_BASE_URL = "https://raw.githubusercontent.com/bergside/awesome-design-skills/main";
exports.PRICING_URL = "https://www.typeui.sh/#pricing";
exports.POLAR_VERIFY_URL = `${exports.API_DOMAIN}/api/license/verify`;
exports.REGISTRY_SPECS_URL = `${exports.GITHUB_REGISTRY_RAW_BASE_URL}/skills/index.json`;
exports.LICENSE_CACHE_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".typeui-sh");
exports.LICENSE_CACHE_PATH = node_path_1.default.join(exports.LICENSE_CACHE_DIR, "license.json");
function getPolarVerifyUrl() {
return exports.POLAR_VERIFY_URL;
}
function getRegistryPullUrl(skillPath) {

@@ -31,0 +17,0 @@ const encodedPath = skillPath

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegistrySlugSchema = exports.FlatDesignSystemPromptSchema = exports.DesignSystemSchema = exports.ProviderSelectionSchema = void 0;
exports.RegistrySlugSchema = exports.FlatDesignSystemPromptSchema = exports.SkillMetadataSchema = exports.DesignSystemSchema = exports.ProviderSelectionSchema = void 0;
const zod_1 = require("zod");

@@ -27,3 +27,2 @@ const types_1 = require("../types");

spacingScale: zod_1.z.string().min(3),
componentFamilies: zod_1.z.array(zod_1.z.string().min(1)).min(1),
accessibilityRequirements: zod_1.z.string().min(3),

@@ -34,2 +33,16 @@ writingTone: zod_1.z.string().min(3),

});
exports.SkillMetadataSchema = zod_1.z.object({
name: zod_1.z
.string()
.trim()
.min(1, "Skill name is required.")
.max(100, "Skill name is too long.")
.regex(/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/, "Skill name must contain only lowercase letters, numbers, dashes, or underscores."),
description: zod_1.z
.string()
.trim()
.min(3, "Skill description is too short.")
.max(240, "Skill description is too long.")
.refine((value) => !/[\r\n]/.test(value), "Skill description must be a single line.")
});
exports.FlatDesignSystemPromptSchema = zod_1.z.object({

@@ -42,3 +55,2 @@ productName: zod_1.z.string().min(2),

spacingScale: zod_1.z.string().min(3),
componentFamilies: nonEmptyList,
accessibilityRequirements: zod_1.z.string().min(3),

@@ -45,0 +57,0 @@ writingTone: zod_1.z.string().min(3),

@@ -7,2 +7,3 @@ "use strict";

exports.parseManagedDesignSystem = parseManagedDesignSystem;
exports.parseSkillMetadata = parseSkillMetadata;
exports.loadExistingDesignSystem = loadExistingDesignSystem;

@@ -13,2 +14,3 @@ const promises_1 = __importDefault(require("node:fs/promises"));

const designSystemSchema_1 = require("../domain/designSystemSchema");
const skillMetadata_1 = require("../skillMetadata");
const types_1 = require("../types");

@@ -26,2 +28,22 @@ function escapeRegExp(value) {

}
function extractFrontmatter(content) {
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
return match?.[1] ?? null;
}
function parseFrontmatterField(frontmatter, key) {
const match = frontmatter.match(new RegExp(`^${escapeRegExp(key)}:\\s*(.+)$`, "m"));
return match?.[1]?.trim();
}
function parseQuotedYamlValue(value) {
const trimmed = value.trim();
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
return trimmed
.slice(1, -1)
.replace(/\\(["\\])/g, "$1");
}
if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
return trimmed.slice(1, -1).replace(/''/g, "'");
}
return trimmed;
}
function extractSection(body, title, nextTitle) {

@@ -57,3 +79,2 @@ const pattern = new RegExp(`${escapeRegExp(title)}\\n([\\s\\S]*?)\\n${escapeRegExp(nextTitle)}`, "m");

const spacingScale = extractStyleValue(managed, "Spacing scale");
const componentFamilies = extractListSection(managed, "## Component Families", "## Accessibility");
const accessibilityRequirements = extractSection(managed, "## Accessibility", "## Writing Tone");

@@ -64,3 +85,3 @@ const writingTone = extractSection(managed, "## Writing Tone", "## Rules: Do");

if (!productName ||
!brandSummary ||
brandSummary === null ||
!visualStyle ||

@@ -70,3 +91,2 @@ !typographyScale ||

!spacingScale ||
!componentFamilies ||
!accessibilityRequirements ||

@@ -85,3 +105,2 @@ !writingTone ||

spacingScale,
componentFamilies,
accessibilityRequirements,

@@ -93,2 +112,18 @@ writingTone,

}
function parseSkillMetadata(content) {
const frontmatter = extractFrontmatter(content);
if (!frontmatter) {
return null;
}
const name = parseFrontmatterField(frontmatter, "name");
const description = parseFrontmatterField(frontmatter, "description");
if (!name || !description) {
return null;
}
const parsed = designSystemSchema_1.SkillMetadataSchema.safeParse({
name: parseQuotedYamlValue(name),
description: parseQuotedYamlValue(description)
});
return parsed.success ? parsed.data : null;
}
async function loadExistingDesignSystem(projectRoot, providers) {

@@ -101,3 +136,7 @@ for (const provider of providers) {

if (parsed) {
return parsed;
const metadata = parseSkillMetadata(content) ?? (0, skillMetadata_1.buildDefaultSkillMetadata)(parsed.productName);
return {
designSystem: parsed,
metadata
};
}

@@ -104,0 +143,0 @@ }

@@ -7,3 +7,3 @@ "use strict";

async function runGeneration(options) {
const providerFiles = (0, renderers_1.renderProviderFiles)(options.designSystem, options.providers);
const providerFiles = (0, renderers_1.renderProviderFiles)(options.designSystem, options.providers, options.metadata);
const results = [];

@@ -10,0 +10,0 @@ for (const file of providerFiles) {

@@ -10,2 +10,29 @@ "use strict";

const config_1 = require("../config");
function extractManagedBlock(content) {
const startIdx = content.indexOf(config_1.MANAGED_BLOCK_START);
const endIdx = content.indexOf(config_1.MANAGED_BLOCK_END);
if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) {
return null;
}
return content.slice(startIdx, endIdx + config_1.MANAGED_BLOCK_END.length);
}
function extractFrontmatter(content) {
const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
return match?.[0] ?? null;
}
function removeLeadingFrontmatter(content) {
const frontmatter = extractFrontmatter(content);
if (!frontmatter) {
return content;
}
return content.slice(frontmatter.length);
}
function applyFrontmatter(content, frontmatter) {
const withoutFrontmatter = removeLeadingFrontmatter(content).trimStart();
const normalizedFrontmatter = frontmatter.trimEnd();
if (!withoutFrontmatter) {
return `${normalizedFrontmatter}\n`;
}
return `${normalizedFrontmatter}\n\n${withoutFrontmatter}`;
}
function mergeWithManagedBlock(existing, generatedBlock) {

@@ -26,3 +53,3 @@ const startIdx = existing.indexOf(config_1.MANAGED_BLOCK_START);

}
async function upsertManagedSkillFile(projectRoot, relativePath, generatedBlock, dryRun = false) {
async function upsertManagedSkillFile(projectRoot, relativePath, generatedContent, dryRun = false) {
const absPath = node_path_1.default.resolve(projectRoot, relativePath);

@@ -40,3 +67,8 @@ await promises_1.default.mkdir(node_path_1.default.dirname(absPath), { recursive: true });

}
const nextContent = mergeWithManagedBlock(existing, generatedBlock);
const generatedBlock = extractManagedBlock(generatedContent) ?? generatedContent;
const generatedFrontmatter = extractFrontmatter(generatedContent);
let nextContent = mergeWithManagedBlock(existing, generatedBlock);
if (generatedFrontmatter) {
nextContent = applyFrontmatter(nextContent, generatedFrontmatter);
}
const changed = existing !== nextContent;

@@ -43,0 +75,0 @@ if (!dryRun && changed) {

@@ -7,2 +7,3 @@ "use strict";

exports.promptDesignSystemUpdates = promptDesignSystemUpdates;
exports.promptSkillMetadata = promptSkillMetadata;
exports.listSupportedProviders = listSupportedProviders;

@@ -41,3 +42,2 @@ const designSystemSchema_1 = require("../domain/designSystemSchema");

{ name: "Spacing scale", value: "spacingScale" },
{ name: "Component families", value: "componentFamilies" },
{ name: "Accessibility requirements", value: "accessibilityRequirements" },

@@ -287,47 +287,2 @@ { name: "Writing tone", value: "writingTone" },

];
const COMPONENT_FAMILY_OPTIONS = [
"buttons",
"inputs",
"forms",
"selects/comboboxes",
"checkboxes/radios/switches",
"textareas",
"date/time pickers",
"file uploaders",
"cards",
"tables",
"data lists",
"data grids",
"charts",
"stats/metrics",
"badges/chips",
"avatars",
"breadcrumbs",
"pagination",
"steppers",
"modals",
"drawers/sheets",
"tooltips",
"popovers/menus",
"navigation",
"sidebars",
"top bars/headers",
"command palette",
"tabs",
"accordions",
"carousels",
"progress indicators",
"skeletons",
"alerts/toasts",
"notifications center",
"search",
"empty states",
"onboarding",
"authentication screens",
"settings pages",
"documentation layouts",
"feedback components",
"pricing blocks",
"data visualization wrappers"
];
const ACCESSIBILITY_OPTIONS = [

@@ -512,7 +467,2 @@ "WCAG 2.2 AA",

});
const componentFamilies = await promptPresetSelection({
message: "Select component families to prioritize:",
presets: COMPONENT_FAMILY_OPTIONS,
defaultSelected: COMPONENT_FAMILY_OPTIONS
});
const accessibilityRequirements = await promptPresetSelection({

@@ -553,3 +503,2 @@ message: "Select accessibility requirements:",

spacingScale,
componentFamilies,
accessibilityRequirements: accessibilityRequirements.join(", "),

@@ -566,5 +515,4 @@ writingTone: writingTone.join(", "),

name: "fields",
message: "Select fields to update:",
choices: designFieldChoices,
validate: (value) => value.length > 0 || "Select at least one field."
message: "Select design fields to update (optional):",
choices: designFieldChoices
}

@@ -631,12 +579,2 @@ ]);

}
case "componentFamilies": {
const defaults = matchPresetDefaults(current.componentFamilies.join(", "), COMPONENT_FAMILY_OPTIONS);
updates.componentFamilies = await promptPresetSelection({
message: "Select component families to prioritize:",
presets: COMPONENT_FAMILY_OPTIONS,
defaultSelected: defaults.selected.length > 0 ? defaults.selected : COMPONENT_FAMILY_OPTIONS,
defaultCustom: defaults.custom
});
break;
}
case "accessibilityRequirements": {

@@ -693,4 +631,28 @@ const defaults = matchPresetDefaults(current.accessibilityRequirements, ACCESSIBILITY_OPTIONS);

}
async function promptSkillMetadata(defaults) {
const answers = await prompt([
{
type: "input",
name: "name",
message: "Skill name (slug, e.g. next-best-practices):",
default: defaults?.name ?? "design-system",
validate: (value) => designSystemSchema_1.SkillMetadataSchema.pick({ name: true }).safeParse({ name: value }).success ||
"Use lowercase letters, numbers, dashes, or underscores."
},
{
type: "input",
name: "description",
message: "Skill description (single line):",
default: defaults?.description ?? "",
validate: (value) => designSystemSchema_1.SkillMetadataSchema.pick({ description: true }).safeParse({ description: value }).success ||
"Description must be a single non-empty line."
}
]);
return designSystemSchema_1.SkillMetadataSchema.parse({
name: answers.name,
description: answers.description
});
}
function listSupportedProviders() {
return types_1.SUPPORTED_PROVIDERS;
}

@@ -6,8 +6,8 @@ "use strict";

const shared_1 = require("./shared");
function renderProviderFiles(design, providers) {
function renderProviderFiles(design, providers, metadata) {
return providers.map((provider) => ({
provider,
relativePath: types_1.PROVIDER_DETAILS[provider].relativePath,
content: (0, shared_1.createManagedSkillBody)(types_1.PROVIDER_DETAILS[provider].title, design)
content: (0, shared_1.createManagedSkillFile)(types_1.PROVIDER_DETAILS[provider].title, design, metadata)
}));
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createManagedSkillBody = createManagedSkillBody;
exports.createSkillFrontmatter = createSkillFrontmatter;
exports.createManagedSkillFile = createManagedSkillFile;
const config_1 = require("../config");
const skillMetadata_1 = require("../skillMetadata");
function list(items) {

@@ -26,5 +29,2 @@ return items.map((item) => `- ${item}`).join("\n");

"",
"## Component Families",
list(design.componentFamilies),
"",
"## Accessibility",

@@ -86,1 +86,17 @@ design.accessibilityRequirements,

}
function escapeYamlString(value) {
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
}
function createSkillFrontmatter(metadata) {
return [
"---",
`name: "${escapeYamlString(metadata.name)}"`,
`description: "${escapeYamlString(metadata.description)}"`,
"metadata:",
` author: ${escapeYamlString(skillMetadata_1.SKILL_AUTHOR)}`,
"---"
].join("\n");
}
function createManagedSkillFile(providerTitle, design, metadata) {
return `${createSkillFrontmatter(metadata)}\n\n${createManagedSkillBody(providerTitle, design)}`;
}

@@ -157,3 +157,2 @@ "use strict";

"spacingScale",
"componentFamilies",
"accessibilityRequirements",

@@ -160,0 +159,0 @@ "writingTone",

{
"name": "typeui.sh",
"version": "0.5.0",
"version": "0.6.0",
"description": "Generate design system specifications and style guides as skill files for AI coding providers",

@@ -24,9 +24,15 @@ "main": "dist/cli.js",

"type": "git",
"url": "https://github.com/bergside/typeui.sh"
"url": "https://github.com/bergside/typeui"
},
"keywords": [
"cli",
"design-system",
"design system",
"skills",
"ai"
"ai",
"design md",
"design skills",
"claude skills",
"ai skills",
"ui",
"ux"
],

@@ -33,0 +39,0 @@ "author": "https://bergside.com",

+18
-16

@@ -24,3 +24,3 @@ ```

Check out all [design systems](https://typeui.sh/design-systems) that can be pulled into your project.
Check out all [design systems](https://typeui.sh/design-skills) that can be pulled into your project.

@@ -35,21 +35,23 @@ ## Available commands

| `list` | Show available registry specs from `bergside/awesome-design-skills` (with typeui.sh preview links), then pull one automatically. |
| `verify` | Verify your license key (pro version) and cache local license status. |
| `license` | Show local cached license status. |
| `clear-cache` | Remove local cache state (`~/.typeui-sh`). |
Shared options for `generate` and `update`:
## Design Skill File Structure
- `-p, --providers <providers>` (comma-separated provider keys)
- `--dry-run` (preview changes without writing files)
Here's a breakdown of the design skill file that is being generated by the TypeUI CLI.
Shared options for `pull`:
| Section | What it does |
| --- | --- |
| `Mission` | Defines the design-system objective and expected output quality for the agent. |
| `Brand` | Captures product context and brand direction to anchor decisions. |
| `Style Foundations` | Defines core visual tokens and constraints (visual style, typography, color palette, spacing). |
| `Accessibility` | States accessibility standards and non-negotiable requirements. |
| `Writing Tone` | Sets tone/style for generated guidance language. |
| `Rules: Do` | Lists required implementation practices to follow. |
| `Rules: Don't` | Lists anti-patterns and prohibited behaviors. |
| `Expected Behavior` | Sets expectations for decision-making and trade-off handling. |
| `Guideline Authoring Workflow` | Gives the ordered process the agent should follow when producing guidelines. |
| `Required Output Structure` | Enforces the final response format for consistency and completeness. |
| `Component Rule Expectations` | Defines required interaction/state details in component guidance. |
| `Quality Gates` | Adds validation criteria for clarity, testability, and consistency. |
| `Example Constraint Language` | Standardizes wording strength (`must` vs `should`) and constraint style. |
- `-p, --providers <providers>` (comma-separated provider keys)
- `--dry-run` (preview changes without writing files)
Shared options for `list`:
- `-p, --providers <providers>` (comma-separated providers passed through to auto-pull)
- `--dry-run` (preview pull file changes without writing)
For local development:

@@ -56,0 +58,0 @@

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readLicenseCache = readLicenseCache;
exports.writeLicenseCache = writeLicenseCache;
exports.isCacheRecordValid = isCacheRecordValid;
exports.fingerprintToken = fingerprintToken;
exports.clearLocalLicenseState = clearLocalLicenseState;
const node_crypto_1 = __importDefault(require("node:crypto"));
const promises_1 = __importDefault(require("node:fs/promises"));
const config_1 = require("../config");
async function readLicenseCache() {
try {
const raw = await promises_1.default.readFile(config_1.LICENSE_CACHE_PATH, "utf8");
const parsed = JSON.parse(raw);
if (!parsed.productId || !parsed.expiresAt || !parsed.licenseKeyFingerprint) {
return null;
}
if (parsed.productId !== config_1.PRODUCT_ID) {
return null;
}
return {
productId: parsed.productId,
verifiedAt: parsed.verifiedAt ?? new Date().toISOString(),
expiresAt: parsed.expiresAt,
licenseKeyFingerprint: parsed.licenseKeyFingerprint,
licenseKey: typeof parsed.licenseKey === "string" ? parsed.licenseKey : undefined
};
}
catch (error) {
const e = error;
if (e.code === "ENOENT") {
return null;
}
throw error;
}
}
async function writeLicenseCache(record) {
await promises_1.default.mkdir(config_1.LICENSE_CACHE_DIR, { recursive: true });
await promises_1.default.writeFile(config_1.LICENSE_CACHE_PATH, JSON.stringify(record, null, 2), "utf8");
}
function isCacheRecordValid(record) {
const expires = new Date(record.expiresAt).getTime();
return Number.isFinite(expires) && expires > Date.now();
}
function fingerprintToken(token) {
return node_crypto_1.default.createHash("sha256").update(token).digest("hex").slice(0, 16);
}
async function clearLocalLicenseState() {
await promises_1.default.rm(config_1.LICENSE_CACHE_DIR, { recursive: true, force: true });
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureVerifiedAccess = ensureVerifiedAccess;
exports.getVerifiedLicenseKey = getVerifiedLicenseKey;
exports.verifyAndCacheLicenseFromPrompt = verifyAndCacheLicenseFromPrompt;
exports.getCachedLicenseSummary = getCachedLicenseSummary;
exports.clearCachedLicenseState = clearCachedLicenseState;
const config_1 = require("../config");
const license_1 = require("../prompts/license");
const licenseCache_1 = require("./licenseCache");
const polarClient_1 = require("./polarClient");
function buildCacheRecord(licenseKey, expiresAt) {
return {
productId: config_1.PRODUCT_ID,
verifiedAt: new Date().toISOString(),
expiresAt,
licenseKeyFingerprint: (0, licenseCache_1.fingerprintToken)(licenseKey),
licenseKey
};
}
function isInvalidLicenseReason(reason) {
const normalized = reason.toLowerCase();
return (normalized === "not_found" ||
normalized.includes("license_invalid") ||
normalized.includes("license key is not valid") ||
normalized.includes("invalid license"));
}
function withLicensePurchaseHelp(reason) {
if (!isInvalidLicenseReason(reason)) {
return reason;
}
return `${reason} You can get a license key at ${config_1.PRICING_URL}.`;
}
async function ensureVerifiedAccess() {
const cached = await (0, licenseCache_1.readLicenseCache)();
if (cached && (0, licenseCache_1.isCacheRecordValid)(cached)) {
return;
}
const { licenseKey } = await (0, license_1.promptLicenseCredentials)();
const verifyResult = await (0, polarClient_1.verifyPurchaseWithPolar)(licenseKey);
if (!verifyResult.ok) {
throw new Error(`License verification failed: ${withLicensePurchaseHelp(verifyResult.reason)}`);
}
await (0, licenseCache_1.writeLicenseCache)(buildCacheRecord(licenseKey, verifyResult.expiresAt));
}
async function getVerifiedLicenseKey() {
const cached = await (0, licenseCache_1.readLicenseCache)();
if (cached && (0, licenseCache_1.isCacheRecordValid)(cached) && cached.licenseKey) {
return cached.licenseKey;
}
const { licenseKey } = await (0, license_1.promptLicenseCredentials)();
const verifyResult = await (0, polarClient_1.verifyPurchaseWithPolar)(licenseKey);
if (!verifyResult.ok) {
throw new Error(`License verification failed: ${withLicensePurchaseHelp(verifyResult.reason)}`);
}
await (0, licenseCache_1.writeLicenseCache)(buildCacheRecord(licenseKey, verifyResult.expiresAt));
return licenseKey;
}
async function verifyAndCacheLicenseFromPrompt() {
const { licenseKey } = await (0, license_1.promptLicenseCredentials)();
const verifyResult = await (0, polarClient_1.verifyPurchaseWithPolar)(licenseKey);
if (!verifyResult.ok) {
throw new Error(`License verification failed: ${withLicensePurchaseHelp(verifyResult.reason)}`);
}
const cacheRecord = buildCacheRecord(licenseKey, verifyResult.expiresAt);
await (0, licenseCache_1.writeLicenseCache)(cacheRecord);
return cacheRecord;
}
async function getCachedLicenseSummary() {
const cached = await (0, licenseCache_1.readLicenseCache)();
if (!cached) {
return "No cached license.";
}
const status = (0, licenseCache_1.isCacheRecordValid)(cached) ? "valid" : "expired";
return `Cached license (${cached.licenseKeyFingerprint}) is ${status} until ${cached.expiresAt}.`;
}
async function clearCachedLicenseState() {
await (0, licenseCache_1.clearLocalLicenseState)();
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyPurchaseWithPolar = verifyPurchaseWithPolar;
const config_1 = require("../config");
const VERIFY_CACHE_TTL_DAYS = 31;
async function verifyPurchaseWithPolar(licenseKey) {
const verifyUrl = (0, config_1.getPolarVerifyUrl)();
let response;
try {
response = await fetch(verifyUrl, {
method: "POST",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
licenseKey
})
});
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
ok: false,
reason: `Could not reach license server at ${verifyUrl}: ${message}. Please ensure your network connection is available and try again.`
};
}
if (!response.ok) {
let serverReason;
try {
const errorData = (await response.json());
serverReason = errorData.reason || errorData.error;
}
catch {
// Ignore JSON parse failures and fall back to status-only message.
}
return {
ok: false,
reason: serverReason
? `License verification failed (${response.status}): ${serverReason}.`
: `License verification failed (${response.status}).`
};
}
const data = (await response.json());
if (!data.valid || data.reason !== "active" || data.status !== "granted") {
return { ok: false, reason: data.reason || data.error || "License key is not valid." };
}
return {
ok: true,
// Local cache is fixed to 31 days from verification.
expiresAt: new Date(Date.now() + VERIFY_CACHE_TTL_DAYS * 24 * 60 * 60 * 1000).toISOString()
};
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.promptLicenseCredentials = promptLicenseCredentials;
async function loadInquirer() {
const dynamicImport = new Function("specifier", "return import(specifier)");
const inquirerModule = await dynamicImport("inquirer");
return inquirerModule.default;
}
async function prompt(questions) {
const inquirer = await loadInquirer();
return (await inquirer.prompt(questions));
}
async function promptLicenseCredentials() {
const answers = await prompt([
{
type: "password",
name: "licenseKey",
message: "License key:",
mask: "*",
validate: (value) => value.trim().length > 5 || "License key is required."
}
]);
return {
licenseKey: answers.licenseKey.trim()
};
}
# Registry Source (GitHub)
This document captures how CLI registry commands resolve data from the GitHub repository:
- [https://github.com/bergside/awesome-design-skills](https://github.com/bergside/awesome-design-skills)
## Index source used by `list` and `pull`
Both commands read:
- `GET https://raw.githubusercontent.com/bergside/awesome-design-skills/main/skills/index.json`
Expected shape:
```json
{
"paper": {
"slug": "paper",
"name": "Paper",
"skillPath": "skills/paper/SKILL.md"
}
}
```
The CLI maps each index entry to:
- `name` -> display name
- `slug` -> selection and pull key
- `previewUrl` -> repository page (`/tree/main/skills/<slug>`)
- `hasSkillMd` -> `true` when `skillPath` is non-empty
## Pull behavior
For `pull <slug>`:
1. Validate slug format locally.
2. Read `skills/index.json`.
3. Resolve `index[slug].skillPath`.
4. Fetch markdown from raw GitHub URL:
- `GET https://raw.githubusercontent.com/bergside/awesome-design-skills/main/<skillPath>`
On success, response is markdown text (`text/markdown` or plain text accepted).
Common failure reasons surfaced by CLI:
- `not_found` (missing slug in index or missing markdown file)
- invalid index JSON/shape
- network/unreachable raw GitHub URLs
# typeui.sh User Guide
This guide is for end users who run the `typeui.sh` CLI in a project to generate and maintain design-system skill files.
## What typeui.sh does
`typeui.sh` asks you a series of interactive questions about your design system, then generates `SKILL.md` files for coding agents.
- Universal target is always generated: `.agents/skills/design-system/SKILL.md`
- You can optionally generate additional agent-specific files during the provider selection step.
## Before you start
- Node.js 18+ recommended
- A valid license key from your Polar purchase (optional, only needed for pro features such as `verify`/`license`)
## Install and run
Use the published CLI (recommended for end users):
```bash
npx typeui.sh --help
```
Or if the package is already installed globally:
```bash
typeui.sh --help
```
## License key activation (optional, Polar purchase)
### 1) Get your license key
After purchase, Polar provides your license key. Copy it exactly.
### 2) Verify against the default endpoint
`typeui.sh` verifies your key directly with:
- `https://typeui.sh/api/license/verify`
### 3) Activate in CLI (for license-aware commands)
Run:
```bash
npx typeui.sh verify
```
You will be prompted for:
- `License key:` (hidden input)
If valid, the CLI caches your license locally in:
- `~/.typeui-sh/license.json`
Useful license commands:
- `npx typeui.sh license` - show cached license status
- `npx typeui.sh clear-cache` - clear local cache and force re-verification
## Generate design-system skills (free)
Run:
```bash
npx typeui.sh generate
```
### Provider selection behavior
At the start of the flow:
- `.agents/skills` is included automatically
- A short list is shown so you know which agents are covered by the universal target
- You can select additional optional provider targets
### Design-system interview flow
You will be prompted for:
1. Product basics
- Product name
- Brand summary
2. Visual style directions
- Multi-select preset list (+ optional custom values)
3. Typography
- Typography scale strategy (exactly one selected)
- Primary UI font family (Google Fonts, choose one)
- Display/heading font family (Google Fonts, choose one)
- Monospace font family (Google Fonts, choose one)
- Core font weights (checkbox list, all selected by default)
4. Color palette
- Palette guidance presets (+ optional custom values)
- Token values for primary, secondary, success, warning, danger, surface, text
5. Spacing
- Spacing scale guidance (exactly one selected)
6. Component families
- Multi-select list (+ optional custom values)
7. Accessibility requirements
- Multi-select list (+ optional custom values)
8. Writing tone
- Multi-select list (+ optional custom values)
9. Rules
- Design DO rules (multi-select + optional custom)
- Design DON'T rules (multi-select + optional custom)
## Update existing skill files
If files already exist, you can update selected design-system fields (free):
```bash
npx typeui.sh update
```
The CLI will:
- Read existing managed content
- Ask which fields to update
- Rewrite only the managed block in each `SKILL.md`
## Pull a published registry skill
Use this when you already know the published skill slug and want to fetch the authored markdown directly from [bergside/awesome-design-skills](https://github.com/bergside/awesome-design-skills):
```bash
npx typeui.sh pull paper
```
Behavior:
- The CLI asks for provider targets the same way as `generate/update` (unless `--providers` is passed).
- Universal target is always included.
- The CLI fetches `skills/index.json` from the registry repository, resolves the slug's `skillPath`, then fetches the corresponding `SKILL.md`.
- On success, the pulled markdown is written to selected provider `SKILL.md` paths.
You can combine `pull` with `--providers` and `--dry-run`:
```bash
npx typeui.sh pull paper --providers cursor,codex
npx typeui.sh pull paper --dry-run
```
## List available registry specs
Use this to choose one available design-system spec from the GitHub registry, then pull it immediately:
```bash
npx typeui.sh list
```
Behavior:
- The CLI fetches `skills/index.json` from `bergside/awesome-design-skills`.
- On success, it shows a checkbox selection where exactly one spec can be selected.
- After you confirm the selection, the CLI automatically runs pull for that slug.
You can pass pull options through list:
```bash
npx typeui.sh list --providers cursor,codex
npx typeui.sh list --dry-run
```
## Preview changes without writing files
Use `--dry-run` with generate, update, or pull:
```bash
npx typeui.sh generate --dry-run
npx typeui.sh update --dry-run
npx typeui.sh pull paper --dry-run
```
## Select providers from CLI options
You can bypass interactive provider selection:
```bash
npx typeui.sh generate --providers cursor,claude-code,mistral-vibe
```
Notes:
- Provider keys are comma-separated.
- Universal target (`.agents/skills`) is still included automatically.
## Where files are written
Every generated target writes:
- `.../skills/design-system/SKILL.md`
Examples:
- `.agents/skills/design-system/SKILL.md` (always)
- `.cursor/skills/design-system/SKILL.md`
- `.claude/skills/design-system/SKILL.md`
- `.codex/skills/design-system/SKILL.md`
- `.opencode/skills/design-system/SKILL.md`
## Troubleshooting
- **"License verification failed"**
- Check your key.
- Confirm you can reach `https://www.typeui.sh/api/license/verify`.
- **"No cached license"**
- Run `npx typeui.sh verify`.
- **"No existing managed design system found" on update**
- Run `npx typeui.sh generate` first.
- **Wrong files generated**
- Re-run with `--dry-run` to verify target paths before writing.
- **"Registry pull failed: not_found"**
- Verify the slug exists and has published markdown.