@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.
Files changed (99) hide show
  1. package/README.md +10 -6
  2. package/README.zh-CN.md +10 -6
  3. package/docs/loopx/design/loopx-skill-suite-v1-design.md +12 -0
  4. package/docs/loopx/plans/2026-06-15-support-lens-skills-migration.md +1153 -0
  5. package/package.json +6 -1
  6. package/plugins/loopx/.codex-plugin/plugin.json +1 -1
  7. package/plugins/loopx/skills/api-designer/SKILL.md +232 -0
  8. package/plugins/loopx/skills/api-designer/references/error-handling.md +541 -0
  9. package/plugins/loopx/skills/api-designer/references/openapi.md +824 -0
  10. package/plugins/loopx/skills/api-designer/references/pagination.md +494 -0
  11. package/plugins/loopx/skills/api-designer/references/rest-patterns.md +335 -0
  12. package/plugins/loopx/skills/api-designer/references/versioning.md +391 -0
  13. package/plugins/loopx/skills/architecture-designer/SKILL.md +117 -0
  14. package/plugins/loopx/skills/architecture-designer/references/adr-template.md +116 -0
  15. package/plugins/loopx/skills/architecture-designer/references/architecture-patterns.md +346 -0
  16. package/plugins/loopx/skills/architecture-designer/references/database-selection.md +102 -0
  17. package/plugins/loopx/skills/architecture-designer/references/nfr-checklist.md +212 -0
  18. package/plugins/loopx/skills/architecture-designer/references/system-design.md +313 -0
  19. package/plugins/loopx/skills/clarify/SKILL.md +1 -1
  20. package/plugins/loopx/skills/cli-developer/SKILL.md +124 -0
  21. package/plugins/loopx/skills/cli-developer/references/design-patterns.md +221 -0
  22. package/plugins/loopx/skills/cli-developer/references/go-cli.md +540 -0
  23. package/plugins/loopx/skills/cli-developer/references/node-cli.md +383 -0
  24. package/plugins/loopx/skills/cli-developer/references/python-cli.md +422 -0
  25. package/plugins/loopx/skills/cli-developer/references/ux-patterns.md +448 -0
  26. package/plugins/loopx/skills/debug/SKILL.md +1 -1
  27. package/plugins/loopx/skills/doc-readability/SKILL.md +1 -1
  28. package/plugins/loopx/skills/exec/SKILL.md +1 -1
  29. package/plugins/loopx/skills/final-review/SKILL.md +1 -1
  30. package/plugins/loopx/skills/finish/SKILL.md +1 -1
  31. package/plugins/loopx/skills/fix-review/SKILL.md +1 -1
  32. package/plugins/loopx/skills/go-style/SKILL.md +1 -1
  33. package/plugins/loopx/skills/kratos/SKILL.md +2 -1
  34. package/plugins/loopx/skills/plan-to-exec/SKILL.md +1 -1
  35. package/plugins/loopx/skills/refactor-plan/SKILL.md +1 -1
  36. package/plugins/loopx/skills/requirement-analyzer/SKILL.md +161 -0
  37. package/plugins/loopx/skills/requirement-analyzer/references/example-reports.md +170 -0
  38. package/plugins/loopx/skills/requirement-analyzer/references/prd-gap-checklist.md +167 -0
  39. package/plugins/loopx/skills/requirement-analyzer/references/readiness-rubric.md +70 -0
  40. package/plugins/loopx/skills/requirement-analyzer/references/report-template.md +83 -0
  41. package/plugins/loopx/skills/review/SKILL.md +1 -1
  42. package/plugins/loopx/skills/spec/SKILL.md +1 -1
  43. package/plugins/loopx/skills/sql-style/SKILL.md +108 -0
  44. package/plugins/loopx/skills/sql-style/references/database-design.md +402 -0
  45. package/plugins/loopx/skills/sql-style/references/dialect-differences.md +419 -0
  46. package/plugins/loopx/skills/sql-style/references/optimization.md +384 -0
  47. package/plugins/loopx/skills/sql-style/references/query-patterns.md +285 -0
  48. package/plugins/loopx/skills/sql-style/references/window-functions.md +328 -0
  49. package/plugins/loopx/skills/subagent-exec/SKILL.md +1 -1
  50. package/plugins/loopx/skills/tdd/SKILL.md +1 -1
  51. package/plugins/loopx/skills/verify/SKILL.md +1 -1
  52. package/scripts/verify-skills.mjs +0 -2
  53. package/skills/RESOLVER.md +8 -1
  54. package/skills/api-designer/SKILL.md +232 -0
  55. package/skills/api-designer/references/error-handling.md +541 -0
  56. package/skills/api-designer/references/openapi.md +824 -0
  57. package/skills/api-designer/references/pagination.md +494 -0
  58. package/skills/api-designer/references/rest-patterns.md +335 -0
  59. package/skills/api-designer/references/versioning.md +391 -0
  60. package/skills/architecture-designer/SKILL.md +117 -0
  61. package/skills/architecture-designer/references/adr-template.md +116 -0
  62. package/skills/architecture-designer/references/architecture-patterns.md +346 -0
  63. package/skills/architecture-designer/references/database-selection.md +102 -0
  64. package/skills/architecture-designer/references/nfr-checklist.md +212 -0
  65. package/skills/architecture-designer/references/system-design.md +313 -0
  66. package/skills/clarify/SKILL.md +1 -1
  67. package/skills/cli-developer/SKILL.md +124 -0
  68. package/skills/cli-developer/references/design-patterns.md +221 -0
  69. package/skills/cli-developer/references/go-cli.md +540 -0
  70. package/skills/cli-developer/references/node-cli.md +383 -0
  71. package/skills/cli-developer/references/python-cli.md +422 -0
  72. package/skills/cli-developer/references/ux-patterns.md +448 -0
  73. package/skills/debug/SKILL.md +1 -1
  74. package/skills/doc-readability/SKILL.md +1 -1
  75. package/skills/exec/SKILL.md +1 -1
  76. package/skills/final-review/SKILL.md +1 -1
  77. package/skills/finish/SKILL.md +1 -1
  78. package/skills/fix-review/SKILL.md +1 -1
  79. package/skills/go-style/SKILL.md +1 -1
  80. package/skills/kratos/SKILL.md +2 -1
  81. package/skills/plan-to-exec/SKILL.md +1 -1
  82. package/skills/refactor-plan/SKILL.md +1 -1
  83. package/skills/requirement-analyzer/SKILL.md +161 -0
  84. package/skills/requirement-analyzer/references/example-reports.md +170 -0
  85. package/skills/requirement-analyzer/references/prd-gap-checklist.md +167 -0
  86. package/skills/requirement-analyzer/references/readiness-rubric.md +70 -0
  87. package/skills/requirement-analyzer/references/report-template.md +83 -0
  88. package/skills/review/SKILL.md +1 -1
  89. package/skills/spec/SKILL.md +1 -1
  90. package/skills/sql-style/SKILL.md +108 -0
  91. package/skills/sql-style/references/database-design.md +402 -0
  92. package/skills/sql-style/references/dialect-differences.md +419 -0
  93. package/skills/sql-style/references/optimization.md +384 -0
  94. package/skills/sql-style/references/query-patterns.md +285 -0
  95. package/skills/sql-style/references/window-functions.md +328 -0
  96. package/skills/subagent-exec/SKILL.md +1 -1
  97. package/skills/tdd/SKILL.md +1 -1
  98. package/skills/verify/SKILL.md +1 -1
  99. package/src/install-discovery.mjs +5 -0
@@ -0,0 +1,422 @@
1
+ # Python CLI Development
2
+
3
+ ## Typer (Recommended - Modern)
4
+
5
+ FastAPI-style CLI framework with automatic help generation.
6
+
7
+ ```python
8
+ #!/usr/bin/env python3
9
+ import typer
10
+ from typing import Optional
11
+ from enum import Enum
12
+
13
+ app = typer.Typer()
14
+
15
+ class Environment(str, Enum):
16
+ dev = "development"
17
+ staging = "staging"
18
+ prod = "production"
19
+
20
+ @app.command()
21
+ def init(
22
+ name: str = typer.Argument(..., help="Project name"),
23
+ template: str = typer.Option("default", help="Project template"),
24
+ force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing"),
25
+ ):
26
+ """Initialize a new project"""
27
+ typer.echo(f"Creating {name} from {template}")
28
+ if force:
29
+ typer.echo("Force mode enabled")
30
+
31
+ @app.command()
32
+ def deploy(
33
+ environment: Environment = typer.Argument(..., help="Target environment"),
34
+ dry_run: bool = typer.Option(False, "--dry-run", help="Preview only"),
35
+ config: Optional[typer.FileText] = typer.Option(None, help="Config file"),
36
+ ):
37
+ """Deploy to environment"""
38
+ if dry_run:
39
+ typer.echo(f"Would deploy to: {environment.value}")
40
+ else:
41
+ typer.echo(f"Deploying to {environment.value}...")
42
+
43
+ # Nested commands
44
+ config_app = typer.Typer()
45
+ app.add_typer(config_app, name="config", help="Manage configuration")
46
+
47
+ @config_app.command("get")
48
+ def config_get(key: str):
49
+ """Get config value"""
50
+ typer.echo(f"Value: {get_config(key)}")
51
+
52
+ @config_app.command("set")
53
+ def config_set(key: str, value: str):
54
+ """Set config value"""
55
+ set_config(key, value)
56
+ typer.echo(f"Set {key} = {value}")
57
+
58
+ if __name__ == "__main__":
59
+ app()
60
+ ```
61
+
62
+ ## Click (Widely Used)
63
+
64
+ Powerful, composable CLI framework.
65
+
66
+ ```python
67
+ import click
68
+
69
+ @click.group()
70
+ @click.version_option()
71
+ def cli():
72
+ """My awesome CLI tool"""
73
+ pass
74
+
75
+ @cli.command()
76
+ @click.argument('name')
77
+ @click.option('--template', default='default', help='Project template')
78
+ @click.option('--force', '-f', is_flag=True, help='Overwrite existing')
79
+ def init(name, template, force):
80
+ """Initialize a new project"""
81
+ click.echo(f"Creating {name} from {template}")
82
+
83
+ @cli.command()
84
+ @click.argument('environment', type=click.Choice(['dev', 'staging', 'prod']))
85
+ @click.option('--dry-run', is_flag=True, help='Preview only')
86
+ @click.option('--config', type=click.File('r'), help='Config file')
87
+ def deploy(environment, dry_run, config):
88
+ """Deploy to environment"""
89
+ if dry_run:
90
+ click.secho(f"Would deploy to: {environment}", fg='yellow')
91
+ else:
92
+ click.secho(f"Deploying to {environment}...", fg='green')
93
+
94
+ # Nested groups
95
+ @cli.group()
96
+ def config():
97
+ """Manage configuration"""
98
+ pass
99
+
100
+ @config.command('get')
101
+ @click.argument('key')
102
+ def config_get(key):
103
+ """Get config value"""
104
+ click.echo(get_config(key))
105
+
106
+ @config.command('set')
107
+ @click.argument('key')
108
+ @click.argument('value')
109
+ def config_set(key, value):
110
+ """Set config value"""
111
+ set_config(key, value)
112
+
113
+ if __name__ == '__main__':
114
+ cli()
115
+ ```
116
+
117
+ ## Rich Terminal Output
118
+
119
+ Beautiful terminal formatting and progress indicators.
120
+
121
+ ```python
122
+ from rich.console import Console
123
+ from rich.table import Table
124
+ from rich.progress import Progress, SpinnerColumn, TextColumn
125
+ from rich.panel import Panel
126
+ from rich.syntax import Syntax
127
+ from rich import print as rprint
128
+
129
+ console = Console()
130
+
131
+ # Styled output
132
+ console.print("[bold blue]Info:[/] Starting deployment...")
133
+ console.print("[bold green]Success:[/] Deployment complete!")
134
+ console.print("[bold yellow]Warning:[/] Deprecated flag used")
135
+ console.print("[bold red]Error:[/] Deployment failed")
136
+
137
+ # Tables
138
+ table = Table(title="Deployments")
139
+ table.add_column("Environment", style="cyan")
140
+ table.add_column("Status", style="magenta")
141
+ table.add_column("Time", style="green")
142
+
143
+ table.add_row("Production", "✓ Success", "2m 34s")
144
+ table.add_row("Staging", "✗ Failed", "1m 12s")
145
+ console.print(table)
146
+
147
+ # Panels
148
+ console.print(Panel.fit(
149
+ "Deploy to production?",
150
+ title="Confirmation",
151
+ border_style="red"
152
+ ))
153
+
154
+ # Syntax highlighting
155
+ code = '''
156
+ def deploy(env: str):
157
+ print(f"Deploying to {env}")
158
+ '''
159
+ console.print(Syntax(code, "python", theme="monokai"))
160
+
161
+ # Progress bars
162
+ with Progress() as progress:
163
+ task = progress.add_task("[cyan]Deploying...", total=100)
164
+ for i in range(100):
165
+ do_work()
166
+ progress.update(task, advance=1)
167
+
168
+ # Spinners
169
+ with Progress(
170
+ SpinnerColumn(),
171
+ TextColumn("[progress.description]{task.description}"),
172
+ ) as progress:
173
+ task = progress.add_task("Installing dependencies...")
174
+ install_dependencies()
175
+ ```
176
+
177
+ ## Interactive Prompts (questionary)
178
+
179
+ ```python
180
+ import questionary
181
+
182
+ # Text input
183
+ name = questionary.text(
184
+ "Project name:",
185
+ default="my-project",
186
+ validate=lambda x: len(x) > 0 or "Name required"
187
+ ).ask()
188
+
189
+ # Select from list
190
+ environment = questionary.select(
191
+ "Select environment:",
192
+ choices=["development", "staging", "production"],
193
+ default="development"
194
+ ).ask()
195
+
196
+ # Checkbox (multi-select)
197
+ features = questionary.checkbox(
198
+ "Select features:",
199
+ choices=[
200
+ questionary.Choice("TypeScript", checked=True),
201
+ questionary.Choice("ESLint", checked=True),
202
+ questionary.Choice("Prettier", checked=True),
203
+ questionary.Choice("Jest", checked=False),
204
+ ]
205
+ ).ask()
206
+
207
+ # Confirmation
208
+ confirmed = questionary.confirm(
209
+ "Deploy to production?",
210
+ default=False
211
+ ).ask()
212
+
213
+ if confirmed:
214
+ deploy()
215
+
216
+ # Password
217
+ password = questionary.password("Enter password:").ask()
218
+ ```
219
+
220
+ ## Argparse (Standard Library)
221
+
222
+ Built-in argument parsing (verbose but no dependencies).
223
+
224
+ ```python
225
+ import argparse
226
+ import sys
227
+
228
+ def main():
229
+ parser = argparse.ArgumentParser(
230
+ prog='mycli',
231
+ description='My awesome CLI tool',
232
+ )
233
+ parser.add_argument('--version', action='version', version='1.0.0')
234
+
235
+ subparsers = parser.add_subparsers(dest='command', required=True)
236
+
237
+ # Init command
238
+ init_parser = subparsers.add_parser('init', help='Initialize project')
239
+ init_parser.add_argument('name', help='Project name')
240
+ init_parser.add_argument('--template', default='default', help='Template')
241
+ init_parser.add_argument('-f', '--force', action='store_true')
242
+
243
+ # Deploy command
244
+ deploy_parser = subparsers.add_parser('deploy', help='Deploy')
245
+ deploy_parser.add_argument(
246
+ 'environment',
247
+ choices=['dev', 'staging', 'prod'],
248
+ help='Target environment'
249
+ )
250
+ deploy_parser.add_argument('--dry-run', action='store_true')
251
+ deploy_parser.add_argument('--config', type=argparse.FileType('r'))
252
+
253
+ args = parser.parse_args()
254
+
255
+ if args.command == 'init':
256
+ init(args.name, args.template, args.force)
257
+ elif args.command == 'deploy':
258
+ deploy(args.environment, args.dry_run, args.config)
259
+
260
+ if __name__ == '__main__':
261
+ main()
262
+ ```
263
+
264
+ ## Error Handling
265
+
266
+ ```python
267
+ import typer
268
+ import sys
269
+ from pathlib import Path
270
+
271
+ app = typer.Typer()
272
+
273
+ @app.command()
274
+ def deploy():
275
+ try:
276
+ perform_deploy()
277
+ except PermissionError as e:
278
+ typer.secho("Permission denied", fg=typer.colors.RED, err=True)
279
+ typer.echo("Try running with sudo or check file permissions")
280
+ raise typer.Exit(code=77)
281
+ except FileNotFoundError as e:
282
+ typer.secho(f"File not found: {e.filename}", fg=typer.colors.RED, err=True)
283
+ raise typer.Exit(code=127)
284
+ except Exception as e:
285
+ typer.secho(f"Deployment failed: {e}", fg=typer.colors.RED, err=True)
286
+ if os.getenv('DEBUG'):
287
+ import traceback
288
+ traceback.print_exc()
289
+ raise typer.Exit(code=1)
290
+
291
+ # Handle KeyboardInterrupt (Ctrl+C)
292
+ def main():
293
+ try:
294
+ app()
295
+ except KeyboardInterrupt:
296
+ typer.echo("\nOperation cancelled")
297
+ sys.exit(130)
298
+
299
+ if __name__ == "__main__":
300
+ main()
301
+ ```
302
+
303
+ ## Configuration Management
304
+
305
+ ```python
306
+ from pathlib import Path
307
+ from typing import Any
308
+ import json
309
+ import os
310
+
311
+ class Config:
312
+ def __init__(self):
313
+ self.config_paths = [
314
+ Path("/etc/mycli/config.json"), # System
315
+ Path.home() / ".config" / "mycli" / "config.json", # User
316
+ Path.cwd() / "mycli.json", # Project
317
+ ]
318
+
319
+ def load(self) -> dict[str, Any]:
320
+ config = self._defaults()
321
+
322
+ # Load from files (lowest to highest priority)
323
+ for path in self.config_paths:
324
+ if path.exists():
325
+ with path.open() as f:
326
+ config.update(json.load(f))
327
+
328
+ # Override with environment variables
329
+ for key in config.keys():
330
+ env_var = f"MYCLI_{key.upper()}"
331
+ if env_var in os.environ:
332
+ config[key] = os.environ[env_var]
333
+
334
+ return config
335
+
336
+ def _defaults(self) -> dict[str, Any]:
337
+ return {
338
+ "environment": "development",
339
+ "verbose": False,
340
+ "timeout": 30,
341
+ }
342
+ ```
343
+
344
+ ## Setup.py / pyproject.toml
345
+
346
+ ```toml
347
+ # pyproject.toml
348
+ [build-system]
349
+ requires = ["setuptools>=61.0"]
350
+ build-backend = "setuptools.build_meta"
351
+
352
+ [project]
353
+ name = "mycli"
354
+ version = "1.0.0"
355
+ description = "My awesome CLI tool"
356
+ requires-python = ">=3.10"
357
+ dependencies = [
358
+ "typer[all]>=0.9.0",
359
+ "rich>=13.0.0",
360
+ "questionary>=2.0.0",
361
+ ]
362
+
363
+ [project.scripts]
364
+ mycli = "mycli.cli:main"
365
+
366
+ [project.optional-dependencies]
367
+ dev = [
368
+ "pytest>=7.0.0",
369
+ "pytest-cov>=4.0.0",
370
+ ]
371
+ ```
372
+
373
+ ## Testing CLIs
374
+
375
+ ```python
376
+ from typer.testing import CliRunner
377
+ from mycli.cli import app
378
+
379
+ runner = CliRunner()
380
+
381
+ def test_version():
382
+ result = runner.invoke(app, ["--version"])
383
+ assert result.exit_code == 0
384
+ assert "1.0.0" in result.stdout
385
+
386
+ def test_init():
387
+ result = runner.invoke(app, ["init", "my-project"])
388
+ assert result.exit_code == 0
389
+ assert "Creating my-project" in result.stdout
390
+
391
+ def test_init_with_template():
392
+ result = runner.invoke(app, ["init", "my-project", "--template", "react"])
393
+ assert result.exit_code == 0
394
+ assert "react" in result.stdout
395
+
396
+ def test_invalid_command():
397
+ result = runner.invoke(app, ["invalid"])
398
+ assert result.exit_code != 0
399
+ ```
400
+
401
+ ## Progress Bars (tqdm)
402
+
403
+ ```python
404
+ from tqdm import tqdm
405
+ import time
406
+
407
+ # Simple progress bar
408
+ for i in tqdm(range(100), desc="Processing"):
409
+ process_item(i)
410
+
411
+ # Custom format
412
+ with tqdm(total=100, desc="Downloading", unit="MB") as pbar:
413
+ for chunk in download_chunks():
414
+ pbar.update(len(chunk))
415
+
416
+ # Multiple progress bars
417
+ from tqdm import trange
418
+
419
+ for epoch in trange(10, desc="Epochs"):
420
+ for batch in trange(100, desc="Batches", leave=False):
421
+ train_batch(batch)
422
+ ```