用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/autohandai/community-skills --skill cli-tool-development命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | cli-tool-development |
| description | Build professional CLI tools with Node.js, commander, and Ink |
| license | MIT |
| compatibility | nodejs 18+, typescript 5+ |
| allowed-tools | read_file write_file apply_patch run_command search_with_context |
src/
index.ts # Entry point with shebang
cli.ts # Commander setup
commands/ # Command handlers
ui/ # Ink components (if interactive)
utils/ # Helpers
types.ts # Type definitions
#!/usr/bin/env node
import { Command } from 'commander';
import packageJson from '../package.json' with { type: 'json' };
const program = ();
program
.()
.()
.(packageJson.);
program
.()
.()
.(, , )
.(, , )
.( (options) => {
(options);
});
program.();
import chalk from 'chalk';
import ora from 'ora';
// Status messages
console.log(chalk.green('✓') + ' Operation complete');
console.log(chalk.red('✗') + ' Operation failed');
console.log(chalk.yellow('⚠') + ' Warning message');
// Progress spinner
const spinner = ora('Loading...').start();
try {
await longOperation();
spinner.succeed('Done!');
} catch (error) {
spinner.fail('Failed');
}
import enquirer from 'enquirer';
const { name } = await enquirer.prompt<{ name: string }>({
type: 'input',
name: 'name',
message: 'Project name:',
validate: (v) => v.length > 0 || 'Name required',
});
const { confirm } = await enquirer.prompt<{ confirm: boolean }>({
type: 'confirm',
name: 'confirm',
message: 'Continue?',
initial: true,
});
import React, { useState } from 'react';
import { render, Box, Text, useInput } from 'ink';
function App() {
const [selected, setSelected] = useState(0);
const items = ['Option 1', 'Option 2', 'Option 3'];
useInput((input, key) => {
if (key.downArrow) setSelected(s => Math.min(s + 1, items.length - 1));
if (key.upArrow) setSelected(s => Math.max(s - 1, 0));
if (key.return) process.exit(0);
});
return (
<Box flexDirection="column">
{items.map((item, i) => (
<Text key={i} color={i === selected ? 'cyan' : undefined}>
{i === selected ? '>' : ' '} {item}
</Text>
))}
</Box>
);
}
render(<App />);
import fs from 'fs-extra';
import path from 'node:path';
import os from 'node:os';
const CONFIG_DIR = path.join(os.homedir(), '.mytool');
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
async function loadConfig(): Promise<Config> {
await fs.ensureDir(CONFIG_DIR);
if (await fs.pathExists(CONFIG_FILE)) {
return fs.readJson(CONFIG_FILE);
}
return getDefaultConfig();
}
async function saveConfig(config: Config): Promise<void> {
await fs.writeJson(CONFIG_FILE, config, { spaces: 2 });
}
// Graceful exit handling
process.on('SIGINT', () => {
console.log('\nCancelled');
process.exit(0);
});
// Top-level error handler
try {
await program.parseAsync();
} catch (error) {
console.error(chalk.red('Error:'), error.message);
process.exit(1);
}
{
"bin": {
"mytool": "./dist/index.js"
},
"files": ["dist"],
"type": "module",
"scripts": {
"build": "tsup src/index.ts --format esm --dts",
"dev": "tsx src/index.ts"
}
}
#!/usr/bin/env node shebang to entry file-f) and options (--force)--help and --version flagsNO_COLOR environment variable