agentic-team-templates 0.3.0
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 +280 -0
- package/bin/cli.js +5 -0
- package/package.json +47 -0
- package/src/index.js +521 -0
- package/templates/_shared/code-quality.md +162 -0
- package/templates/_shared/communication.md +114 -0
- package/templates/_shared/core-principles.md +62 -0
- package/templates/_shared/git-workflow.md +165 -0
- package/templates/_shared/security-fundamentals.md +173 -0
- package/templates/blockchain/.cursorrules/defi-patterns.md +520 -0
- package/templates/blockchain/.cursorrules/gas-optimization.md +339 -0
- package/templates/blockchain/.cursorrules/overview.md +130 -0
- package/templates/blockchain/.cursorrules/security.md +318 -0
- package/templates/blockchain/.cursorrules/smart-contracts.md +364 -0
- package/templates/blockchain/.cursorrules/testing.md +415 -0
- package/templates/blockchain/.cursorrules/web3-integration.md +538 -0
- package/templates/blockchain/CLAUDE.md +389 -0
- package/templates/cli-tools/.cursorrules/architecture.md +412 -0
- package/templates/cli-tools/.cursorrules/arguments.md +406 -0
- package/templates/cli-tools/.cursorrules/distribution.md +546 -0
- package/templates/cli-tools/.cursorrules/error-handling.md +455 -0
- package/templates/cli-tools/.cursorrules/overview.md +136 -0
- package/templates/cli-tools/.cursorrules/testing.md +537 -0
- package/templates/cli-tools/.cursorrules/user-experience.md +545 -0
- package/templates/cli-tools/CLAUDE.md +356 -0
- package/templates/data-engineering/.cursorrules/data-modeling.md +367 -0
- package/templates/data-engineering/.cursorrules/data-quality.md +455 -0
- package/templates/data-engineering/.cursorrules/overview.md +85 -0
- package/templates/data-engineering/.cursorrules/performance.md +339 -0
- package/templates/data-engineering/.cursorrules/pipeline-design.md +280 -0
- package/templates/data-engineering/.cursorrules/security.md +460 -0
- package/templates/data-engineering/.cursorrules/testing.md +452 -0
- package/templates/data-engineering/CLAUDE.md +974 -0
- package/templates/devops-sre/.cursorrules/capacity-planning.md +653 -0
- package/templates/devops-sre/.cursorrules/change-management.md +584 -0
- package/templates/devops-sre/.cursorrules/chaos-engineering.md +651 -0
- package/templates/devops-sre/.cursorrules/disaster-recovery.md +641 -0
- package/templates/devops-sre/.cursorrules/incident-management.md +565 -0
- package/templates/devops-sre/.cursorrules/observability.md +714 -0
- package/templates/devops-sre/.cursorrules/overview.md +230 -0
- package/templates/devops-sre/.cursorrules/postmortems.md +588 -0
- package/templates/devops-sre/.cursorrules/runbooks.md +760 -0
- package/templates/devops-sre/.cursorrules/slo-sli.md +617 -0
- package/templates/devops-sre/.cursorrules/toil-reduction.md +567 -0
- package/templates/devops-sre/CLAUDE.md +1007 -0
- package/templates/documentation/.cursorrules/adr.md +277 -0
- package/templates/documentation/.cursorrules/api-documentation.md +411 -0
- package/templates/documentation/.cursorrules/code-comments.md +253 -0
- package/templates/documentation/.cursorrules/maintenance.md +260 -0
- package/templates/documentation/.cursorrules/overview.md +82 -0
- package/templates/documentation/.cursorrules/readme-standards.md +306 -0
- package/templates/documentation/CLAUDE.md +120 -0
- package/templates/fullstack/.cursorrules/api-contracts.md +331 -0
- package/templates/fullstack/.cursorrules/architecture.md +298 -0
- package/templates/fullstack/.cursorrules/overview.md +109 -0
- package/templates/fullstack/.cursorrules/shared-types.md +348 -0
- package/templates/fullstack/.cursorrules/testing.md +386 -0
- package/templates/fullstack/CLAUDE.md +349 -0
- package/templates/ml-ai/.cursorrules/data-engineering.md +483 -0
- package/templates/ml-ai/.cursorrules/deployment.md +601 -0
- package/templates/ml-ai/.cursorrules/model-development.md +538 -0
- package/templates/ml-ai/.cursorrules/monitoring.md +658 -0
- package/templates/ml-ai/.cursorrules/overview.md +131 -0
- package/templates/ml-ai/.cursorrules/security.md +637 -0
- package/templates/ml-ai/.cursorrules/testing.md +678 -0
- package/templates/ml-ai/CLAUDE.md +1136 -0
- package/templates/mobile/.cursorrules/navigation.md +246 -0
- package/templates/mobile/.cursorrules/offline-first.md +302 -0
- package/templates/mobile/.cursorrules/overview.md +71 -0
- package/templates/mobile/.cursorrules/performance.md +345 -0
- package/templates/mobile/.cursorrules/testing.md +339 -0
- package/templates/mobile/CLAUDE.md +233 -0
- package/templates/platform-engineering/.cursorrules/ci-cd.md +778 -0
- package/templates/platform-engineering/.cursorrules/developer-experience.md +632 -0
- package/templates/platform-engineering/.cursorrules/infrastructure-as-code.md +600 -0
- package/templates/platform-engineering/.cursorrules/kubernetes.md +710 -0
- package/templates/platform-engineering/.cursorrules/observability.md +747 -0
- package/templates/platform-engineering/.cursorrules/overview.md +215 -0
- package/templates/platform-engineering/.cursorrules/security.md +855 -0
- package/templates/platform-engineering/.cursorrules/testing.md +878 -0
- package/templates/platform-engineering/CLAUDE.md +850 -0
- package/templates/utility-agent/.cursorrules/action-control.md +284 -0
- package/templates/utility-agent/.cursorrules/context-management.md +186 -0
- package/templates/utility-agent/.cursorrules/hallucination-prevention.md +253 -0
- package/templates/utility-agent/.cursorrules/overview.md +78 -0
- package/templates/utility-agent/.cursorrules/token-optimization.md +369 -0
- package/templates/utility-agent/CLAUDE.md +513 -0
- package/templates/web-backend/.cursorrules/api-design.md +255 -0
- package/templates/web-backend/.cursorrules/authentication.md +309 -0
- package/templates/web-backend/.cursorrules/database-patterns.md +298 -0
- package/templates/web-backend/.cursorrules/error-handling.md +366 -0
- package/templates/web-backend/.cursorrules/overview.md +69 -0
- package/templates/web-backend/.cursorrules/security.md +358 -0
- package/templates/web-backend/.cursorrules/testing.md +395 -0
- package/templates/web-backend/CLAUDE.md +366 -0
- package/templates/web-frontend/.cursorrules/accessibility.md +296 -0
- package/templates/web-frontend/.cursorrules/component-patterns.md +204 -0
- package/templates/web-frontend/.cursorrules/overview.md +72 -0
- package/templates/web-frontend/.cursorrules/performance.md +325 -0
- package/templates/web-frontend/.cursorrules/state-management.md +227 -0
- package/templates/web-frontend/.cursorrules/styling.md +271 -0
- package/templates/web-frontend/.cursorrules/testing.md +311 -0
- package/templates/web-frontend/CLAUDE.md +399 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# CLI Architecture
|
|
2
|
+
|
|
3
|
+
Patterns for structuring maintainable, testable command-line applications.
|
|
4
|
+
|
|
5
|
+
## Command Structure
|
|
6
|
+
|
|
7
|
+
### Root Command Pattern
|
|
8
|
+
|
|
9
|
+
Every CLI has a root command that sets up global configuration:
|
|
10
|
+
|
|
11
|
+
```go
|
|
12
|
+
// Go with Cobra
|
|
13
|
+
var rootCmd = &cobra.Command{
|
|
14
|
+
Use: "mytool",
|
|
15
|
+
Short: "A brief description of your tool",
|
|
16
|
+
Long: `A longer description that spans multiple lines
|
|
17
|
+
and provides more context about the tool.`,
|
|
18
|
+
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
|
19
|
+
return initConfig()
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func init() {
|
|
24
|
+
// Global flags (available to all subcommands)
|
|
25
|
+
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file")
|
|
26
|
+
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "verbose output")
|
|
27
|
+
rootCmd.PersistentFlags().Bool("no-color", false, "disable colored output")
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// TypeScript with Commander
|
|
33
|
+
import { Command } from 'commander';
|
|
34
|
+
|
|
35
|
+
const program = new Command();
|
|
36
|
+
|
|
37
|
+
program
|
|
38
|
+
.name('mytool')
|
|
39
|
+
.description('A brief description of your tool')
|
|
40
|
+
.version('1.0.0')
|
|
41
|
+
.option('-v, --verbose', 'verbose output')
|
|
42
|
+
.option('--no-color', 'disable colored output')
|
|
43
|
+
.option('-c, --config <path>', 'config file path');
|
|
44
|
+
|
|
45
|
+
// Add subcommands
|
|
46
|
+
program.addCommand(initCommand);
|
|
47
|
+
program.addCommand(buildCommand);
|
|
48
|
+
|
|
49
|
+
program.parse();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Subcommand Pattern
|
|
53
|
+
|
|
54
|
+
Organize related functionality into subcommands:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
mytool init # Initialize a new project
|
|
58
|
+
mytool build # Build the project
|
|
59
|
+
mytool deploy # Deploy to production
|
|
60
|
+
mytool config get # Nested subcommand
|
|
61
|
+
mytool config set
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```go
|
|
65
|
+
// Go subcommand
|
|
66
|
+
var buildCmd = &cobra.Command{
|
|
67
|
+
Use: "build [flags] [path]",
|
|
68
|
+
Short: "Build the project",
|
|
69
|
+
Long: `Build compiles the project and outputs artifacts.`,
|
|
70
|
+
Args: cobra.MaximumNArgs(1),
|
|
71
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
72
|
+
path := "."
|
|
73
|
+
if len(args) > 0 {
|
|
74
|
+
path = args[0]
|
|
75
|
+
}
|
|
76
|
+
return runBuild(cmd.Context(), path)
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func init() {
|
|
81
|
+
// Local flags (only for this command)
|
|
82
|
+
buildCmd.Flags().StringP("output", "o", "dist", "output directory")
|
|
83
|
+
buildCmd.Flags().Bool("minify", false, "minify output")
|
|
84
|
+
|
|
85
|
+
rootCmd.AddCommand(buildCmd)
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Configuration Management
|
|
90
|
+
|
|
91
|
+
### Configuration Precedence
|
|
92
|
+
|
|
93
|
+
Always follow this hierarchy (highest to lowest priority):
|
|
94
|
+
|
|
95
|
+
1. **Command-line flags** - Explicit user intent
|
|
96
|
+
2. **Environment variables** - Deployment/CI configuration
|
|
97
|
+
3. **Local config file** - Project-specific settings
|
|
98
|
+
4. **Global config file** - User preferences
|
|
99
|
+
5. **Default values** - Sensible fallbacks
|
|
100
|
+
|
|
101
|
+
```go
|
|
102
|
+
// Go with Viper
|
|
103
|
+
func initConfig() error {
|
|
104
|
+
if cfgFile != "" {
|
|
105
|
+
viper.SetConfigFile(cfgFile)
|
|
106
|
+
} else {
|
|
107
|
+
// Look for config in standard locations
|
|
108
|
+
viper.SetConfigName(".mytool")
|
|
109
|
+
viper.SetConfigType("yaml")
|
|
110
|
+
viper.AddConfigPath(".") // Current directory
|
|
111
|
+
viper.AddConfigPath("$HOME") // Home directory
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Environment variables
|
|
115
|
+
viper.SetEnvPrefix("MYTOOL")
|
|
116
|
+
viper.AutomaticEnv()
|
|
117
|
+
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
|
118
|
+
|
|
119
|
+
// Bind flags to viper
|
|
120
|
+
viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose"))
|
|
121
|
+
|
|
122
|
+
// Read config file (optional)
|
|
123
|
+
if err := viper.ReadInConfig(); err != nil {
|
|
124
|
+
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
|
125
|
+
return err
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return nil
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// TypeScript with cosmiconfig
|
|
135
|
+
import { cosmiconfig } from 'cosmiconfig';
|
|
136
|
+
|
|
137
|
+
interface Config {
|
|
138
|
+
verbose: boolean;
|
|
139
|
+
output: string;
|
|
140
|
+
plugins: string[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function loadConfig(): Promise<Config> {
|
|
144
|
+
const explorer = cosmiconfig('mytool');
|
|
145
|
+
const result = await explorer.search();
|
|
146
|
+
|
|
147
|
+
const fileConfig = result?.config ?? {};
|
|
148
|
+
|
|
149
|
+
// Merge with precedence: flags > env > file > defaults
|
|
150
|
+
return {
|
|
151
|
+
verbose: process.env.MYTOOL_VERBOSE === 'true' || fileConfig.verbose || false,
|
|
152
|
+
output: process.env.MYTOOL_OUTPUT || fileConfig.output || 'dist',
|
|
153
|
+
plugins: fileConfig.plugins || [],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Config File Formats
|
|
159
|
+
|
|
160
|
+
Support common formats:
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
# .mytool.yaml
|
|
164
|
+
verbose: true
|
|
165
|
+
output: dist
|
|
166
|
+
plugins:
|
|
167
|
+
- typescript
|
|
168
|
+
- minify
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
// .mytoolrc.json
|
|
173
|
+
{
|
|
174
|
+
"verbose": true,
|
|
175
|
+
"output": "dist",
|
|
176
|
+
"plugins": ["typescript", "minify"]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```toml
|
|
181
|
+
# mytool.toml
|
|
182
|
+
verbose = true
|
|
183
|
+
output = "dist"
|
|
184
|
+
plugins = ["typescript", "minify"]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Dependency Injection
|
|
188
|
+
|
|
189
|
+
### Service Pattern
|
|
190
|
+
|
|
191
|
+
Inject dependencies for testability:
|
|
192
|
+
|
|
193
|
+
```go
|
|
194
|
+
// Define interfaces
|
|
195
|
+
type Logger interface {
|
|
196
|
+
Info(msg string, fields ...any)
|
|
197
|
+
Error(msg string, fields ...any)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
type FileSystem interface {
|
|
201
|
+
ReadFile(path string) ([]byte, error)
|
|
202
|
+
WriteFile(path string, data []byte) error
|
|
203
|
+
Exists(path string) bool
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Service with dependencies
|
|
207
|
+
type Builder struct {
|
|
208
|
+
logger Logger
|
|
209
|
+
fs FileSystem
|
|
210
|
+
config *BuildConfig
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
func NewBuilder(logger Logger, fs FileSystem, config *BuildConfig) *Builder {
|
|
214
|
+
return &Builder{
|
|
215
|
+
logger: logger,
|
|
216
|
+
fs: fs,
|
|
217
|
+
config: config,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
func (b *Builder) Build(ctx context.Context, path string) error {
|
|
222
|
+
b.logger.Info("Starting build", "path", path)
|
|
223
|
+
// Use b.fs instead of direct file operations
|
|
224
|
+
// This allows mocking in tests
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// TypeScript with dependency injection
|
|
230
|
+
interface Logger {
|
|
231
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
232
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
interface FileSystem {
|
|
236
|
+
readFile(path: string): Promise<string>;
|
|
237
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
238
|
+
exists(path: string): Promise<boolean>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
class Builder {
|
|
242
|
+
constructor(
|
|
243
|
+
private logger: Logger,
|
|
244
|
+
private fs: FileSystem,
|
|
245
|
+
private config: BuildConfig
|
|
246
|
+
) {}
|
|
247
|
+
|
|
248
|
+
async build(path: string): Promise<void> {
|
|
249
|
+
this.logger.info('Starting build', { path });
|
|
250
|
+
// Use this.fs instead of direct fs operations
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// In production
|
|
255
|
+
const builder = new Builder(consoleLogger, nodeFs, config);
|
|
256
|
+
|
|
257
|
+
// In tests
|
|
258
|
+
const builder = new Builder(mockLogger, mockFs, testConfig);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Context and Cancellation
|
|
262
|
+
|
|
263
|
+
### Handling Interrupts
|
|
264
|
+
|
|
265
|
+
Gracefully handle Ctrl+C and other signals:
|
|
266
|
+
|
|
267
|
+
```go
|
|
268
|
+
func main() {
|
|
269
|
+
ctx, cancel := context.WithCancel(context.Background())
|
|
270
|
+
|
|
271
|
+
// Handle interrupt
|
|
272
|
+
sigChan := make(chan os.Signal, 1)
|
|
273
|
+
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
274
|
+
|
|
275
|
+
go func() {
|
|
276
|
+
<-sigChan
|
|
277
|
+
fmt.Fprintln(os.Stderr, "\nInterrupted, cleaning up...")
|
|
278
|
+
cancel()
|
|
279
|
+
}()
|
|
280
|
+
|
|
281
|
+
if err := rootCmd.ExecuteContext(ctx); err != nil {
|
|
282
|
+
os.Exit(1)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// In command implementation
|
|
287
|
+
func runBuild(ctx context.Context, path string) error {
|
|
288
|
+
for _, file := range files {
|
|
289
|
+
// Check for cancellation
|
|
290
|
+
select {
|
|
291
|
+
case <-ctx.Done():
|
|
292
|
+
return ctx.Err()
|
|
293
|
+
default:
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if err := processFile(ctx, file); err != nil {
|
|
297
|
+
return err
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return nil
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Node.js signal handling
|
|
306
|
+
process.on('SIGINT', () => {
|
|
307
|
+
console.error('\nInterrupted, cleaning up...');
|
|
308
|
+
cleanup();
|
|
309
|
+
process.exit(130); // 128 + signal number
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
process.on('SIGTERM', () => {
|
|
313
|
+
cleanup();
|
|
314
|
+
process.exit(143);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Using AbortController
|
|
318
|
+
const controller = new AbortController();
|
|
319
|
+
|
|
320
|
+
process.on('SIGINT', () => controller.abort());
|
|
321
|
+
|
|
322
|
+
async function build(signal: AbortSignal) {
|
|
323
|
+
for (const file of files) {
|
|
324
|
+
if (signal.aborted) {
|
|
325
|
+
throw new Error('Build cancelled');
|
|
326
|
+
}
|
|
327
|
+
await processFile(file);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Plugin Architecture
|
|
333
|
+
|
|
334
|
+
For extensible CLIs:
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
// Plugin interface
|
|
338
|
+
interface Plugin {
|
|
339
|
+
name: string;
|
|
340
|
+
version: string;
|
|
341
|
+
setup(context: PluginContext): void | Promise<void>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
interface PluginContext {
|
|
345
|
+
config: Config;
|
|
346
|
+
logger: Logger;
|
|
347
|
+
registerCommand(command: Command): void;
|
|
348
|
+
registerHook(event: string, handler: HookHandler): void;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Plugin loader
|
|
352
|
+
async function loadPlugins(config: Config): Promise<Plugin[]> {
|
|
353
|
+
const plugins: Plugin[] = [];
|
|
354
|
+
|
|
355
|
+
for (const pluginName of config.plugins) {
|
|
356
|
+
try {
|
|
357
|
+
const plugin = await import(pluginName);
|
|
358
|
+
plugins.push(plugin.default);
|
|
359
|
+
} catch (error) {
|
|
360
|
+
throw new Error(`Failed to load plugin: ${pluginName}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return plugins;
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Anti-Patterns to Avoid
|
|
369
|
+
|
|
370
|
+
### Global State
|
|
371
|
+
|
|
372
|
+
```go
|
|
373
|
+
// Bad: Global mutable state
|
|
374
|
+
var config Config
|
|
375
|
+
|
|
376
|
+
func doSomething() {
|
|
377
|
+
// Uses global config - hard to test
|
|
378
|
+
if config.Verbose { ... }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Good: Pass dependencies
|
|
382
|
+
func doSomething(config *Config) {
|
|
383
|
+
if config.Verbose { ... }
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### God Commands
|
|
388
|
+
|
|
389
|
+
```go
|
|
390
|
+
// Bad: One command does everything
|
|
391
|
+
var doEverythingCmd = &cobra.Command{
|
|
392
|
+
Use: "do",
|
|
393
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
394
|
+
// 500 lines of code doing init, build, deploy, etc.
|
|
395
|
+
},
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Good: Separate concerns
|
|
399
|
+
var initCmd = &cobra.Command{ ... }
|
|
400
|
+
var buildCmd = &cobra.Command{ ... }
|
|
401
|
+
var deployCmd = &cobra.Command{ ... }
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Hardcoded Paths
|
|
405
|
+
|
|
406
|
+
```go
|
|
407
|
+
// Bad: Hardcoded paths
|
|
408
|
+
configPath := "/etc/mytool/config.yaml"
|
|
409
|
+
|
|
410
|
+
// Good: Respect XDG/platform conventions
|
|
411
|
+
configPath := filepath.Join(xdg.ConfigHome, "mytool", "config.yaml")
|
|
412
|
+
```
|