@ai-content-space/loopx 0.2.9 → 0.2.10
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/README.md +10 -6
- package/README.zh-CN.md +10 -6
- package/docs/loopx/design/loopx-skill-suite-v1-design.md +12 -0
- package/docs/loopx/plans/2026-06-15-support-lens-skills-migration.md +1153 -0
- package/package.json +6 -1
- package/plugins/loopx/.codex-plugin/plugin.json +1 -1
- package/plugins/loopx/skills/api-designer/SKILL.md +232 -0
- package/plugins/loopx/skills/api-designer/references/error-handling.md +541 -0
- package/plugins/loopx/skills/api-designer/references/openapi.md +824 -0
- package/plugins/loopx/skills/api-designer/references/pagination.md +494 -0
- package/plugins/loopx/skills/api-designer/references/rest-patterns.md +335 -0
- package/plugins/loopx/skills/api-designer/references/versioning.md +391 -0
- package/plugins/loopx/skills/architecture-designer/SKILL.md +117 -0
- package/plugins/loopx/skills/architecture-designer/references/adr-template.md +116 -0
- package/plugins/loopx/skills/architecture-designer/references/architecture-patterns.md +346 -0
- package/plugins/loopx/skills/architecture-designer/references/database-selection.md +102 -0
- package/plugins/loopx/skills/architecture-designer/references/nfr-checklist.md +212 -0
- package/plugins/loopx/skills/architecture-designer/references/system-design.md +313 -0
- package/plugins/loopx/skills/clarify/SKILL.md +1 -1
- package/plugins/loopx/skills/cli-developer/SKILL.md +124 -0
- package/plugins/loopx/skills/cli-developer/references/design-patterns.md +221 -0
- package/plugins/loopx/skills/cli-developer/references/go-cli.md +540 -0
- package/plugins/loopx/skills/cli-developer/references/node-cli.md +383 -0
- package/plugins/loopx/skills/cli-developer/references/python-cli.md +422 -0
- package/plugins/loopx/skills/cli-developer/references/ux-patterns.md +448 -0
- package/plugins/loopx/skills/debug/SKILL.md +1 -1
- package/plugins/loopx/skills/doc-readability/SKILL.md +1 -1
- package/plugins/loopx/skills/exec/SKILL.md +1 -1
- package/plugins/loopx/skills/final-review/SKILL.md +1 -1
- package/plugins/loopx/skills/finish/SKILL.md +1 -1
- package/plugins/loopx/skills/fix-review/SKILL.md +1 -1
- package/plugins/loopx/skills/go-style/SKILL.md +1 -1
- package/plugins/loopx/skills/kratos/SKILL.md +2 -1
- package/plugins/loopx/skills/plan-to-exec/SKILL.md +1 -1
- package/plugins/loopx/skills/refactor-plan/SKILL.md +1 -1
- package/plugins/loopx/skills/requirement-analyzer/SKILL.md +161 -0
- package/plugins/loopx/skills/requirement-analyzer/references/example-reports.md +170 -0
- package/plugins/loopx/skills/requirement-analyzer/references/prd-gap-checklist.md +167 -0
- package/plugins/loopx/skills/requirement-analyzer/references/readiness-rubric.md +70 -0
- package/plugins/loopx/skills/requirement-analyzer/references/report-template.md +83 -0
- package/plugins/loopx/skills/review/SKILL.md +1 -1
- package/plugins/loopx/skills/spec/SKILL.md +1 -1
- package/plugins/loopx/skills/sql-style/SKILL.md +108 -0
- package/plugins/loopx/skills/sql-style/references/database-design.md +402 -0
- package/plugins/loopx/skills/sql-style/references/dialect-differences.md +419 -0
- package/plugins/loopx/skills/sql-style/references/optimization.md +384 -0
- package/plugins/loopx/skills/sql-style/references/query-patterns.md +285 -0
- package/plugins/loopx/skills/sql-style/references/window-functions.md +328 -0
- package/plugins/loopx/skills/subagent-exec/SKILL.md +1 -1
- package/plugins/loopx/skills/tdd/SKILL.md +1 -1
- package/plugins/loopx/skills/verify/SKILL.md +1 -1
- package/scripts/verify-skills.mjs +0 -2
- package/skills/RESOLVER.md +8 -1
- package/skills/api-designer/SKILL.md +232 -0
- package/skills/api-designer/references/error-handling.md +541 -0
- package/skills/api-designer/references/openapi.md +824 -0
- package/skills/api-designer/references/pagination.md +494 -0
- package/skills/api-designer/references/rest-patterns.md +335 -0
- package/skills/api-designer/references/versioning.md +391 -0
- package/skills/architecture-designer/SKILL.md +117 -0
- package/skills/architecture-designer/references/adr-template.md +116 -0
- package/skills/architecture-designer/references/architecture-patterns.md +346 -0
- package/skills/architecture-designer/references/database-selection.md +102 -0
- package/skills/architecture-designer/references/nfr-checklist.md +212 -0
- package/skills/architecture-designer/references/system-design.md +313 -0
- package/skills/clarify/SKILL.md +1 -1
- package/skills/cli-developer/SKILL.md +124 -0
- package/skills/cli-developer/references/design-patterns.md +221 -0
- package/skills/cli-developer/references/go-cli.md +540 -0
- package/skills/cli-developer/references/node-cli.md +383 -0
- package/skills/cli-developer/references/python-cli.md +422 -0
- package/skills/cli-developer/references/ux-patterns.md +448 -0
- package/skills/debug/SKILL.md +1 -1
- package/skills/doc-readability/SKILL.md +1 -1
- package/skills/exec/SKILL.md +1 -1
- package/skills/final-review/SKILL.md +1 -1
- package/skills/finish/SKILL.md +1 -1
- package/skills/fix-review/SKILL.md +1 -1
- package/skills/go-style/SKILL.md +1 -1
- package/skills/kratos/SKILL.md +2 -1
- package/skills/plan-to-exec/SKILL.md +1 -1
- package/skills/refactor-plan/SKILL.md +1 -1
- package/skills/requirement-analyzer/SKILL.md +161 -0
- package/skills/requirement-analyzer/references/example-reports.md +170 -0
- package/skills/requirement-analyzer/references/prd-gap-checklist.md +167 -0
- package/skills/requirement-analyzer/references/readiness-rubric.md +70 -0
- package/skills/requirement-analyzer/references/report-template.md +83 -0
- package/skills/review/SKILL.md +1 -1
- package/skills/spec/SKILL.md +1 -1
- package/skills/sql-style/SKILL.md +108 -0
- package/skills/sql-style/references/database-design.md +402 -0
- package/skills/sql-style/references/dialect-differences.md +419 -0
- package/skills/sql-style/references/optimization.md +384 -0
- package/skills/sql-style/references/query-patterns.md +285 -0
- package/skills/sql-style/references/window-functions.md +328 -0
- package/skills/subagent-exec/SKILL.md +1 -1
- package/skills/tdd/SKILL.md +1 -1
- package/skills/verify/SKILL.md +1 -1
- package/src/install-discovery.mjs +5 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# Node.js CLI Development
|
|
2
|
+
|
|
3
|
+
## Commander.js (Recommended)
|
|
4
|
+
|
|
5
|
+
Modern, elegant CLI framework with TypeScript support.
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
#!/usr/bin/env node
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import { version } from './package.json';
|
|
11
|
+
|
|
12
|
+
const program = new Command();
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('mycli')
|
|
16
|
+
.description('My awesome CLI tool')
|
|
17
|
+
.version(version);
|
|
18
|
+
|
|
19
|
+
// Simple command
|
|
20
|
+
program
|
|
21
|
+
.command('init')
|
|
22
|
+
.description('Initialize a new project')
|
|
23
|
+
.option('-t, --template <type>', 'Project template', 'default')
|
|
24
|
+
.option('-f, --force', 'Overwrite existing files')
|
|
25
|
+
.action(async (options) => {
|
|
26
|
+
console.log(`Initializing with template: ${options.template}`);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Command with arguments
|
|
30
|
+
program
|
|
31
|
+
.command('deploy <environment>')
|
|
32
|
+
.description('Deploy to environment')
|
|
33
|
+
.option('--dry-run', 'Preview without executing')
|
|
34
|
+
.action(async (environment, options) => {
|
|
35
|
+
if (options.dryRun) {
|
|
36
|
+
console.log(`Would deploy to: ${environment}`);
|
|
37
|
+
} else {
|
|
38
|
+
await deploy(environment);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Nested subcommands
|
|
43
|
+
const config = program.command('config').description('Manage configuration');
|
|
44
|
+
|
|
45
|
+
config
|
|
46
|
+
.command('get <key>')
|
|
47
|
+
.description('Get config value')
|
|
48
|
+
.action((key) => console.log(getConfig(key)));
|
|
49
|
+
|
|
50
|
+
config
|
|
51
|
+
.command('set <key> <value>')
|
|
52
|
+
.description('Set config value')
|
|
53
|
+
.action((key, value) => setConfig(key, value));
|
|
54
|
+
|
|
55
|
+
program.parse();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Yargs (Alternative)
|
|
59
|
+
|
|
60
|
+
Powerful argument parsing with middleware support.
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
#!/usr/bin/env node
|
|
64
|
+
import yargs from 'yargs';
|
|
65
|
+
import { hideBin } from 'yargs/helpers';
|
|
66
|
+
|
|
67
|
+
yargs(hideBin(process.argv))
|
|
68
|
+
.command(
|
|
69
|
+
'deploy <env>',
|
|
70
|
+
'Deploy to environment',
|
|
71
|
+
(yargs) => {
|
|
72
|
+
return yargs
|
|
73
|
+
.positional('env', {
|
|
74
|
+
describe: 'Environment name',
|
|
75
|
+
choices: ['dev', 'staging', 'prod'],
|
|
76
|
+
})
|
|
77
|
+
.option('force', {
|
|
78
|
+
alias: 'f',
|
|
79
|
+
type: 'boolean',
|
|
80
|
+
description: 'Force deployment',
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
async (argv) => {
|
|
84
|
+
await deploy(argv.env, { force: argv.force });
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
.middleware([(argv) => {
|
|
88
|
+
// Validate before all commands
|
|
89
|
+
if (!isConfigValid()) {
|
|
90
|
+
throw new Error('Invalid config');
|
|
91
|
+
}
|
|
92
|
+
}])
|
|
93
|
+
.demandCommand()
|
|
94
|
+
.help()
|
|
95
|
+
.parse();
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Interactive Prompts (Inquirer)
|
|
99
|
+
|
|
100
|
+
Beautiful interactive prompts for user input.
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
import inquirer from 'inquirer';
|
|
104
|
+
|
|
105
|
+
// Text input
|
|
106
|
+
const { name } = await inquirer.prompt([
|
|
107
|
+
{
|
|
108
|
+
type: 'input',
|
|
109
|
+
name: 'name',
|
|
110
|
+
message: 'Project name:',
|
|
111
|
+
default: 'my-project',
|
|
112
|
+
validate: (input) => input.length > 0 || 'Name required',
|
|
113
|
+
},
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
// Select from list
|
|
117
|
+
const { environment } = await inquirer.prompt([
|
|
118
|
+
{
|
|
119
|
+
type: 'list',
|
|
120
|
+
name: 'environment',
|
|
121
|
+
message: 'Select environment:',
|
|
122
|
+
choices: ['development', 'staging', 'production'],
|
|
123
|
+
default: 'development',
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
// Checkbox (multi-select)
|
|
128
|
+
const { features } = await inquirer.prompt([
|
|
129
|
+
{
|
|
130
|
+
type: 'checkbox',
|
|
131
|
+
name: 'features',
|
|
132
|
+
message: 'Select features:',
|
|
133
|
+
choices: [
|
|
134
|
+
{ name: 'TypeScript', checked: true },
|
|
135
|
+
{ name: 'ESLint', checked: true },
|
|
136
|
+
{ name: 'Prettier', checked: true },
|
|
137
|
+
{ name: 'Jest', checked: false },
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
// Confirmation
|
|
143
|
+
const { confirmed } = await inquirer.prompt([
|
|
144
|
+
{
|
|
145
|
+
type: 'confirm',
|
|
146
|
+
name: 'confirmed',
|
|
147
|
+
message: 'Deploy to production?',
|
|
148
|
+
default: false,
|
|
149
|
+
},
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
// Password
|
|
153
|
+
const { password } = await inquirer.prompt([
|
|
154
|
+
{
|
|
155
|
+
type: 'password',
|
|
156
|
+
name: 'password',
|
|
157
|
+
message: 'Enter password:',
|
|
158
|
+
mask: '*',
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Terminal Output (Chalk)
|
|
164
|
+
|
|
165
|
+
Colorful terminal output with proper TTY detection.
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
import chalk from 'chalk';
|
|
169
|
+
|
|
170
|
+
// Basic colors
|
|
171
|
+
console.log(chalk.blue('Info: ') + 'Starting deployment...');
|
|
172
|
+
console.log(chalk.green('Success: ') + 'Deployment complete');
|
|
173
|
+
console.log(chalk.yellow('Warning: ') + 'Deprecated flag used');
|
|
174
|
+
console.log(chalk.red('Error: ') + 'Deployment failed');
|
|
175
|
+
|
|
176
|
+
// Styles
|
|
177
|
+
console.log(chalk.bold.underline('Important'));
|
|
178
|
+
console.log(chalk.dim('Less important'));
|
|
179
|
+
|
|
180
|
+
// Templates
|
|
181
|
+
const success = chalk.green.bold;
|
|
182
|
+
const error = chalk.red.bold;
|
|
183
|
+
console.log(success('✓') + ' Build successful');
|
|
184
|
+
console.log(error('✗') + ' Build failed');
|
|
185
|
+
|
|
186
|
+
// Disable colors for CI
|
|
187
|
+
const log = {
|
|
188
|
+
info: (msg) => console.log(chalk.blue('ℹ'), msg),
|
|
189
|
+
success: (msg) => console.log(chalk.green('✔'), msg),
|
|
190
|
+
warn: (msg) => console.log(chalk.yellow('⚠'), msg),
|
|
191
|
+
error: (msg) => console.log(chalk.red('✖'), msg),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Auto-detects TTY and CI environments
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Progress Indicators (Ora)
|
|
198
|
+
|
|
199
|
+
Elegant terminal spinners and progress indicators.
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
import ora from 'ora';
|
|
203
|
+
|
|
204
|
+
// Simple spinner
|
|
205
|
+
const spinner = ora('Loading...').start();
|
|
206
|
+
await doWork();
|
|
207
|
+
spinner.succeed('Done!');
|
|
208
|
+
|
|
209
|
+
// Update text
|
|
210
|
+
const spinner = ora('Starting...').start();
|
|
211
|
+
spinner.text = 'Processing...';
|
|
212
|
+
await process();
|
|
213
|
+
spinner.text = 'Finalizing...';
|
|
214
|
+
await finalize();
|
|
215
|
+
spinner.succeed('Complete!');
|
|
216
|
+
|
|
217
|
+
// Different states
|
|
218
|
+
spinner.start('Installing dependencies...');
|
|
219
|
+
// ... work
|
|
220
|
+
spinner.succeed('Dependencies installed');
|
|
221
|
+
// or
|
|
222
|
+
spinner.fail('Installation failed');
|
|
223
|
+
// or
|
|
224
|
+
spinner.warn('Some packages skipped');
|
|
225
|
+
// or
|
|
226
|
+
spinner.info('Using cached packages');
|
|
227
|
+
|
|
228
|
+
// Multiple spinners
|
|
229
|
+
const spinners = {
|
|
230
|
+
api: ora('Deploying API...').start(),
|
|
231
|
+
web: ora('Deploying web app...').start(),
|
|
232
|
+
db: ora('Running migrations...').start(),
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
await Promise.all([
|
|
236
|
+
deployApi().then(() => spinners.api.succeed()),
|
|
237
|
+
deployWeb().then(() => spinners.web.succeed()),
|
|
238
|
+
runMigrations().then(() => spinners.db.succeed()),
|
|
239
|
+
]);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Progress Bars (cli-progress)
|
|
243
|
+
|
|
244
|
+
```javascript
|
|
245
|
+
import cliProgress from 'cli-progress';
|
|
246
|
+
|
|
247
|
+
// Single progress bar
|
|
248
|
+
const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
|
|
249
|
+
bar.start(100, 0);
|
|
250
|
+
|
|
251
|
+
for (let i = 0; i <= 100; i++) {
|
|
252
|
+
await processItem(i);
|
|
253
|
+
bar.update(i);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
bar.stop();
|
|
257
|
+
|
|
258
|
+
// Multi-progress
|
|
259
|
+
const multibar = new cliProgress.MultiBar({
|
|
260
|
+
clearOnComplete: false,
|
|
261
|
+
hideCursor: true,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const bar1 = multibar.create(100, 0, { task: 'API' });
|
|
265
|
+
const bar2 = multibar.create(100, 0, { task: 'Web' });
|
|
266
|
+
|
|
267
|
+
await Promise.all([
|
|
268
|
+
processApi(bar1),
|
|
269
|
+
processWeb(bar2),
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
multibar.stop();
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## File System Helpers
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
import fs from 'fs-extra';
|
|
279
|
+
import { globby } from 'globby';
|
|
280
|
+
import path from 'path';
|
|
281
|
+
|
|
282
|
+
// Copy with template
|
|
283
|
+
await fs.copy('templates/app', targetDir, {
|
|
284
|
+
filter: (src) => !src.includes('node_modules'),
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Read/write JSON
|
|
288
|
+
const config = await fs.readJson('config.json');
|
|
289
|
+
await fs.writeJson('output.json', data, { spaces: 2 });
|
|
290
|
+
|
|
291
|
+
// Ensure directory exists
|
|
292
|
+
await fs.ensureDir('dist/assets');
|
|
293
|
+
|
|
294
|
+
// Find files
|
|
295
|
+
const files = await globby(['src/**/*.ts', '!src/**/*.test.ts']);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Error Handling
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
import { Command } from 'commander';
|
|
302
|
+
|
|
303
|
+
program
|
|
304
|
+
.command('deploy')
|
|
305
|
+
.action(async () => {
|
|
306
|
+
try {
|
|
307
|
+
await deploy();
|
|
308
|
+
} catch (error) {
|
|
309
|
+
if (error.code === 'EACCES') {
|
|
310
|
+
console.error(chalk.red('Permission denied'));
|
|
311
|
+
console.error('Try running with sudo or check file permissions');
|
|
312
|
+
process.exit(77);
|
|
313
|
+
} else if (error.code === 'ENOENT') {
|
|
314
|
+
console.error(chalk.red('File not found:'), error.path);
|
|
315
|
+
process.exit(127);
|
|
316
|
+
} else {
|
|
317
|
+
console.error(chalk.red('Deployment failed:'), error.message);
|
|
318
|
+
if (process.env.DEBUG) {
|
|
319
|
+
console.error(error.stack);
|
|
320
|
+
}
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Handle SIGINT (Ctrl+C)
|
|
327
|
+
process.on('SIGINT', () => {
|
|
328
|
+
console.log('\nOperation cancelled');
|
|
329
|
+
process.exit(130);
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Package.json Setup
|
|
334
|
+
|
|
335
|
+
```json
|
|
336
|
+
{
|
|
337
|
+
"name": "mycli",
|
|
338
|
+
"version": "1.0.0",
|
|
339
|
+
"type": "module",
|
|
340
|
+
"bin": {
|
|
341
|
+
"mycli": "./bin/cli.js"
|
|
342
|
+
},
|
|
343
|
+
"files": [
|
|
344
|
+
"bin/",
|
|
345
|
+
"lib/",
|
|
346
|
+
"templates/"
|
|
347
|
+
],
|
|
348
|
+
"engines": {
|
|
349
|
+
"node": ">=18.0.0"
|
|
350
|
+
},
|
|
351
|
+
"dependencies": {
|
|
352
|
+
"commander": "^11.0.0",
|
|
353
|
+
"inquirer": "^9.0.0",
|
|
354
|
+
"chalk": "^5.0.0",
|
|
355
|
+
"ora": "^7.0.0"
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Testing CLIs
|
|
361
|
+
|
|
362
|
+
```javascript
|
|
363
|
+
import { execaCommand } from 'execa';
|
|
364
|
+
import { describe, it, expect } from 'vitest';
|
|
365
|
+
|
|
366
|
+
describe('mycli', () => {
|
|
367
|
+
it('shows version', async () => {
|
|
368
|
+
const { stdout } = await execaCommand('node bin/cli.js --version');
|
|
369
|
+
expect(stdout).toMatch(/\d+\.\d+\.\d+/);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('shows help', async () => {
|
|
373
|
+
const { stdout } = await execaCommand('node bin/cli.js --help');
|
|
374
|
+
expect(stdout).toContain('Usage:');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('handles invalid command', async () => {
|
|
378
|
+
await expect(
|
|
379
|
+
execaCommand('node bin/cli.js invalid')
|
|
380
|
+
).rejects.toThrow();
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
```
|