@polymorphism-tech/morph-spec 2.1.2 → 2.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.
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Validation CLI
5
+ *
6
+ * Runs all validators (package, architecture, contrast) on the project.
7
+ * Part of Sprint 4: Continuous Validation + Learning System
8
+ *
9
+ * Usage:
10
+ * morph-spec validate [options]
11
+ * morph-spec validate packages
12
+ * morph-spec validate architecture
13
+ * morph-spec validate contrast
14
+ * morph-spec validate all
15
+ *
16
+ * MORPH-SPEC 3.0
17
+ */
18
+
19
+ import chalk from 'chalk';
20
+ import { validatePackages } from '../src/lib/validators/package-validator.js';
21
+ import { validateArchitecture } from '../src/lib/validators/architecture-validator.js';
22
+ import { validateContrast } from '../src/lib/validators/ui-contrast-validator.js';
23
+ import { LearningSystem } from '../src/lib/learning-system.js';
24
+
25
+ const VALIDATORS = {
26
+ packages: {
27
+ name: 'Package Compatibility',
28
+ description: 'Validates NuGet package versions against .NET compatibility matrix',
29
+ run: validatePackages
30
+ },
31
+ architecture: {
32
+ name: 'Architecture Patterns',
33
+ description: 'Detects DI anti-patterns and lifecycle issues in Blazor',
34
+ run: validateArchitecture
35
+ },
36
+ contrast: {
37
+ name: 'UI Contrast (WCAG)',
38
+ description: 'Validates color contrast ratios for accessibility',
39
+ run: validateContrast
40
+ }
41
+ };
42
+
43
+ /**
44
+ * Main validation command
45
+ */
46
+ export async function validateCommand(args = []) {
47
+ const validatorName = args[0] || 'all';
48
+ const options = parseOptions(args);
49
+
50
+ console.log(chalk.bold.cyan('\nšŸ” MORPH-SPEC Validator\n'));
51
+
52
+ if (validatorName === 'all') {
53
+ await runAllValidators(options);
54
+ } else if (VALIDATORS[validatorName]) {
55
+ await runValidator(validatorName, options);
56
+ } else {
57
+ console.error(chalk.red(`āŒ Unknown validator: ${validatorName}`));
58
+ console.log(chalk.gray('\nAvailable validators:'));
59
+ for (const [name, validator] of Object.entries(VALIDATORS)) {
60
+ console.log(chalk.gray(` - ${name}: ${validator.description}`));
61
+ }
62
+ console.log(chalk.gray(' - all: Run all validators'));
63
+ process.exit(1);
64
+ }
65
+
66
+ // Show learning insights if --insights flag
67
+ if (options.insights) {
68
+ await showLearningInsights(options);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Run all validators
74
+ */
75
+ async function runAllValidators(options) {
76
+ const results = {};
77
+ let totalErrors = 0;
78
+ let totalWarnings = 0;
79
+
80
+ console.log(chalk.gray('Running all validators...\n'));
81
+
82
+ for (const [name, validator] of Object.entries(VALIDATORS)) {
83
+ console.log(chalk.cyan(`\nā–ø ${validator.name}`));
84
+ console.log(chalk.gray(` ${validator.description}\n`));
85
+
86
+ try {
87
+ const result = await validator.run('.', {
88
+ verbose: options.verbose,
89
+ autoFix: options.autoFix
90
+ });
91
+
92
+ results[name] = result;
93
+
94
+ // Count errors/warnings
95
+ if (result.errors !== undefined) {
96
+ totalErrors += result.errors;
97
+ totalWarnings += result.warnings || 0;
98
+ } else if (result.results) {
99
+ // Count issues in results array
100
+ const issues = result.results.flatMap(r => r.issues || []);
101
+ totalErrors += issues.filter(i => i.level === 'error').length;
102
+ totalWarnings += issues.filter(i => i.level === 'warning').length;
103
+ }
104
+
105
+ } catch (error) {
106
+ console.error(chalk.red(` āŒ Failed: ${error.message}`));
107
+ results[name] = { status: 'error', error: error.message };
108
+ }
109
+ }
110
+
111
+ // Summary
112
+ console.log(chalk.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
113
+ console.log(chalk.bold('šŸ“Š Validation Summary\n'));
114
+
115
+ if (totalErrors === 0 && totalWarnings === 0) {
116
+ console.log(chalk.green('āœ… All validations passed!'));
117
+ } else {
118
+ if (totalErrors > 0) {
119
+ console.log(chalk.red(`āŒ ${totalErrors} error(s) found`));
120
+ }
121
+ if (totalWarnings > 0) {
122
+ console.log(chalk.yellow(`āš ļø ${totalWarnings} warning(s) found`));
123
+ }
124
+ }
125
+
126
+ console.log('');
127
+
128
+ // Exit with error code if errors found
129
+ if (totalErrors > 0 && !options.noFail) {
130
+ process.exit(1);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Run single validator
136
+ */
137
+ async function runValidator(name, options) {
138
+ const validator = VALIDATORS[name];
139
+
140
+ console.log(chalk.cyan(`ā–ø ${validator.name}`));
141
+ console.log(chalk.gray(` ${validator.description}\n`));
142
+
143
+ try {
144
+ const result = await validator.run('.', {
145
+ verbose: true, // Always verbose for single validator
146
+ autoFix: options.autoFix,
147
+ wcagLevel: options.wcagLevel
148
+ });
149
+
150
+ if (result.status === 'ok') {
151
+ console.log(chalk.green('\nāœ… Validation passed!\n'));
152
+ } else {
153
+ console.log(chalk.yellow('\nāš ļø Validation completed with issues\n'));
154
+
155
+ if (result.errors > 0 && !options.noFail) {
156
+ process.exit(1);
157
+ }
158
+ }
159
+
160
+ } catch (error) {
161
+ console.error(chalk.red(`\nāŒ Validation failed: ${error.message}\n`));
162
+ if (!options.noFail) {
163
+ process.exit(1);
164
+ }
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Show learning insights
170
+ */
171
+ async function showLearningInsights(options) {
172
+ console.log(chalk.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
173
+
174
+ const learner = new LearningSystem('.');
175
+ learner.formatInsights();
176
+
177
+ if (options.verbose) {
178
+ const suggestions = learner.getAllSuggestions();
179
+ learner.formatSuggestions(suggestions);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Parse command-line options
185
+ */
186
+ function parseOptions(args) {
187
+ const options = {
188
+ verbose: false,
189
+ autoFix: false,
190
+ noFail: false,
191
+ insights: false,
192
+ wcagLevel: 'AA'
193
+ };
194
+
195
+ for (const arg of args) {
196
+ if (arg === '--verbose' || arg === '-v') {
197
+ options.verbose = true;
198
+ } else if (arg === '--auto-fix' || arg === '--fix') {
199
+ options.autoFix = true;
200
+ } else if (arg === '--no-fail') {
201
+ options.noFail = true;
202
+ } else if (arg === '--insights' || arg === '-i') {
203
+ options.insights = true;
204
+ } else if (arg === '--wcag-aaa') {
205
+ options.wcagLevel = 'AAA';
206
+ }
207
+ }
208
+
209
+ return options;
210
+ }
211
+
212
+ /**
213
+ * Show help
214
+ */
215
+ export function showValidateHelp() {
216
+ console.log(`
217
+ ${chalk.bold.cyan('morph-spec validate')} - Run project validations
218
+
219
+ ${chalk.bold('USAGE')}
220
+ morph-spec validate [validator] [options]
221
+
222
+ ${chalk.bold('VALIDATORS')}
223
+ all Run all validators (default)
224
+ packages Validate NuGet package compatibility (.NET 10)
225
+ architecture Validate architecture patterns (DI, lifecycle)
226
+ contrast Validate UI contrast ratios (WCAG 2.1)
227
+
228
+ ${chalk.bold('OPTIONS')}
229
+ --verbose, -v Show detailed output
230
+ --auto-fix, --fix Auto-fix issues where possible
231
+ --no-fail Don't exit with error code
232
+ --insights, -i Show learning insights
233
+ --wcag-aaa Use WCAG AAA standard (stricter)
234
+
235
+ ${chalk.bold('EXAMPLES')}
236
+ morph-spec validate # Run all validators
237
+ morph-spec validate packages --fix # Validate packages and auto-fix
238
+ morph-spec validate architecture -v # Verbose architecture validation
239
+ morph-spec validate contrast --wcag-aaa # Stricter contrast validation
240
+ morph-spec validate all --insights # All validators + AI insights
241
+
242
+ ${chalk.bold('EXIT CODES')}
243
+ 0 All validations passed
244
+ 1 Validation errors found (unless --no-fail)
245
+
246
+ ${chalk.bold('INTEGRATION')}
247
+ Add to pre-commit hook:
248
+ morph-spec validate --no-fail
249
+
250
+ CI/CD pipeline:
251
+ morph-spec validate || exit 1
252
+ `);
253
+ }
254
+
255
+ // Run if called directly
256
+ if (import.meta.url === `file://${process.argv[1]}`) {
257
+ const args = process.argv.slice(2);
258
+
259
+ if (args.includes('--help') || args.includes('-h')) {
260
+ showValidateHelp();
261
+ process.exit(0);
262
+ }
263
+
264
+ validateCommand(args).catch(error => {
265
+ console.error(chalk.red(`\nāŒ Error: ${error.message}\n`));
266
+ process.exit(1);
267
+ });
268
+ }
@@ -13,6 +13,17 @@ Especialista em Entity Framework Core para modelagem de dados e banco de dados.
13
13
 
14
14
  Keywords: `entity`, `database`, `migration`, `ef core`, `dbcontext`, `table`, `column`, `relationship`, `query`
15
15
 
16
+ ---
17
+
18
+ ## āš ļø .NET 10 Compatibility
19
+
20
+ **ALWAYS check package version:**
21
+ - Entity Framework Core: **≄ 10.0.0** (required for .NET 10)
22
+ - New features: Vector search, primitive collections
23
+ - Breaking changes: Required navigation properties, query splitting
24
+
25
+ **Consult:** `framework/standards/dotnet10-compatibility.md`
26
+
16
27
  ## Estrutura de Entidades
17
28
 
18
29
  ```csharp
@@ -13,6 +13,16 @@ Especialista em background jobs, tarefas agendadas e processamento assĆ­ncrono c
13
13
 
14
14
  Keywords: `scheduled`, `job`, `background`, `cron`, `recurring`, `batch`, `queue`, `hangfire`, `task`
15
15
 
16
+ ---
17
+
18
+ ## āš ļø .NET 10 Compatibility
19
+
20
+ **ALWAYS check package version:**
21
+ - Hangfire.AspNetCore: **≄ 1.8.22** (required for .NET 10)
22
+ - No breaking changes from .NET 9
23
+
24
+ **Consult:** `framework/standards/dotnet10-compatibility.md`
25
+
16
26
  ## Por que Hangfire (nĆ£o Azure Functions)
17
27
 
18
28
  | Aspecto | Hangfire | Azure Functions |
@@ -19,6 +19,46 @@ Especialista em design de interfaces, experiência do usuÔrio e componentes Bla
19
19
  - `blazor`, `component`, `page`, `razor`, `ui`, `ux`, `design`, `layout`
20
20
  - `wizard`, `dashboard`, `form`, `chart`, `table`, `dialog`, `modal`
21
21
 
22
+ ---
23
+
24
+ ## šŸŽØ Design Moderno (2025+)
25
+
26
+ **SEMPRE aim for modern, beautiful UI:**
27
+
28
+ ### Visual Trends
29
+ - **Glassmorphism**: `backdrop-filter: blur(10px)`, semi-transparent backgrounds
30
+ - **Gradients**: Purple (#667eea → #764ba2), Blue-Purple (#4facfe → #00f2fe)
31
+ - **Soft Shadows**: `box-shadow: 0 4px 6px rgba(0,0,0,0.1)`
32
+ - **Rounded Corners**: `border-radius: 12px-16px` (não 4px!)
33
+ - **Micro-interactions**: Hover effects, transitions (transform, scale, opacity)
34
+
35
+ ### Component Patterns
36
+ - **Cards with elevation**: Subtle shadow + hover lift effect
37
+ - **Skeleton loaders**: Durante loading, não apenas spinner
38
+ - **Toast notifications**: Bottom-right, auto-dismiss
39
+ - **Empty states**: Ilustração + CTA, nunca apenas texto
40
+ - **Dark mode support**: ALWAYS design for both light/dark
41
+
42
+ ### Layout Principles
43
+ - **Whitespace**: Generous padding (32px-48px containers, não 16px)
44
+ - **Grid systems**: 12-column grid, gap 24px-32px
45
+ - **Typography hierarchy**: Clear sizes (xs: 12px, sm: 14px, base: 16px, lg: 18px, xl: 20px, 2xl: 24px)
46
+ - **Contrast**: WCAG AA minimum (4.5:1 text, 3:1 UI components)
47
+
48
+ ### ReferĆŖncias Modernas
49
+ - **Linear** (linear.app): Clean, purple gradients, minimal
50
+ - **Vercel** (vercel.com): Black & white, subtle animations
51
+ - **Stripe** (stripe.com): Professional, blue accent, great forms
52
+ - **Dribbble** (dribbble.com/tags/dashboard): Modern dashboard designs
53
+ - **Awwwards** (awwwards.com): Award-winning UI patterns
54
+
55
+ **Framework integration:**
56
+ - Use `src/lib/mockup-generator.js` for wireframes
57
+ - Use `framework/templates/ui-components/` for component scaffolding
58
+ - Consult `framework/standards/blazor-pitfalls.md` for Blazor-specific patterns
59
+
60
+ ---
61
+
22
62
  ## Deliverables da FASE 1.5
23
63
 
24
64
  | Arquivo | ConteĆŗdo |
@@ -17,6 +17,24 @@ Stack principal para desenvolvimento de aplicaƧƵes web com .NET e Blazor Serve
17
17
 
18
18
  Keywords: `blazor`, `razor`, `server-side`, `.net`, `csharp`, `dotnet`
19
19
 
20
+ ---
21
+
22
+ ## āš ļø CRITICAL PITFALLS (Read FIRST!)
23
+
24
+ **ALWAYS consult framework standards before implementing:**
25
+ - `framework/standards/blazor-pitfalls.md` - Common issues & solutions
26
+ - `framework/standards/blazor-lifecycle.md` - Lifecycle patterns
27
+ - `framework/standards/program-cs-checklist.md` - Required Program.cs setup
28
+ - `framework/standards/dotnet10-compatibility.md` - Package compatibility
29
+
30
+ **Quick Checklist:**
31
+ - [ ] DI Pattern: Use HttpClient, NOT direct Application layer injection
32
+ - [ ] Lifecycle: JSRuntime ONLY in OnAfterRenderAsync(firstRender)
33
+ - [ ] Program.cs: app.UseStaticFiles() BEFORE app.UseAntiforgery()
34
+ - [ ] File Upload: Specify maxAllowedSize in OpenReadStream()
35
+ - [ ] RenderMode: NO @rendermode on MainLayout
36
+ - [ ] Package Versions: MudBlazor ≄ 8.15.0 for .NET 10
37
+
20
38
  ## Estrutura de Projeto
21
39
 
22
40
  ```
@@ -0,0 +1,188 @@
1
+ {
2
+ "version": "3.0.0",
3
+ "project": {
4
+ "name": "FishArt",
5
+ "type": "blazor-server",
6
+ "createdAt": "2024-01-15T10:00:00Z",
7
+ "updatedAt": "2024-01-15T14:30:00Z"
8
+ },
9
+ "features": {
10
+ "scheduled-reports": {
11
+ "status": "in_progress",
12
+ "phase": "implement",
13
+ "activeAgents": [
14
+ "standards-architect",
15
+ "blazor-builder",
16
+ "ef-modeler",
17
+ "hangfire-orchestrator"
18
+ ],
19
+ "tasks": [
20
+ {
21
+ "id": "T001",
22
+ "title": "Create ScheduledReport entity",
23
+ "status": "completed",
24
+ "completedAt": "2024-01-15T10:30:00Z",
25
+ "completedBy": "claude",
26
+ "dependencies": [],
27
+ "files": [
28
+ "Domain/Entities/ScheduledReport.cs",
29
+ "Infrastructure/Persistence/Config/ScheduledReportConfig.cs"
30
+ ],
31
+ "checkpoint": "CHECKPOINT_001"
32
+ },
33
+ {
34
+ "id": "T002",
35
+ "title": "Create CreateReportCommand + Handler",
36
+ "status": "completed",
37
+ "completedAt": "2024-01-15T11:15:00Z",
38
+ "completedBy": "claude",
39
+ "dependencies": ["T001"],
40
+ "files": [
41
+ "Application/Commands/CreateReport/CreateReportCommand.cs",
42
+ "Application/Commands/CreateReport/CreateReportHandler.cs"
43
+ ]
44
+ },
45
+ {
46
+ "id": "T003",
47
+ "title": "Create Hangfire job for report generation",
48
+ "status": "completed",
49
+ "completedAt": "2024-01-15T12:00:00Z",
50
+ "completedBy": "claude",
51
+ "dependencies": ["T002"],
52
+ "files": [
53
+ "Infrastructure/Jobs/GenerateReportJob.cs"
54
+ ],
55
+ "checkpoint": "CHECKPOINT_002"
56
+ },
57
+ {
58
+ "id": "T004",
59
+ "title": "Create ReportList component (Blazor)",
60
+ "status": "in_progress",
61
+ "startedAt": "2024-01-15T13:00:00Z",
62
+ "dependencies": ["T001"],
63
+ "files": [
64
+ "Components/Reports/ReportList.razor",
65
+ "Components/Reports/ReportList.razor.cs",
66
+ "Components/Reports/ReportList.razor.css"
67
+ ]
68
+ },
69
+ {
70
+ "id": "T005",
71
+ "title": "Create CreateReportForm component (Blazor)",
72
+ "status": "pending",
73
+ "dependencies": ["T002"],
74
+ "files": [
75
+ "Components/Reports/CreateReportForm.razor",
76
+ "Components/Reports/CreateReportForm.razor.cs",
77
+ "Components/Reports/CreateReportForm.razor.css"
78
+ ]
79
+ },
80
+ {
81
+ "id": "T006",
82
+ "title": "Create API controller for reports",
83
+ "status": "pending",
84
+ "dependencies": ["T002"],
85
+ "files": [
86
+ "API/Controllers/ReportsController.cs"
87
+ ]
88
+ },
89
+ {
90
+ "id": "T007",
91
+ "title": "Add Hangfire dashboard configuration",
92
+ "status": "pending",
93
+ "dependencies": ["T003"],
94
+ "files": [
95
+ "Program.cs"
96
+ ]
97
+ },
98
+ {
99
+ "id": "T008",
100
+ "title": "Create unit tests for CreateReportCommand",
101
+ "status": "pending",
102
+ "dependencies": ["T002"],
103
+ "files": [
104
+ "Tests/Application/Commands/CreateReportCommandTests.cs"
105
+ ]
106
+ },
107
+ {
108
+ "id": "T009",
109
+ "title": "Create integration tests for report generation",
110
+ "status": "pending",
111
+ "dependencies": ["T003", "T006"],
112
+ "files": [
113
+ "Tests/Integration/ReportGenerationTests.cs"
114
+ ],
115
+ "checkpoint": "CHECKPOINT_003"
116
+ }
117
+ ],
118
+ "progress": {
119
+ "total": 9,
120
+ "completed": 3,
121
+ "inProgress": 1,
122
+ "pending": 5,
123
+ "percentage": 33
124
+ },
125
+ "checkpoints": [
126
+ {
127
+ "id": "CHECKPOINT_001",
128
+ "timestamp": "2024-01-15T10:30:00Z",
129
+ "tasksCompleted": ["T001"],
130
+ "note": "Checkpoint: Create ScheduledReport entity"
131
+ },
132
+ {
133
+ "id": "CHECKPOINT_002",
134
+ "timestamp": "2024-01-15T12:00:00Z",
135
+ "tasksCompleted": ["T003"],
136
+ "note": "Checkpoint: Create Hangfire job for report generation"
137
+ },
138
+ {
139
+ "id": "CHECKPOINT_AUTO_1705326000000",
140
+ "timestamp": "2024-01-15T12:00:00Z",
141
+ "tasksCompleted": ["T001", "T002", "T003"],
142
+ "note": "Auto-checkpoint: 3 tasks completed"
143
+ }
144
+ ],
145
+ "outputs": {
146
+ "proposal": {
147
+ "created": true,
148
+ "path": ".morph/project/outputs/scheduled-reports/proposal.md"
149
+ },
150
+ "spec": {
151
+ "created": true,
152
+ "path": ".morph/project/outputs/scheduled-reports/spec.md"
153
+ },
154
+ "contracts": {
155
+ "created": true,
156
+ "path": ".morph/project/outputs/scheduled-reports/contracts.cs"
157
+ },
158
+ "ui-spec": {
159
+ "created": true,
160
+ "path": ".morph/project/outputs/scheduled-reports/ui-spec.md"
161
+ },
162
+ "tasks": {
163
+ "created": true,
164
+ "note": "Tasks are now inline in state.json (schema 3.0.0)"
165
+ },
166
+ "decisions": {
167
+ "created": true,
168
+ "path": ".morph/project/outputs/scheduled-reports/decisions.md"
169
+ }
170
+ },
171
+ "costs": {
172
+ "estimated": 5.50,
173
+ "approved": true,
174
+ "breakdown": {
175
+ "hangfire": 0,
176
+ "storage": 0.02,
177
+ "sql": 4.99,
178
+ "containerApp": 0.49
179
+ }
180
+ }
181
+ }
182
+ },
183
+ "metadata": {
184
+ "totalFeatures": 1,
185
+ "completedFeatures": 0,
186
+ "totalCostEstimated": 5.50
187
+ }
188
+ }
@@ -50,6 +50,7 @@ export async function detectStructure(projectPath) {
50
50
  * Detect stack type
51
51
  */
52
52
  async function detectStack(projectPath) {
53
+ // Order matters: more specific patterns first
53
54
  const patterns = {
54
55
  blazor: [
55
56
  '**/*.razor',
@@ -57,19 +58,47 @@ async function detectStack(projectPath) {
57
58
  '**/Components/**/*.razor'
58
59
  ],
59
60
  nextjs: [
60
- 'pages/**/*.tsx',
61
- 'app/**/*.tsx',
62
61
  'next.config.js',
63
- 'next.config.mjs'
62
+ 'next.config.mjs',
63
+ 'next.config.ts',
64
+ 'pages/**/*.tsx',
65
+ 'app/**/*.tsx'
64
66
  ],
65
67
  shopify: [
66
68
  '**/*.liquid',
67
69
  'sections/**/*.liquid',
68
70
  'templates/**/*.liquid'
69
71
  ],
72
+ react: [
73
+ 'src/App.jsx',
74
+ 'src/App.tsx',
75
+ 'vite.config.js',
76
+ 'craco.config.js'
77
+ ],
78
+ vue: [
79
+ 'vite.config.js', // Vue 3 + Vite
80
+ 'vue.config.js', // Vue 2
81
+ 'src/App.vue'
82
+ ],
70
83
  dotnet: [
71
84
  '**/*.csproj',
72
85
  '**/Program.cs'
86
+ ],
87
+ typescript: [
88
+ 'tsconfig.json',
89
+ 'src/**/*.ts'
90
+ ],
91
+ nodejs: [
92
+ 'package.json' // Fallback: generic Node.js
93
+ ],
94
+ python: [
95
+ 'requirements.txt',
96
+ 'pyproject.toml',
97
+ 'setup.py'
98
+ ],
99
+ go: [
100
+ 'go.mod',
101
+ 'main.go'
73
102
  ]
74
103
  };
75
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polymorphism-tech/morph-spec",
3
- "version": "2.1.2",
3
+ "version": "2.3.0",
4
4
  "description": "MORPH-SPEC v2.0: AI-First development framework with .NET 10, Microsoft Agent Framework, and Fluent UI Blazor",
5
5
  "keywords": [
6
6
  "claude-code",