This document describes the internal API for the CraftDesk CLI, useful for contributors and developers extending the functionality.
Manages configuration and registry resolution.
import { configManager } from './services/config-manager';Returns the appropriate registry URL for a given package.
Parameters:
packageName- Package name (may be scoped like@company/package)
Returns: Registry URL
Example:
const registry = await configManager.getRegistryForPackage('@company/auth');
// Returns: 'https://company.internal' (if configured)Gets authentication token from environment variables.
Parameters:
registryName- Registry name from craftdesk.json
Returns: Auth token or null
Environment Variable Format: CRAFTDESK_AUTH_{REGISTRY_NAME_UPPERCASE}
Example:
// Looks for CRAFTDESK_AUTH_COMPANY_PRIVATE
const token = await configManager.getAuthToken('company-private');Returns the installation directory path.
Returns: .claude (relative path)
Reads and caches the craftdesk.json file.
Returns: Parsed craftdesk.json or null if not found
Resolves git dependencies and extracts metadata.
import { GitResolver } from './services/git-resolver';
const resolver = new GitResolver();Resolves a git dependency by cloning and reading metadata.
Parameters:
interface GitDependencyInfo {
url: string;
branch?: string;
tag?: string;
commit?: string;
path?: string;
craftDeskJson?: CraftDeskJson;
resolvedCommit?: string;
}Returns: Enhanced GitDependencyInfo with craftdesk.json and resolved commit
Example:
const info = await resolver.resolveGitDependency({
url: 'https://github.com/company/auth.git',
branch: 'main',
path: 'packages/auth'
});
console.log(info.craftDeskJson.name); // 'auth-skill'
console.log(info.resolvedCommit); // Full commit hashresolveAllDependencies(dependencies: Record<string, string | DependencyConfig>): Promise<{resolved, lockfile}>
Resolves all dependencies including transitive dependencies.
Parameters:
dependencies- Map of dependency names to version strings or config objects
Returns:
{
resolved: Record<string, any>; // All resolved dependencies
lockfile: { // Generated lockfile structure
version: string;
lockfileVersion: number;
crafts: Record<string, any>;
}
}Example:
const result = await resolver.resolveAllDependencies({
'ruby-on-rails': '^7.0.0',
'custom-auth': {
git: 'https://github.com/company/auth.git',
branch: 'main'
}
});Handles installation of crafts from various sources.
import { Installer } from './services/installer';
const installer = new Installer();Installs all crafts defined in a lockfile.
Parameters:
interface CraftDeskLock {
version: string;
lockfileVersion: number;
crafts: Record<string, LockEntry>;
}Throws: Error if any craft fails to install
Example:
const lockfile = await readLockfile();
await installer.installFromLockfile(lockfile);Installs a single craft.
Parameters:
name- Craft nameentry- Lockfile entry with installation details
Example:
await installer.installCraft('ruby-on-rails', {
version: '7.1.0',
resolved: 'https://craftdesk.ai/download/...',
type: 'skill',
dependencies: {}
});Communicates with CraftDesk registries.
import { registryClient } from './services/registry-client';Fetches craft information from registry.
Parameters:
name- Craft nameversion- Optional version constraintregistry- Optional registry URL override
Returns: Craft metadata or null if not found
Example:
const info = await registryClient.getCraftInfo('ruby-on-rails', '^7.0.0');
console.log(info.version); // '7.1.0'interface CraftDeskJson {
name: string;
version: string;
type: 'skill' | 'agent' | 'command' | 'hook';
description?: string;
author?: string;
dependencies?: Record<string, string | DependencyConfig>;
devDependencies?: Record<string, string | DependencyConfig>;
registries?: Record<string, RegistryConfig>;
}interface DependencyConfig {
version?: string; // For registry deps
registry?: string; // Custom registry URL
git?: string; // Git repository URL
branch?: string; // Git branch
tag?: string; // Git tag
commit?: string; // Git commit hash
path?: string; // Subdirectory path for monorepos
}interface RegistryConfig {
url: string;
scope?: string; // Scope for scoped packages (@company)
auth?: string; // DEPRECATED: Use environment variables
}interface LockEntry {
version: string;
resolved: string; // Download URL or git URL
integrity: string; // SHA-256 hash or commit hash
type: 'skill' | 'agent' | 'command' | 'hook';
author?: string;
dependencies: Record<string, string>;
// Git-specific fields
git?: string;
branch?: string;
tag?: string;
commit?: string;
path?: string;
// Registry-specific fields
registry?: string;
}import { readCraftDeskJson, writeCraftDeskJson, ensureDir } from './utils/file-system';Reads craftdesk.json from current directory.
Writes craftdesk.json to current directory.
Creates directory if it doesn't exist.
import { logger } from './utils/logger';info(message: string)- Info messagesuccess(message: string)- Success message (green)error(message: string)- Error message (red)warn(message: string)- Warning message (yellow)debug(message: string)- Debug message (if verbose)startSpinner(message: string)- Start loading spinnerupdateSpinner(message: string)- Update spinner messagesucceedSpinner(message?: string)- Stop spinner with successfailSpinner(message?: string)- Stop spinner with failure
- Update type definition in
src/types/craftdesk-json.ts:
export type CraftType = 'skill' | 'agent' | 'command' | 'hook' | 'your-new-type';- Update installer's
getTypeDirectory()method:
private getTypeDirectory(type: CraftType): string {
switch (type) {
case 'your-new-type':
return 'your-types';
// ...
}
}- Update git-resolver's
inferCraftType()method:
if (files.includes('YOUR_TYPE.md')) return 'your-new-type';- Create command file in
src/commands/:
import { Command } from 'commander';
export function createYourCommand(): Command {
return new Command('your-command')
.description('Your command description')
.action(async () => {
// Implementation
});
}- Register in
src/index.ts:
import { createYourCommand } from './commands/your-command';
program.addCommand(createYourCommand());- Update
DependencyConfiginterface - Modify
GitResolver.resolveAllDependencies()to handle new source type - Update
Installer.installCraft()to support new installation method
All service methods may throw errors. Wrap calls in try-catch:
try {
await installer.installCraft(name, entry);
} catch (error) {
logger.error(`Installation failed: ${error.message}`);
process.exit(1);
}See TESTING.md for testing guidelines and examples.
When adding new functionality:
- Add JSDoc comments to all public functions
- Include usage examples in documentation
- Add unit tests for new functions
- Update integration tests if CLI behavior changes
- Update type definitions
- Add inline comments for complex algorithms