@voodocs/cli 1.0.6 → 2.0.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/lib/cli/init.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """@darkarts
2
- ⊢{cli:init}
3
- ∂{click,pathlib,json}
2
+ ⊢{cli:init-wizard}
3
+ ∂{click,pathlib,json,os,subprocess}
4
4
  ⚠{write-access:cwd}
5
- ⊨{idempotent:config-creation}
5
+ ⊨{idempotent:config-creation,rerunnable:upgrade-support}
6
6
  🔒{read-write:filesystem}
7
7
  ⚡{O(1):file-creation}
8
8
  """
@@ -10,52 +10,250 @@
10
10
  """
11
11
  VooDocs Init Command
12
12
 
13
- Initialize a new project with VooDocs configuration and example annotations.
13
+ Interactive wizard to initialize or upgrade a project with VooDocs.
14
+ Re-runnable to add missing features after upgrades.
14
15
  """
15
16
 
16
17
  import click
17
18
  import json
19
+ import os
20
+ import subprocess
18
21
  from pathlib import Path
22
+ from typing import Optional, Dict, Any
19
23
 
20
24
 
21
25
  @click.command()
22
26
  @click.option(
23
- '--format',
24
- type=click.Choice(['voodocs', 'darkarts'], case_sensitive=False),
25
- default='voodocs',
26
- help='Annotation format to use (voodocs or darkarts symbolic)'
27
+ '--non-interactive',
28
+ is_flag=True,
29
+ help='Skip interactive prompts and use defaults'
27
30
  )
28
31
  @click.option(
29
- '--config-only',
32
+ '--upgrade',
30
33
  is_flag=True,
31
- help='Only create configuration file, skip example files'
34
+ help='Upgrade existing configuration (add missing features)'
32
35
  )
33
- def init(format, config_only):
36
+ def init(non_interactive, upgrade):
34
37
  """
35
- Initialize a new project with VooDocs.
38
+ Initialize or upgrade a project with VooDocs.
39
+
40
+ This interactive wizard will:
41
+ - Set up your project configuration
42
+ - Auto-detect project details (version, repository, AI environment)
43
+ - Create AI instructions for your coding assistant
44
+ - Configure privacy settings (.gitignore)
45
+ - Generate example annotated files
36
46
 
37
- Creates a .voodocs.json configuration file and optionally generates
38
- example annotated files to help you get started.
47
+ Re-run after upgrading to add new features!
39
48
 
40
49
  Examples:
41
- voodocs init # Initialize with @voodocs format
42
- voodocs init --format darkarts # Initialize with symbolic @darkarts format
43
- voodocs init --config-only # Only create config file
50
+ voodocs init # Interactive wizard
51
+ voodocs init --upgrade # Upgrade existing config
52
+ voodocs init --non-interactive # Use all defaults
44
53
  """
54
+
45
55
  cwd = Path.cwd()
46
56
  config_path = cwd / '.voodocs.json'
47
57
 
48
- # Check if config already exists
49
- if config_path.exists():
50
- click.echo(click.style('⚠ .voodocs.json already exists', fg='yellow'))
51
- if not click.confirm('Overwrite existing configuration?'):
58
+ # Check if this is an upgrade or fresh install
59
+ existing_config = None
60
+ is_upgrade = config_path.exists()
61
+
62
+ if is_upgrade:
63
+ try:
64
+ with open(config_path) as f:
65
+ existing_config = json.load(f)
66
+ except:
67
+ existing_config = None
68
+
69
+ # Print welcome header
70
+ click.echo()
71
+ if is_upgrade:
72
+ click.echo(click.style('╔══════════════════════════════════════════╗', fg='yellow', bold=True))
73
+ click.echo(click.style('║ 🔄 VooDocs Upgrade Wizard ║', fg='yellow', bold=True))
74
+ click.echo(click.style('╚══════════════════════════════════════════╝', fg='yellow', bold=True))
75
+ click.echo()
76
+ click.echo(click.style('Existing configuration detected!', fg='yellow'))
77
+ click.echo('This wizard will help you add any missing features.')
78
+ else:
79
+ click.echo(click.style('╔══════════════════════════════════════════╗', fg='cyan', bold=True))
80
+ click.echo(click.style('║ 🎉 Welcome to VooDocs Setup Wizard ║', fg='cyan', bold=True))
81
+ click.echo(click.style('╚══════════════════════════════════════════╝', fg='cyan', bold=True))
82
+ click.echo()
83
+
84
+ # Detect what's already configured
85
+ has_ai_instructions = _check_ai_instructions(cwd)
86
+ has_examples = (cwd / 'voodocs-examples').exists()
87
+ has_gitignore_entry = _check_gitignore(cwd, '.voodocs.json')
88
+
89
+ if is_upgrade and not non_interactive:
90
+ click.echo(click.style('Current Setup:', fg='cyan', bold=True))
91
+ click.echo(f' Configuration: {_status_icon(True)} .voodocs.json exists')
92
+ click.echo(f' AI Instructions: {_status_icon(has_ai_instructions)} {_get_ai_file_status(cwd)}')
93
+ click.echo(f' Example Files: {_status_icon(has_examples)} voodocs-examples/')
94
+ click.echo(f' .gitignore: {_status_icon(has_gitignore_entry)} .voodocs.json entry')
95
+ click.echo()
96
+
97
+ if not click.confirm('Continue with upgrade?', default=True):
98
+ click.echo('Aborted.')
99
+ return
100
+ click.echo()
101
+
102
+ # Step 1: Project Information
103
+ click.echo(click.style('📋 Step 1: Project Information', fg='blue', bold=True))
104
+ click.echo()
105
+
106
+ # Auto-detect or use existing config
107
+ project_name = _get_value(existing_config, 'project.name', _detect_project_name(cwd))
108
+ project_version = _get_value(existing_config, 'project.version', _detect_version(cwd))
109
+ repository_url = _get_value(existing_config, 'project.repository', _detect_repository(cwd))
110
+ project_purpose = _get_value(existing_config, 'project.purpose', f'{project_name} project')
111
+
112
+ if non_interactive:
113
+ name = project_name
114
+ version = project_version
115
+ repository = repository_url
116
+ purpose = project_purpose
117
+ else:
118
+ if is_upgrade:
119
+ click.echo(f'Current values (press Enter to keep):')
120
+ name = click.prompt('Project name', default=project_name)
121
+ purpose = click.prompt('Project purpose', default=project_purpose)
122
+ version = click.prompt('Current version', default=project_version)
123
+ repository = click.prompt('Repository URL (leave empty to skip)', default=repository_url or '', show_default=False)
124
+
125
+ click.echo()
126
+
127
+ # Step 2: Annotation Format
128
+ click.echo(click.style('✨ Step 2: Annotation Format', fg='blue', bold=True))
129
+ click.echo()
130
+
131
+ # DarkArts symbolic format only
132
+ format_choice = 'darkarts'
133
+ click.echo('Using DarkArts symbolic format:')
134
+ click.echo(' ⊢{...} module_purpose ∂{...} dependencies ⚠{...} assumptions')
135
+ click.echo(' ⊳{...} preconditions ⊲{...} postconditions ⊨{...} invariants')
136
+ click.echo(' ⚡{...} complexity 🔒{...} security')
137
+ click.echo(click.style(' ✓', fg='green') + ' AI-native symbolic annotations')
138
+
139
+ click.echo()
140
+
141
+ # Step 3: AI Assistant Setup
142
+ click.echo(click.style('🤖 Step 3: AI Assistant Setup', fg='blue', bold=True))
143
+ click.echo()
144
+
145
+ detected_ai = _detect_ai_environment()
146
+
147
+ if has_ai_instructions and is_upgrade:
148
+ click.echo(f'{_status_icon(True)} AI instructions already configured')
149
+ if non_interactive:
150
+ setup_ai = False
151
+ ai_choice = None
152
+ else:
153
+ setup_ai = click.confirm('Update AI instructions?', default=False)
154
+ if setup_ai:
155
+ ai_choice = click.prompt(
156
+ 'Which AI assistant?',
157
+ type=click.Choice(['cursor', 'claude', 'default'], case_sensitive=False),
158
+ default=detected_ai or 'cursor'
159
+ )
160
+ else:
161
+ ai_choice = None
162
+ else:
163
+ if detected_ai:
164
+ click.echo(f'Detected AI environment: {click.style(detected_ai, fg="green", bold=True)}')
165
+ else:
166
+ click.echo('No AI environment detected')
167
+ click.echo()
168
+
169
+ if non_interactive:
170
+ setup_ai = detected_ai is not None
171
+ ai_choice = detected_ai or 'cursor'
172
+ else:
173
+ setup_ai = click.confirm('Generate AI instructions for your coding assistant?', default=True)
174
+ if setup_ai:
175
+ ai_choice = click.prompt(
176
+ 'Which AI assistant?',
177
+ type=click.Choice(['cursor', 'claude', 'default'], case_sensitive=False),
178
+ default=detected_ai or 'cursor'
179
+ )
180
+ else:
181
+ ai_choice = None
182
+
183
+ click.echo()
184
+
185
+ # Step 4: Privacy Settings
186
+ click.echo(click.style('🔒 Step 4: Privacy Settings', fg='blue', bold=True))
187
+ click.echo()
188
+
189
+ if has_gitignore_entry and is_upgrade:
190
+ click.echo(f'{_status_icon(True)} .gitignore already configured')
191
+ add_to_gitignore = False
192
+ else:
193
+ if non_interactive:
194
+ is_private = True
195
+ add_to_gitignore = True
196
+ else:
197
+ is_private = click.confirm('Is this a private project?', default=True)
198
+ add_to_gitignore = False
199
+ if is_private:
200
+ add_to_gitignore = click.confirm('Add .voodocs.json to .gitignore?', default=True)
201
+
202
+ click.echo()
203
+
204
+ # Step 5: Example Files
205
+ click.echo(click.style('📝 Step 5: Example Files', fg='blue', bold=True))
206
+ click.echo()
207
+
208
+ if has_examples and is_upgrade:
209
+ click.echo(f'{_status_icon(True)} Example files already exist')
210
+ create_examples = False
211
+ else:
212
+ if non_interactive:
213
+ create_examples = True
214
+ else:
215
+ create_examples = click.confirm('Create example annotated files?', default=not is_upgrade)
216
+
217
+ click.echo()
218
+
219
+ # Show configuration summary
220
+ click.echo(click.style('📊 Configuration Summary', fg='cyan', bold=True))
221
+ click.echo(click.style('─' * 50, fg='cyan'))
222
+ click.echo(f' Project Name: {click.style(name, fg="green")}')
223
+ click.echo(f' Purpose: {purpose}')
224
+ click.echo(f' Version: {version}')
225
+ if repository:
226
+ click.echo(f' Repository: {repository}')
227
+ click.echo(f' Format: {click.style(format_choice, fg="green", bold=True)}')
228
+ if setup_ai and ai_choice:
229
+ click.echo(f' AI Assistant: {click.style(ai_choice, fg="green")} {_status_icon(True, "new") if not has_ai_instructions else ""}')
230
+ if add_to_gitignore:
231
+ click.echo(f' .gitignore: {click.style("Yes", fg="green")} {_status_icon(True, "new")}')
232
+ if create_examples:
233
+ click.echo(f' Example Files: {click.style("Yes", fg="green")} {_status_icon(True, "new")}')
234
+ click.echo(click.style('─' * 50, fg='cyan'))
235
+ click.echo()
236
+
237
+ if not non_interactive:
238
+ action = 'upgrade' if is_upgrade else 'create'
239
+ if not click.confirm(f'Proceed to {action} configuration?', default=True):
52
240
  click.echo('Aborted.')
53
241
  return
242
+ click.echo()
243
+
244
+ # Create/update configuration
245
+ action_verb = 'Updating' if is_upgrade else 'Creating'
246
+ click.echo(click.style(f'⚙️ {action_verb} configuration...', fg='blue'))
54
247
 
55
- # Create default configuration
56
248
  config = {
57
249
  "version": "1.0",
58
- "format": format,
250
+ "project": {
251
+ "name": name,
252
+ "purpose": purpose,
253
+ "version": version,
254
+ "repository": repository if repository else None
255
+ },
256
+ "format": format_choice,
59
257
  "validation": {
60
258
  "semantic": True,
61
259
  "performance": True,
@@ -76,40 +274,252 @@ def init(format, config_only):
76
274
  ]
77
275
  }
78
276
 
277
+ # Merge with existing config if upgrading
278
+ if is_upgrade and existing_config:
279
+ # Preserve any custom fields
280
+ for key in existing_config:
281
+ if key not in config:
282
+ config[key] = existing_config[key]
283
+
79
284
  # Write configuration
80
285
  with open(config_path, 'w') as f:
81
286
  json.dump(config, f, indent=2)
82
287
 
83
- click.echo(click.style('✓ Created .voodocs.json', fg='green'))
288
+ verb = 'Updated' if is_upgrade else 'Created'
289
+ click.echo(click.style(f' ✓ {verb} .voodocs.json', fg='green'))
84
290
 
85
- if not config_only:
86
- # Create example files
291
+ # Add to .gitignore if requested
292
+ if add_to_gitignore:
293
+ _add_to_gitignore(cwd, '.voodocs.json')
294
+ click.echo(click.style(' ✓ Added to .gitignore', fg='green'))
295
+
296
+ # Generate AI instructions
297
+ if setup_ai and ai_choice:
298
+ click.echo(click.style(f' ⚙️ Generating {ai_choice} instructions...', fg='blue'))
299
+ _generate_ai_instructions(cwd, ai_choice, format_choice)
300
+ click.echo(click.style(f' ✓ Created AI instructions', fg='green'))
301
+
302
+ # Create example files
303
+ if create_examples:
304
+ click.echo(click.style(' ⚙️ Creating example files...', fg='blue'))
87
305
  examples_dir = cwd / 'voodocs-examples'
88
306
  examples_dir.mkdir(exist_ok=True)
89
307
 
90
- if format == 'voodocs':
91
- create_voodocs_example(examples_dir)
308
+ if format_choice == 'voodocs':
309
+ _create_voodocs_examples(examples_dir)
92
310
  else:
93
- create_darkarts_example(examples_dir)
311
+ _create_darkarts_examples(examples_dir)
94
312
 
95
- click.echo(click.style(f'✓ Created example files in {examples_dir}/', fg='green'))
313
+ click.echo(click.style(f' ✓ Created examples in {examples_dir}/', fg='green'))
96
314
 
315
+ # Success!
97
316
  click.echo()
98
- click.echo(click.style('🎉 VooDocs initialized successfully!', fg='green', bold=True))
317
+ if is_upgrade:
318
+ click.echo(click.style('╔══════════════════════════════════════════╗', fg='green', bold=True))
319
+ click.echo(click.style('║ ✅ VooDocs Upgrade Complete! ║', fg='green', bold=True))
320
+ click.echo(click.style('╚══════════════════════════════════════════╝', fg='green', bold=True))
321
+ else:
322
+ click.echo(click.style('╔══════════════════════════════════════════╗', fg='green', bold=True))
323
+ click.echo(click.style('║ 🎉 VooDocs Setup Complete! ║', fg='green', bold=True))
324
+ click.echo(click.style('╚══════════════════════════════════════════╝', fg='green', bold=True))
99
325
  click.echo()
100
- click.echo('Next steps:')
326
+ click.echo(click.style('Next steps:', fg='cyan', bold=True))
101
327
  click.echo(' 1. Review .voodocs.json configuration')
102
- click.echo(' 2. Add annotations to your code')
103
- click.echo(' 3. Run: voodocs validate .')
104
- click.echo(' 4. Run: voodocs generate . ./docs')
328
+ if setup_ai and ai_choice:
329
+ ai_file = '.cursorrules' if ai_choice == 'cursor' else f'{ai_choice}-instructions.md'
330
+ click.echo(f' 2. Your AI assistant will use {ai_file}')
331
+ click.echo(' 3. Add annotations to your code')
332
+ click.echo(' 4. Run: voodocs validate .')
333
+ click.echo(' 5. Run: voodocs generate . ./docs')
334
+ click.echo()
335
+ if is_upgrade:
336
+ click.echo(click.style('💡 Tip:', fg='yellow') + ' Run ' + click.style('voodocs init --upgrade', fg='cyan') + ' again after future updates!')
337
+ click.echo()
338
+ click.echo(f'Documentation: {click.style("https://voodocs.com/docs", fg="blue", underline=True)}')
105
339
  click.echo()
106
- click.echo('Documentation: https://voodocs.com/docs')
107
340
 
108
341
 
109
- def create_voodocs_example(examples_dir: Path):
342
+ def _status_icon(exists: bool, label: str = None) -> str:
343
+ """Return a status icon."""
344
+ if label == "new":
345
+ return click.style('🆕', fg='green')
346
+ return click.style('✓', fg='green') if exists else click.style('✗', fg='red')
347
+
348
+
349
+ def _get_value(config: Optional[Dict[str, Any]], path: str, default: Any) -> Any:
350
+ """Get a value from nested config dict using dot notation."""
351
+ if not config:
352
+ return default
353
+
354
+ keys = path.split('.')
355
+ value = config
356
+ for key in keys:
357
+ if isinstance(value, dict) and key in value:
358
+ value = value[key]
359
+ else:
360
+ return default
361
+
362
+ return value if value is not None else default
363
+
364
+
365
+ def _check_ai_instructions(cwd: Path) -> bool:
366
+ """Check if AI instructions exist."""
367
+ return (
368
+ (cwd / '.cursorrules').exists() or
369
+ (cwd / '.claude' / 'instructions.md').exists() or
370
+ (cwd / 'AI_INSTRUCTIONS.md').exists()
371
+ )
372
+
373
+
374
+ def _get_ai_file_status(cwd: Path) -> str:
375
+ """Get status of AI instruction files."""
376
+ files = []
377
+ if (cwd / '.cursorrules').exists():
378
+ files.append('.cursorrules')
379
+ if (cwd / '.claude' / 'instructions.md').exists():
380
+ files.append('.claude/instructions.md')
381
+ if (cwd / 'AI_INSTRUCTIONS.md').exists():
382
+ files.append('AI_INSTRUCTIONS.md')
383
+
384
+ return ', '.join(files) if files else 'none'
385
+
386
+
387
+ def _check_gitignore(cwd: Path, entry: str) -> bool:
388
+ """Check if entry exists in .gitignore."""
389
+ gitignore = cwd / '.gitignore'
390
+ if not gitignore.exists():
391
+ return False
392
+
393
+ content = gitignore.read_text()
394
+ return entry in content
395
+
396
+
397
+ def _detect_project_name(cwd: Path) -> str:
398
+ """Detect project name from directory or package.json."""
399
+ package_json = cwd / 'package.json'
400
+ if package_json.exists():
401
+ try:
402
+ with open(package_json) as f:
403
+ data = json.load(f)
404
+ if 'name' in data:
405
+ return data['name']
406
+ except:
407
+ pass
408
+
409
+ return cwd.name
410
+
411
+
412
+ def _detect_version(cwd: Path) -> str:
413
+ """Detect project version."""
414
+ package_json = cwd / 'package.json'
415
+ if package_json.exists():
416
+ try:
417
+ with open(package_json) as f:
418
+ data = json.load(f)
419
+ if 'version' in data:
420
+ return data['version']
421
+ except:
422
+ pass
423
+
424
+ pyproject = cwd / 'pyproject.toml'
425
+ if pyproject.exists():
426
+ try:
427
+ with open(pyproject) as f:
428
+ for line in f:
429
+ if line.startswith('version'):
430
+ return line.split('=')[1].strip().strip('"\'')
431
+ except:
432
+ pass
433
+
434
+ version_file = cwd / 'VERSION'
435
+ if version_file.exists():
436
+ try:
437
+ return version_file.read_text().strip()
438
+ except:
439
+ pass
440
+
441
+ return '1.0.0'
442
+
443
+
444
+ def _detect_repository(cwd: Path) -> Optional[str]:
445
+ """Detect repository URL from git config."""
446
+ try:
447
+ result = subprocess.run(
448
+ ['git', 'config', '--get', 'remote.origin.url'],
449
+ capture_output=True,
450
+ text=True,
451
+ cwd=cwd
452
+ )
453
+ if result.returncode == 0:
454
+ url = result.stdout.strip()
455
+ if url.startswith('git@github.com:'):
456
+ url = url.replace('git@github.com:', 'https://github.com/')
457
+ if url.endswith('.git'):
458
+ url = url[:-4]
459
+ return url
460
+ except:
461
+ pass
462
+
463
+ return None
464
+
465
+
466
+ def _detect_ai_environment() -> Optional[str]:
467
+ """Detect which AI environment is being used."""
468
+ if os.environ.get('CURSOR_USER') or Path('.cursorrules').exists():
469
+ return 'cursor'
470
+
471
+ if os.environ.get('CLAUDE_API_KEY') or Path('.claude').exists():
472
+ return 'claude'
473
+
474
+ return None
475
+
476
+
477
+ def _add_to_gitignore(cwd: Path, entry: str):
478
+ """Add an entry to .gitignore."""
479
+ gitignore = cwd / '.gitignore'
480
+
481
+ if gitignore.exists():
482
+ content = gitignore.read_text()
483
+ if entry in content:
484
+ return
485
+ else:
486
+ content = ''
487
+
488
+ if content and not content.endswith('\n'):
489
+ content += '\n'
490
+ content += f'\n# VooDocs\n{entry}\n'
491
+
492
+ gitignore.write_text(content)
493
+
494
+
495
+ def _generate_ai_instructions(cwd: Path, ai_type: str, format_type: str):
496
+ """Generate AI instructions file."""
497
+ cli_dir = Path(__file__).parent
498
+ template_path = cli_dir.parent / 'darkarts' / 'instructions' / f'{ai_type}.md'
499
+
500
+ if not template_path.exists():
501
+ template_path = cli_dir.parent / 'darkarts' / 'instructions' / 'default.md'
502
+
503
+ if not template_path.exists():
504
+ return
505
+
506
+ instructions = template_path.read_text()
507
+
508
+ if ai_type == 'cursor':
509
+ output_file = cwd / '.cursorrules'
510
+ elif ai_type == 'claude':
511
+ output_dir = cwd / '.claude'
512
+ output_dir.mkdir(exist_ok=True)
513
+ output_file = output_dir / 'instructions.md'
514
+ else:
515
+ output_file = cwd / 'AI_INSTRUCTIONS.md'
516
+
517
+ output_file.write_text(instructions)
518
+
519
+
520
+ def _create_voodocs_examples(examples_dir: Path):
110
521
  """Create example files with @voodocs annotations."""
111
522
 
112
- # TypeScript example
113
523
  ts_example = '''/**@voodocs
114
524
  module_purpose: "Example TypeScript module demonstrating @voodocs annotations"
115
525
  dependencies: []
@@ -137,41 +547,11 @@ export function greet(name: string, age: number): string {
137
547
  '''
138
548
 
139
549
  (examples_dir / 'example.ts').write_text(ts_example)
140
-
141
- # Python example
142
- py_example = '''"""@voodocs
143
- module_purpose: "Example Python module demonstrating @voodocs annotations"
144
- dependencies: []
145
- assumptions: ["Python 3.7+"]
146
- """
147
-
148
- def calculate_total(prices: list[float], tax_rate: float) -> float:
149
- """@voodocs
150
- preconditions: [
151
- "prices is a non-empty list of positive numbers",
152
- "tax_rate is between 0 and 1"
153
- ]
154
- postconditions: [
155
- "Returns the total with tax applied",
156
- "Result is greater than or equal to sum of prices"
157
- ]
158
- invariants: [
159
- "Does not modify the prices list"
160
- ]
161
- side_effects: []
162
- complexity: "O(n) where n is the length of prices"
163
- """
164
- subtotal = sum(prices)
165
- return subtotal * (1 + tax_rate)
166
- '''
167
-
168
- (examples_dir / 'example.py').write_text(py_example)
169
550
 
170
551
 
171
- def create_darkarts_example(examples_dir: Path):
552
+ def _create_darkarts_examples(examples_dir: Path):
172
553
  """Create example files with symbolic @darkarts annotations."""
173
554
 
174
- # TypeScript example
175
555
  ts_example = '''/**@darkarts
176
556
  ⊢{Example TypeScript module demonstrating symbolic @darkarts annotations}
177
557
  ∂{}
@@ -190,7 +570,7 @@ def create_darkarts_example(examples_dir: Path):
190
570
  ⊨{
191
571
  Does ¬ modify input parameters
192
572
  }
193
- ⚡{}
573
+ ⚡{O(1)}
194
574
  */
195
575
  export function greet(name: string, age: number): string {
196
576
  return `Hello, ${name}! You are ${age} years old.`;
@@ -198,31 +578,3 @@ export function greet(name: string, age: number): string {
198
578
  '''
199
579
 
200
580
  (examples_dir / 'example.ts').write_text(ts_example)
201
-
202
- # Python example
203
- py_example = '''"""@darkarts
204
- ⊢{Example Python module demonstrating symbolic @darkarts annotations}
205
- ∂{}
206
- ⚠{Python 3.7+}
207
- """
208
-
209
- def calculate_total(prices: list[float], tax_rate: float) -> float:
210
- """@darkarts
211
- ⊳{
212
- prices is a non-empty list ∧ ∀ p ∈ prices: p > 0
213
- tax_rate ∈ [0, 1]
214
- }
215
- ⊲{
216
- Returns the total with tax applied
217
- result ≥ sum(prices)
218
- }
219
- ⊨{
220
- Does ¬ modify the prices list
221
- }
222
- ⚡{O(n) where n = len(prices)}
223
- """
224
- subtotal = sum(prices)
225
- return subtotal * (1 + tax_rate)
226
- '''
227
-
228
- (examples_dir / 'example.py').write_text(py_example)
@@ -43,23 +43,18 @@ except ImportError:
43
43
 
44
44
 
45
45
  class AnnotationParser:
46
- """Parser for DarkArts annotations."""
46
+ """Parser for DarkArts symbolic annotations (@darkarts only)."""
47
47
 
48
- # Regex patterns for different languages
49
- PATTERNS = {
50
- Language.PYTHON: r'"""@voodocs\s*(.*?)\s*"""',
51
- }
52
-
53
- # DarkArts patterns (symbolic annotations) - support both @voodocs and @darkarts
48
+ # DarkArts patterns (symbolic annotations)
54
49
  DARKARTS_PATTERNS = {
55
- Language.PYTHON: r'"""@(?:darkarts|voodocs)\s*(.*?)\s*"""',
56
- Language.TYPESCRIPT: r'/\*\*@(?:darkarts|voodocs)\s*(.*?)\s*\*/',
57
- Language.JAVASCRIPT: r'/\*\*@(?:darkarts|voodocs)\s*(.*?)\s*\*/',
58
- Language.JAVA: r'/\*\*@(?:darkarts|voodocs)\s*(.*?)\s*\*/',
59
- Language.CPP: r'/\*\*@(?:darkarts|voodocs)\s*(.*?)\s*\*/',
60
- Language.CSHARP: r'/\*\*@(?:darkarts|voodocs)\s*(.*?)\s*\*/',
61
- Language.GO: r'/\*\*@(?:darkarts|voodocs)\s*(.*?)\s*\*/',
62
- Language.RUST: r'/\*\*@(?:darkarts|voodocs)\s*(.*?)\s*\*/',
50
+ Language.PYTHON: r'"""@darkarts\s*(.*?)\s*"""',
51
+ Language.TYPESCRIPT: r'/\*\*@darkarts\s*(.*?)\s*\*/',
52
+ Language.JAVASCRIPT: r'/\*\*@darkarts\s*(.*?)\s*\*/',
53
+ Language.JAVA: r'/\*\*@darkarts\s*(.*?)\s*\*/',
54
+ Language.CPP: r'/\*\*@darkarts\s*(.*?)\s*\*/',
55
+ Language.CSHARP: r'/\*\*@darkarts\s*(.*?)\s*\*/',
56
+ Language.GO: r'/\*\*@darkarts\s*(.*?)\s*\*/',
57
+ Language.RUST: r'/\*\*@darkarts\s*(.*?)\s*\*/',
63
58
  }
64
59
 
65
60
  def __init__(self):