feat: add @tpmjs/create-basic-tools CLI generator
- Interactive CLI generator for scaffolding TPMJS tool packages - Generates packages with minimum 2 tools (ideally 2-3) - Zod 4 schemas - uses Zod directly (not jsonSchema wrapper) - One file per tool in src/tools/<toolName>.ts - TPMJS validated against official schemas from @tpmjs/types - Complete package generation ready to publish to npm - Works both standalone and in monorepo packages/ folders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8b000cf0ca
commit
38c85bd904
28 changed files with 1426 additions and 2 deletions
|
|
@ -0,0 +1,89 @@
|
|||
import validatePackageName from 'validate-npm-package-name';
|
||||
|
||||
export interface PackageNameValidation {
|
||||
valid: boolean;
|
||||
errors?: string[];
|
||||
warnings?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an npm package name
|
||||
*/
|
||||
export function validateNpmPackageName(name: string): PackageNameValidation {
|
||||
const result = validatePackageName(name);
|
||||
|
||||
if (result.validForNewPackages) {
|
||||
return {
|
||||
valid: true,
|
||||
warnings: result.warnings,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
errors: result.errors,
|
||||
warnings: result.warnings,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that export name is a valid JavaScript identifier
|
||||
*/
|
||||
export function validateExportName(name: string): { valid: boolean; error?: string } {
|
||||
if (!name || name.length === 0) {
|
||||
return { valid: false, error: 'Export name cannot be empty' };
|
||||
}
|
||||
|
||||
// JavaScript identifier regex: must start with letter, $, or _, followed by letters, digits, $, or _
|
||||
const identifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
||||
|
||||
if (!identifierRegex.test(name)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Export name must be a valid JavaScript identifier (e.g., myTool, summarizeText)',
|
||||
};
|
||||
}
|
||||
|
||||
// Check for reserved words
|
||||
const reservedWords = [
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'export',
|
||||
'extends',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'new',
|
||||
'return',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'yield',
|
||||
];
|
||||
|
||||
if (reservedWords.includes(name)) {
|
||||
return { valid: false, error: `"${name}" is a reserved JavaScript keyword` };
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import { TPMJS_CATEGORIES, TpmjsToolDefinitionSchema } from '@tpmjs/types/tpmjs';
|
||||
import type { TpmjsCategory } from '@tpmjs/types/tpmjs';
|
||||
import type { ToolDefinition } from '../types.js';
|
||||
|
||||
/**
|
||||
* Validates a tool definition against TPMJS schema
|
||||
*/
|
||||
export function validateToolDefinition(tool: ToolDefinition): {
|
||||
valid: boolean;
|
||||
errors?: string[];
|
||||
} {
|
||||
const result = TpmjsToolDefinitionSchema.safeParse(tool);
|
||||
|
||||
if (result.success) {
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
const errors = result.error.issues.map((issue) => {
|
||||
const path = issue.path.join('.');
|
||||
return `${path}: ${issue.message}`;
|
||||
});
|
||||
|
||||
return { valid: false, errors };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that category is one of the allowed TPMJS categories
|
||||
*/
|
||||
export function validateCategory(category: string): {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
} {
|
||||
if (!TPMJS_CATEGORIES.includes(category as TpmjsCategory)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Category must be one of: ${TPMJS_CATEGORIES.join(', ')}`,
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates tool description length
|
||||
*/
|
||||
export function validateDescription(description: string): {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
} {
|
||||
if (description.length < 20) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Description must be at least 20 characters',
|
||||
};
|
||||
}
|
||||
|
||||
if (description.length > 500) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Description must be at most 500 characters',
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available TPMJS categories
|
||||
*/
|
||||
export function getAvailableCategories(): readonly TpmjsCategory[] {
|
||||
return TPMJS_CATEGORIES;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue