@voodocs/cli 0.3.1 → 0.4.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/CHANGELOG.md +454 -0
- package/cli.py +32 -3
- package/lib/darkarts/annotations/DARKARTS_SYMBOLS.md +529 -0
- package/lib/darkarts/annotations/TRANSFORMATION_EXAMPLES.md +478 -0
- package/lib/darkarts/annotations/__init__.py +42 -0
- package/lib/darkarts/annotations/darkarts_parser.py +238 -0
- package/lib/darkarts/annotations/parser.py +186 -5
- package/lib/darkarts/annotations/symbols.py +244 -0
- package/lib/darkarts/annotations/translator.py +386 -0
- package/lib/darkarts/context/ai_instructions.py +8 -1
- package/lib/darkarts/context/ai_integrations.py +22 -1
- package/lib/darkarts/context/checker.py +291 -40
- package/lib/darkarts/context/commands.py +375 -267
- package/lib/darkarts/context/diagram.py +22 -1
- package/lib/darkarts/context/errors.py +164 -0
- package/lib/darkarts/context/models.py +23 -1
- package/lib/darkarts/context/module_utils.py +198 -0
- package/lib/darkarts/context/ui.py +337 -0
- package/lib/darkarts/context/validation.py +311 -0
- package/lib/darkarts/context/yaml_utils.py +130 -16
- package/lib/darkarts/exceptions.py +5 -0
- package/lib/darkarts/plugins/voodocs/instruction_generator.py +8 -1
- package/package.json +1 -1
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""@darkarts
|
|
2
|
+
⊢commands:ctx.cli
|
|
3
|
+
∂{yaml_utils,models,ai_integrations,checker,diagram}
|
|
4
|
+
⚠{.voodocs.context∈root,git:available,py≥3.11,cwd=root}
|
|
5
|
+
⊨{ctx:yaml,v∈semver,exit∈{0,1},∀update→v++,files:utf8}
|
|
6
|
+
🔒{read:ctx,write⊳confirm}
|
|
7
|
+
⚡{O(1)|most-cmds,O(n)|generate:n=src-files}
|
|
8
|
+
|
|
2
9
|
Context System Commands
|
|
3
10
|
|
|
4
11
|
Implements the command-line interface for the context system.
|
|
@@ -15,6 +22,26 @@ from .yaml_utils import (
|
|
|
15
22
|
add_to_gitignore,
|
|
16
23
|
format_context_as_markdown
|
|
17
24
|
)
|
|
25
|
+
from .errors import (
|
|
26
|
+
ContextFileNotFoundError,
|
|
27
|
+
InvalidContextError,
|
|
28
|
+
GitNotAvailableError,
|
|
29
|
+
UserInterruptError,
|
|
30
|
+
InvalidProjectNameError,
|
|
31
|
+
InvalidVersionError
|
|
32
|
+
)
|
|
33
|
+
from .validation import (
|
|
34
|
+
validate_project_name,
|
|
35
|
+
validate_version,
|
|
36
|
+
validate_path,
|
|
37
|
+
validate_context_structure
|
|
38
|
+
)
|
|
39
|
+
from .ui import (
|
|
40
|
+
success, error, warning, info, step, debug,
|
|
41
|
+
confirm, prompt, progress,
|
|
42
|
+
print_header, print_section, print_key_value
|
|
43
|
+
)
|
|
44
|
+
from .module_utils import extract_modules_from_results
|
|
18
45
|
|
|
19
46
|
|
|
20
47
|
def get_project_root() -> Path:
|
|
@@ -182,63 +209,76 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
182
209
|
Returns:
|
|
183
210
|
Exit code (0 for success, 1 for error)
|
|
184
211
|
"""
|
|
185
|
-
print("🎯 VooDocs Context Initialization")
|
|
186
|
-
print()
|
|
187
|
-
|
|
188
|
-
# Check if context file already exists
|
|
189
|
-
if context_file_exists() and not force:
|
|
190
|
-
print("❌ Error: .voodocs.context already exists in this directory.")
|
|
191
|
-
print(" Use --force to overwrite the existing file.")
|
|
192
|
-
return 1
|
|
193
|
-
|
|
194
|
-
# Detect project information
|
|
195
|
-
detected_version = detect_code_version()
|
|
196
|
-
detected_repo = detect_repository()
|
|
197
|
-
|
|
198
|
-
print("Let's set up your project context. This will create a .voodocs.context file.")
|
|
199
|
-
print()
|
|
200
|
-
|
|
201
|
-
# Prompt for project information
|
|
202
|
-
project_name = prompt_user("Project name")
|
|
203
|
-
|
|
204
|
-
project_purpose = prompt_user("Project purpose (one sentence)")
|
|
205
|
-
|
|
206
|
-
if detected_version:
|
|
207
|
-
code_version = prompt_user("Current code version", detected_version)
|
|
208
|
-
else:
|
|
209
|
-
code_version = prompt_user("Current code version", "1.0.0")
|
|
210
|
-
|
|
211
|
-
# Validate version format
|
|
212
|
-
if not code_version.count('.') >= 2:
|
|
213
|
-
print(f"⚠️ Warning: '{code_version}' doesn't look like semantic versioning (MAJOR.MINOR.PATCH)")
|
|
214
|
-
if not prompt_yes_no("Continue anyway?", default=False):
|
|
215
|
-
print("Aborted.")
|
|
216
|
-
return 1
|
|
217
|
-
|
|
218
|
-
if detected_repo:
|
|
219
|
-
repository = prompt_user("Repository URL (leave empty to skip)", detected_repo, required=False)
|
|
220
|
-
else:
|
|
221
|
-
repository = prompt_user("Repository URL (leave empty to skip)", "", required=False)
|
|
222
|
-
|
|
223
|
-
license_name = prompt_user("License (leave empty to skip)", "", required=False)
|
|
224
|
-
|
|
225
|
-
print()
|
|
226
|
-
print("📝 Configuration Summary:")
|
|
227
|
-
print(f" Project Name: {project_name}")
|
|
228
|
-
print(f" Purpose: {project_purpose}")
|
|
229
|
-
print(f" Code Version: {code_version}")
|
|
230
|
-
if repository:
|
|
231
|
-
print(f" Repository: {repository}")
|
|
232
|
-
if license_name:
|
|
233
|
-
print(f" License: {license_name}")
|
|
234
|
-
print()
|
|
235
|
-
|
|
236
|
-
if not prompt_yes_no("Create context file?", default=True):
|
|
237
|
-
print("Aborted.")
|
|
238
|
-
return 1
|
|
239
|
-
|
|
240
|
-
# Create context file
|
|
241
212
|
try:
|
|
213
|
+
print_header("VooDocs Context Initialization")
|
|
214
|
+
|
|
215
|
+
# Check if context file already exists
|
|
216
|
+
context_path = get_context_file_path()
|
|
217
|
+
if context_file_exists() and not force:
|
|
218
|
+
error(f"Context file already exists: {context_path}")
|
|
219
|
+
info("Use --force to overwrite, or 'voodocs context update' to modify")
|
|
220
|
+
return 1
|
|
221
|
+
|
|
222
|
+
# Warn if overwriting
|
|
223
|
+
if context_file_exists() and force:
|
|
224
|
+
if not confirm(f"Overwrite existing context file at {context_path}?", default=False):
|
|
225
|
+
raise UserInterruptError("Initialization")
|
|
226
|
+
|
|
227
|
+
step("Initializing VooDocs context...")
|
|
228
|
+
print()
|
|
229
|
+
|
|
230
|
+
# Detect project information
|
|
231
|
+
detected_version = detect_code_version()
|
|
232
|
+
detected_repo = detect_repository()
|
|
233
|
+
|
|
234
|
+
info("Let's set up your project context. This will create a .voodocs.context file.")
|
|
235
|
+
print()
|
|
236
|
+
|
|
237
|
+
# Collect and validate user input
|
|
238
|
+
try:
|
|
239
|
+
# Project name with validation
|
|
240
|
+
default_name = get_project_root().name
|
|
241
|
+
name_input = prompt("Project name", default=default_name)
|
|
242
|
+
project_name = validate_project_name(name_input)
|
|
243
|
+
|
|
244
|
+
# Project purpose
|
|
245
|
+
project_purpose = prompt("Project purpose (one sentence)", required=True)
|
|
246
|
+
|
|
247
|
+
# Code version with validation
|
|
248
|
+
version_default = detected_version or "1.0.0"
|
|
249
|
+
version_input = prompt("Current code version", default=version_default)
|
|
250
|
+
code_version = validate_version(version_input) or "1.0.0"
|
|
251
|
+
|
|
252
|
+
# Repository (optional)
|
|
253
|
+
repository = prompt(
|
|
254
|
+
"Repository URL (leave empty to skip)",
|
|
255
|
+
default=detected_repo or "",
|
|
256
|
+
required=False
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# License (optional)
|
|
260
|
+
license_name = prompt("License (leave empty to skip)", default="", required=False)
|
|
261
|
+
|
|
262
|
+
except KeyboardInterrupt:
|
|
263
|
+
raise UserInterruptError("Initialization")
|
|
264
|
+
|
|
265
|
+
# Show configuration summary
|
|
266
|
+
print()
|
|
267
|
+
print_section("Configuration Summary")
|
|
268
|
+
print_key_value("Project Name", project_name)
|
|
269
|
+
print_key_value("Purpose", project_purpose)
|
|
270
|
+
print_key_value("Code Version", code_version)
|
|
271
|
+
if repository:
|
|
272
|
+
print_key_value("Repository", repository)
|
|
273
|
+
if license_name:
|
|
274
|
+
print_key_value("License", license_name)
|
|
275
|
+
print()
|
|
276
|
+
|
|
277
|
+
if not confirm("Create context file?", default=True):
|
|
278
|
+
raise UserInterruptError("Initialization")
|
|
279
|
+
|
|
280
|
+
# Create context file
|
|
281
|
+
step("Creating context structure...")
|
|
242
282
|
context = create_minimal_context(
|
|
243
283
|
project_name=project_name,
|
|
244
284
|
project_purpose=project_purpose,
|
|
@@ -247,20 +287,20 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
247
287
|
license=license_name if license_name else None
|
|
248
288
|
)
|
|
249
289
|
|
|
250
|
-
|
|
290
|
+
step(f"Writing context file to {context_path}...")
|
|
251
291
|
write_context_yaml(context, context_path)
|
|
252
292
|
|
|
253
293
|
print()
|
|
254
|
-
|
|
255
|
-
|
|
294
|
+
success("Context file created successfully!")
|
|
295
|
+
info(f"Context version: {context.versioning.context_version}")
|
|
256
296
|
print()
|
|
257
297
|
|
|
258
298
|
# Ask about .gitignore
|
|
259
|
-
if
|
|
299
|
+
if confirm("Add .voodocs.context to .gitignore? (recommended for private projects)", default=True):
|
|
260
300
|
if add_to_gitignore(get_project_root()):
|
|
261
|
-
|
|
301
|
+
success("Added to .gitignore")
|
|
262
302
|
else:
|
|
263
|
-
|
|
303
|
+
info("Already in .gitignore")
|
|
264
304
|
|
|
265
305
|
print()
|
|
266
306
|
|
|
@@ -344,9 +384,9 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
344
384
|
print("ℹ️ Skipped AI integration")
|
|
345
385
|
|
|
346
386
|
print()
|
|
347
|
-
|
|
387
|
+
success("🎉 Context initialized successfully!")
|
|
348
388
|
print()
|
|
349
|
-
|
|
389
|
+
info("Next steps:")
|
|
350
390
|
print(" 1. Review the context file: voodocs context view")
|
|
351
391
|
print(" 2. Auto-generate from code: voodocs context generate --update")
|
|
352
392
|
print(" 3. Check invariants: voodocs context check")
|
|
@@ -356,8 +396,18 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
356
396
|
|
|
357
397
|
return 0
|
|
358
398
|
|
|
399
|
+
except UserInterruptError as e:
|
|
400
|
+
print() # New line after interrupt
|
|
401
|
+
error(str(e))
|
|
402
|
+
return 1
|
|
403
|
+
except (InvalidProjectNameError, InvalidVersionError) as e:
|
|
404
|
+
print()
|
|
405
|
+
error(str(e))
|
|
406
|
+
return 1
|
|
359
407
|
except Exception as e:
|
|
360
|
-
print(
|
|
408
|
+
print()
|
|
409
|
+
error(f"Failed to initialize context: {e}")
|
|
410
|
+
warning("Run with --debug for full stack trace")
|
|
361
411
|
return 1
|
|
362
412
|
|
|
363
413
|
|
|
@@ -371,19 +421,17 @@ def cmd_context_view(output_file: Optional[str] = None) -> int:
|
|
|
371
421
|
Returns:
|
|
372
422
|
Exit code (0 for success, 1 for error)
|
|
373
423
|
"""
|
|
374
|
-
if not context_file_exists():
|
|
375
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
376
|
-
print(" Run 'voodocs context init' to create one.")
|
|
377
|
-
return 1
|
|
378
|
-
|
|
379
424
|
try:
|
|
380
|
-
#
|
|
425
|
+
# Check if context file exists
|
|
381
426
|
context_path = get_context_file_path()
|
|
427
|
+
if not context_file_exists():
|
|
428
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
429
|
+
|
|
430
|
+
# Read and parse context file
|
|
382
431
|
data = read_context_yaml(context_path)
|
|
383
432
|
|
|
384
433
|
if not data:
|
|
385
|
-
|
|
386
|
-
return 1
|
|
434
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
387
435
|
|
|
388
436
|
# Parse and format as Markdown
|
|
389
437
|
from .yaml_utils import parse_context_file
|
|
@@ -393,17 +441,25 @@ def cmd_context_view(output_file: Optional[str] = None) -> int:
|
|
|
393
441
|
if output_file:
|
|
394
442
|
# Write to file
|
|
395
443
|
output_path = Path(output_file)
|
|
444
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
396
445
|
with open(output_path, 'w', encoding='utf-8') as f:
|
|
397
446
|
f.write(markdown)
|
|
398
|
-
|
|
447
|
+
success(f"Context exported to {output_file}")
|
|
399
448
|
return 0
|
|
400
449
|
else:
|
|
401
450
|
# Print to console
|
|
402
451
|
print(markdown)
|
|
403
452
|
return 0
|
|
404
453
|
|
|
454
|
+
except ContextFileNotFoundError as e:
|
|
455
|
+
error(str(e))
|
|
456
|
+
return 1
|
|
457
|
+
except InvalidContextError as e:
|
|
458
|
+
error(str(e))
|
|
459
|
+
return 1
|
|
405
460
|
except Exception as e:
|
|
406
|
-
|
|
461
|
+
error(f"Failed to view context: {e}")
|
|
462
|
+
warning("Run with --debug for full stack trace")
|
|
407
463
|
return 1
|
|
408
464
|
|
|
409
465
|
|
|
@@ -414,19 +470,17 @@ def cmd_context_status() -> int:
|
|
|
414
470
|
Returns:
|
|
415
471
|
Exit code (0 for success, 1 for error)
|
|
416
472
|
"""
|
|
417
|
-
if not context_file_exists():
|
|
418
|
-
print("❌ No context file found")
|
|
419
|
-
print(" Run 'voodocs context init' to create one.")
|
|
420
|
-
return 1
|
|
421
|
-
|
|
422
473
|
try:
|
|
423
|
-
#
|
|
474
|
+
# Check if context file exists
|
|
424
475
|
context_path = get_context_file_path()
|
|
476
|
+
if not context_file_exists():
|
|
477
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
478
|
+
|
|
479
|
+
# Read context file
|
|
425
480
|
data = read_context_yaml(context_path)
|
|
426
481
|
|
|
427
482
|
if not data:
|
|
428
|
-
|
|
429
|
-
return 1
|
|
483
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
430
484
|
|
|
431
485
|
# Extract key information
|
|
432
486
|
versioning = data.get('versioning', {})
|
|
@@ -455,12 +509,19 @@ def cmd_context_status() -> int:
|
|
|
455
509
|
print(f"Known Issues: {len(known_issues)}")
|
|
456
510
|
|
|
457
511
|
print()
|
|
458
|
-
|
|
512
|
+
info("Use 'voodocs context view' to see the full context.")
|
|
459
513
|
|
|
460
514
|
return 0
|
|
461
515
|
|
|
516
|
+
except ContextFileNotFoundError as e:
|
|
517
|
+
error(str(e))
|
|
518
|
+
return 1
|
|
519
|
+
except InvalidContextError as e:
|
|
520
|
+
error(str(e))
|
|
521
|
+
return 1
|
|
462
522
|
except Exception as e:
|
|
463
|
-
|
|
523
|
+
error(f"Failed to read context status: {e}")
|
|
524
|
+
warning("Run with --debug for full stack trace")
|
|
464
525
|
return 1
|
|
465
526
|
|
|
466
527
|
|
|
@@ -474,22 +535,20 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
474
535
|
Returns:
|
|
475
536
|
Exit code (0 for success, 1 for error)
|
|
476
537
|
"""
|
|
477
|
-
if not context_file_exists():
|
|
478
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
479
|
-
print(" Run 'voodocs context init' to create one.")
|
|
480
|
-
return 1
|
|
481
|
-
|
|
482
538
|
try:
|
|
539
|
+
# Check if context file exists
|
|
540
|
+
context_path = get_context_file_path()
|
|
541
|
+
if not context_file_exists():
|
|
542
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
543
|
+
|
|
483
544
|
from datetime import date
|
|
484
545
|
from .models import Change
|
|
485
546
|
|
|
486
547
|
# Read current context
|
|
487
|
-
context_path = get_context_file_path()
|
|
488
548
|
data = read_context_yaml(context_path)
|
|
489
549
|
|
|
490
550
|
if not data:
|
|
491
|
-
|
|
492
|
-
return 1
|
|
551
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
493
552
|
|
|
494
553
|
# Get current versions
|
|
495
554
|
versioning = data.get('versioning', {})
|
|
@@ -499,14 +558,13 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
499
558
|
# Parse context version
|
|
500
559
|
parts = current_context_version.split('.')
|
|
501
560
|
if len(parts) != 2:
|
|
502
|
-
|
|
503
|
-
return 1
|
|
561
|
+
raise ValueError(f"Invalid context version format: {current_context_version}. Expected MAJOR.MINOR format.")
|
|
504
562
|
|
|
505
563
|
major, minor = parts
|
|
506
564
|
new_minor = int(minor) + 1
|
|
507
565
|
new_context_version = f"{major}.{new_minor}"
|
|
508
566
|
|
|
509
|
-
|
|
567
|
+
step("Updating context...")
|
|
510
568
|
print()
|
|
511
569
|
print(f"Current version: {current_context_version}")
|
|
512
570
|
print(f"New version: {new_context_version}")
|
|
@@ -514,9 +572,10 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
514
572
|
|
|
515
573
|
# Prompt for description if not provided
|
|
516
574
|
if not description:
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
575
|
+
try:
|
|
576
|
+
description = prompt("Change description", default="Context updated", required=False)
|
|
577
|
+
except KeyboardInterrupt:
|
|
578
|
+
raise UserInterruptError("Context update")
|
|
520
579
|
|
|
521
580
|
# Update versioning
|
|
522
581
|
today = date.today().isoformat()
|
|
@@ -547,20 +606,34 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
547
606
|
data['changes'].append(change_entry)
|
|
548
607
|
|
|
549
608
|
# Write updated context
|
|
609
|
+
step("Writing updated context...")
|
|
550
610
|
from .yaml_utils import parse_context_file
|
|
551
611
|
context = parse_context_file(data)
|
|
552
612
|
write_context_yaml(context, context_path)
|
|
553
613
|
|
|
554
|
-
print(
|
|
555
|
-
|
|
614
|
+
print()
|
|
615
|
+
success(f"Context updated to version {new_context_version}")
|
|
616
|
+
info(f"Description: {description}")
|
|
556
617
|
print()
|
|
557
618
|
|
|
558
619
|
return 0
|
|
559
620
|
|
|
621
|
+
except ContextFileNotFoundError as e:
|
|
622
|
+
error(str(e))
|
|
623
|
+
return 1
|
|
624
|
+
except InvalidContextError as e:
|
|
625
|
+
error(str(e))
|
|
626
|
+
return 1
|
|
627
|
+
except UserInterruptError as e:
|
|
628
|
+
print()
|
|
629
|
+
error(str(e))
|
|
630
|
+
return 1
|
|
631
|
+
except ValueError as e:
|
|
632
|
+
error(str(e))
|
|
633
|
+
return 1
|
|
560
634
|
except Exception as e:
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
traceback.print_exc()
|
|
635
|
+
error(f"Failed to update context: {e}")
|
|
636
|
+
warning("Run with --debug for full stack trace")
|
|
564
637
|
return 1
|
|
565
638
|
|
|
566
639
|
|
|
@@ -575,21 +648,19 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
575
648
|
Returns:
|
|
576
649
|
Exit code (0 for success, 1 for error)
|
|
577
650
|
"""
|
|
578
|
-
if not context_file_exists():
|
|
579
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
580
|
-
print(" Run 'voodocs context init' to create one.")
|
|
581
|
-
return 1
|
|
582
|
-
|
|
583
651
|
try:
|
|
652
|
+
# Check if context file exists
|
|
653
|
+
context_path = get_context_file_path()
|
|
654
|
+
if not context_file_exists():
|
|
655
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
656
|
+
|
|
584
657
|
from datetime import date
|
|
585
658
|
|
|
586
659
|
# Read current context
|
|
587
|
-
context_path = get_context_file_path()
|
|
588
660
|
data = read_context_yaml(context_path)
|
|
589
661
|
|
|
590
662
|
if not data:
|
|
591
|
-
|
|
592
|
-
return 1
|
|
663
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
593
664
|
|
|
594
665
|
# Get current versions
|
|
595
666
|
versioning = data.get('versioning', {})
|
|
@@ -599,30 +670,33 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
599
670
|
# If no code version provided, try to detect it
|
|
600
671
|
if not code_version:
|
|
601
672
|
detected_version = detect_code_version()
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
673
|
+
try:
|
|
674
|
+
if detected_version:
|
|
675
|
+
code_version = prompt(
|
|
676
|
+
"New code version",
|
|
677
|
+
default=detected_version,
|
|
678
|
+
required=True
|
|
679
|
+
)
|
|
680
|
+
else:
|
|
681
|
+
code_version = prompt(
|
|
682
|
+
"New code version",
|
|
683
|
+
required=True
|
|
684
|
+
)
|
|
685
|
+
except KeyboardInterrupt:
|
|
686
|
+
raise UserInterruptError("Context sync")
|
|
613
687
|
|
|
614
688
|
# Validate version format
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
689
|
+
try:
|
|
690
|
+
code_version = validate_version(code_version, allow_empty=False)
|
|
691
|
+
except InvalidVersionError as e:
|
|
692
|
+
raise InvalidVersionError(code_version)
|
|
619
693
|
|
|
620
694
|
# Extract major versions
|
|
621
695
|
current_major = current_code_version.split('.')[0]
|
|
622
696
|
new_major = code_version.split('.')[0]
|
|
623
697
|
context_major = current_context_version.split('.')[0]
|
|
624
698
|
|
|
625
|
-
|
|
699
|
+
step("Syncing context with code version...")
|
|
626
700
|
print()
|
|
627
701
|
print(f"Current code version: {current_code_version}")
|
|
628
702
|
print(f"New code version: {code_version}")
|
|
@@ -639,9 +713,8 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
639
713
|
print()
|
|
640
714
|
|
|
641
715
|
if not force:
|
|
642
|
-
if not
|
|
643
|
-
|
|
644
|
-
return 1
|
|
716
|
+
if not confirm("Continue with reset?", default=True):
|
|
717
|
+
raise UserInterruptError("Context sync")
|
|
645
718
|
|
|
646
719
|
# Update versions
|
|
647
720
|
today = date.today().isoformat()
|
|
@@ -677,8 +750,8 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
677
750
|
context = parse_context_file(data)
|
|
678
751
|
write_context_yaml(context, context_path)
|
|
679
752
|
|
|
680
|
-
|
|
681
|
-
|
|
753
|
+
success(f"Context version reset to {new_context_version}")
|
|
754
|
+
info(f"Code version updated to {code_version}")
|
|
682
755
|
print()
|
|
683
756
|
|
|
684
757
|
elif context_major != new_major:
|
|
@@ -722,8 +795,8 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
722
795
|
context = parse_context_file(data)
|
|
723
796
|
write_context_yaml(context, context_path)
|
|
724
797
|
|
|
725
|
-
|
|
726
|
-
|
|
798
|
+
success(f"Context synced to {new_context_version}")
|
|
799
|
+
info(f"Code version updated to {code_version}")
|
|
727
800
|
print()
|
|
728
801
|
|
|
729
802
|
else:
|
|
@@ -736,16 +809,28 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
736
809
|
context = parse_context_file(data)
|
|
737
810
|
write_context_yaml(context, context_path)
|
|
738
811
|
|
|
739
|
-
|
|
740
|
-
|
|
812
|
+
success(f"Code version updated to {code_version}")
|
|
813
|
+
info(f"Context version unchanged: {current_context_version}")
|
|
741
814
|
print()
|
|
742
815
|
|
|
743
816
|
return 0
|
|
744
817
|
|
|
818
|
+
except ContextFileNotFoundError as e:
|
|
819
|
+
error(str(e))
|
|
820
|
+
return 1
|
|
821
|
+
except InvalidContextError as e:
|
|
822
|
+
error(str(e))
|
|
823
|
+
return 1
|
|
824
|
+
except InvalidVersionError as e:
|
|
825
|
+
error(str(e))
|
|
826
|
+
return 1
|
|
827
|
+
except UserInterruptError as e:
|
|
828
|
+
print()
|
|
829
|
+
error(str(e))
|
|
830
|
+
return 1
|
|
745
831
|
except Exception as e:
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
traceback.print_exc()
|
|
832
|
+
error(f"Failed to sync context: {e}")
|
|
833
|
+
warning("Run with --debug for full stack trace")
|
|
749
834
|
return 1
|
|
750
835
|
|
|
751
836
|
|
|
@@ -756,26 +841,24 @@ def cmd_context_history() -> int:
|
|
|
756
841
|
Returns:
|
|
757
842
|
Exit code (0 for success, 1 for error)
|
|
758
843
|
"""
|
|
759
|
-
if not context_file_exists():
|
|
760
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
761
|
-
print(" Run 'voodocs context init' to create one.")
|
|
762
|
-
return 1
|
|
763
|
-
|
|
764
844
|
try:
|
|
765
|
-
#
|
|
845
|
+
# Check if context file exists
|
|
766
846
|
context_path = get_context_file_path()
|
|
847
|
+
if not context_file_exists():
|
|
848
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
849
|
+
|
|
850
|
+
# Read current context
|
|
767
851
|
data = read_context_yaml(context_path)
|
|
768
852
|
|
|
769
853
|
if not data:
|
|
770
|
-
|
|
771
|
-
return 1
|
|
854
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
772
855
|
|
|
773
856
|
# Get changes
|
|
774
857
|
changes = data.get('changes', [])
|
|
775
858
|
|
|
776
859
|
if not changes:
|
|
777
|
-
|
|
778
|
-
|
|
860
|
+
warning("No change history found.")
|
|
861
|
+
info("Changes will be recorded when you run 'voodocs context update'.")
|
|
779
862
|
return 0
|
|
780
863
|
|
|
781
864
|
print("📜 Context Version History")
|
|
@@ -806,8 +889,15 @@ def cmd_context_history() -> int:
|
|
|
806
889
|
|
|
807
890
|
return 0
|
|
808
891
|
|
|
892
|
+
except ContextFileNotFoundError as e:
|
|
893
|
+
error(str(e))
|
|
894
|
+
return 1
|
|
895
|
+
except InvalidContextError as e:
|
|
896
|
+
error(str(e))
|
|
897
|
+
return 1
|
|
809
898
|
except Exception as e:
|
|
810
|
-
|
|
899
|
+
error(f"Failed to read history: {e}")
|
|
900
|
+
warning("Run with --debug for full stack trace")
|
|
811
901
|
return 1
|
|
812
902
|
|
|
813
903
|
|
|
@@ -822,19 +912,17 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
822
912
|
Returns:
|
|
823
913
|
Exit code (0 for success, 1 for error)
|
|
824
914
|
"""
|
|
825
|
-
if not context_file_exists():
|
|
826
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
827
|
-
print(" Run 'voodocs context init' to create one.")
|
|
828
|
-
return 1
|
|
829
|
-
|
|
830
915
|
try:
|
|
831
|
-
#
|
|
916
|
+
# Check if context file exists
|
|
832
917
|
context_path = get_context_file_path()
|
|
918
|
+
if not context_file_exists():
|
|
919
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
920
|
+
|
|
921
|
+
# Read current context
|
|
833
922
|
data = read_context_yaml(context_path)
|
|
834
923
|
|
|
835
924
|
if not data:
|
|
836
|
-
|
|
837
|
-
return 1
|
|
925
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
838
926
|
|
|
839
927
|
# Get current version
|
|
840
928
|
versioning = data.get('versioning', {})
|
|
@@ -848,8 +936,8 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
848
936
|
if not version1:
|
|
849
937
|
changes = data.get('changes', [])
|
|
850
938
|
if len(changes) < 2:
|
|
851
|
-
|
|
852
|
-
|
|
939
|
+
warning("Not enough version history to compare.")
|
|
940
|
+
info("Need at least 2 versions. Run 'voodocs context history' to see available versions.")
|
|
853
941
|
return 0
|
|
854
942
|
|
|
855
943
|
# Get the two most recent versions
|
|
@@ -881,7 +969,7 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
881
969
|
continue
|
|
882
970
|
|
|
883
971
|
if not relevant_changes:
|
|
884
|
-
|
|
972
|
+
info(f"No changes found between v{version1} and v{version2}")
|
|
885
973
|
return 0
|
|
886
974
|
|
|
887
975
|
print(f"Changes ({len(relevant_changes)}):")
|
|
@@ -900,14 +988,19 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
900
988
|
print(f" Code: v{code_version}")
|
|
901
989
|
print()
|
|
902
990
|
|
|
903
|
-
|
|
991
|
+
info("Tip: Use 'voodocs context history' to see the full timeline.")
|
|
904
992
|
|
|
905
993
|
return 0
|
|
906
994
|
|
|
995
|
+
except ContextFileNotFoundError as e:
|
|
996
|
+
error(str(e))
|
|
997
|
+
return 1
|
|
998
|
+
except InvalidContextError as e:
|
|
999
|
+
error(str(e))
|
|
1000
|
+
return 1
|
|
907
1001
|
except Exception as e:
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
traceback.print_exc()
|
|
1002
|
+
error(f"Failed to compare versions: {e}")
|
|
1003
|
+
warning("Run with --debug for full stack trace")
|
|
911
1004
|
return 1
|
|
912
1005
|
|
|
913
1006
|
|
|
@@ -922,21 +1015,19 @@ def cmd_context_validate(check_version: bool = False, check_invariants: bool = F
|
|
|
922
1015
|
Returns:
|
|
923
1016
|
Exit code (0 for success, 1 for warnings, 2 for errors)
|
|
924
1017
|
"""
|
|
925
|
-
if not context_file_exists():
|
|
926
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
927
|
-
print(" Run 'voodocs context init' to create one.")
|
|
928
|
-
return 2
|
|
929
|
-
|
|
930
1018
|
try:
|
|
931
|
-
#
|
|
1019
|
+
# Check if context file exists
|
|
932
1020
|
context_path = get_context_file_path()
|
|
1021
|
+
if not context_file_exists():
|
|
1022
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
1023
|
+
|
|
1024
|
+
# Read current context
|
|
933
1025
|
data = read_context_yaml(context_path)
|
|
934
1026
|
|
|
935
1027
|
if not data:
|
|
936
|
-
|
|
937
|
-
return 2
|
|
1028
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
938
1029
|
|
|
939
|
-
|
|
1030
|
+
step("Validating context file...")
|
|
940
1031
|
print()
|
|
941
1032
|
|
|
942
1033
|
errors = []
|
|
@@ -1056,7 +1147,7 @@ def cmd_context_validate(check_version: bool = False, check_invariants: bool = F
|
|
|
1056
1147
|
break
|
|
1057
1148
|
|
|
1058
1149
|
if not errors and not warnings:
|
|
1059
|
-
|
|
1150
|
+
success("Context file is valid!")
|
|
1060
1151
|
print()
|
|
1061
1152
|
|
|
1062
1153
|
# Show summary
|
|
@@ -1069,23 +1160,28 @@ def cmd_context_validate(check_version: bool = False, check_invariants: bool = F
|
|
|
1069
1160
|
|
|
1070
1161
|
# Show suggestions if any
|
|
1071
1162
|
if suggestions:
|
|
1072
|
-
|
|
1163
|
+
info("Suggestions:")
|
|
1073
1164
|
for suggestion in suggestions[:5]: # Limit to 5
|
|
1074
1165
|
print(f" • {suggestion}")
|
|
1075
1166
|
print()
|
|
1076
1167
|
|
|
1077
1168
|
return 0
|
|
1078
1169
|
elif errors:
|
|
1079
|
-
|
|
1170
|
+
error(f"Validation failed with {len(errors)} error(s) and {len(warnings)} warning(s)")
|
|
1080
1171
|
return 2
|
|
1081
1172
|
else:
|
|
1082
|
-
|
|
1173
|
+
warning(f"Validation passed with {len(warnings)} warning(s)")
|
|
1083
1174
|
return 1
|
|
1084
1175
|
|
|
1176
|
+
except ContextFileNotFoundError as e:
|
|
1177
|
+
error(str(e))
|
|
1178
|
+
return 2
|
|
1179
|
+
except InvalidContextError as e:
|
|
1180
|
+
error(str(e))
|
|
1181
|
+
return 2
|
|
1085
1182
|
except Exception as e:
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
traceback.print_exc()
|
|
1183
|
+
error(f"Failed to validate context: {e}")
|
|
1184
|
+
warning("Run with --debug for full stack trace")
|
|
1089
1185
|
return 2
|
|
1090
1186
|
|
|
1091
1187
|
|
|
@@ -1103,20 +1199,16 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1103
1199
|
from pathlib import Path
|
|
1104
1200
|
import sys
|
|
1105
1201
|
|
|
1106
|
-
# Determine source directory
|
|
1107
|
-
if source_dir:
|
|
1108
|
-
scan_path = Path(source_dir).resolve()
|
|
1109
|
-
else:
|
|
1110
|
-
scan_path = Path.cwd()
|
|
1111
|
-
|
|
1112
|
-
if not scan_path.exists():
|
|
1113
|
-
print(f"❌ Error: Directory not found: {scan_path}")
|
|
1114
|
-
return 1
|
|
1115
|
-
|
|
1116
|
-
print(f"🔍 Scanning for @voodocs annotations in: {scan_path}")
|
|
1117
|
-
print()
|
|
1118
|
-
|
|
1119
1202
|
try:
|
|
1203
|
+
# Determine source directory
|
|
1204
|
+
if source_dir:
|
|
1205
|
+
scan_path = validate_path(source_dir, must_exist=True, must_be_dir=True)
|
|
1206
|
+
else:
|
|
1207
|
+
scan_path = Path.cwd()
|
|
1208
|
+
|
|
1209
|
+
step(f"Scanning for @voodocs annotations in: {scan_path}")
|
|
1210
|
+
print()
|
|
1211
|
+
|
|
1120
1212
|
# Import the annotation parser
|
|
1121
1213
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
1122
1214
|
from annotations.parser import AnnotationParser
|
|
@@ -1126,14 +1218,16 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1126
1218
|
results = parser.parse_directory(scan_path)
|
|
1127
1219
|
|
|
1128
1220
|
if not results:
|
|
1129
|
-
|
|
1130
|
-
|
|
1221
|
+
warning("No @voodocs annotations found.")
|
|
1222
|
+
info("Add @voodocs annotations to your code first.")
|
|
1131
1223
|
return 1
|
|
1132
1224
|
|
|
1133
1225
|
# Extract global invariants from all annotations
|
|
1134
1226
|
global_invariants = set()
|
|
1135
1227
|
assumptions = []
|
|
1136
|
-
|
|
1228
|
+
|
|
1229
|
+
# Extract modules using helper function
|
|
1230
|
+
modules_info = extract_modules_from_results(results, scan_path)
|
|
1137
1231
|
|
|
1138
1232
|
for parsed in results:
|
|
1139
1233
|
# Get module annotation
|
|
@@ -1152,14 +1246,6 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1152
1246
|
'source': Path(parsed.source_file).relative_to(scan_path).as_posix()
|
|
1153
1247
|
})
|
|
1154
1248
|
|
|
1155
|
-
# Store module info
|
|
1156
|
-
if hasattr(module_ann, 'module_purpose') and module_ann.module_purpose:
|
|
1157
|
-
rel_path = Path(parsed.source_file).relative_to(scan_path).as_posix()
|
|
1158
|
-
modules_info[rel_path] = {
|
|
1159
|
-
'purpose': module_ann.module_purpose,
|
|
1160
|
-
'dependencies': getattr(module_ann, 'dependencies', [])
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
1249
|
# Extract function-level invariants that might be global
|
|
1164
1250
|
for func_ann in module_ann.functions:
|
|
1165
1251
|
if hasattr(func_ann, 'invariants') and func_ann.invariants:
|
|
@@ -1246,28 +1332,37 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1246
1332
|
context = parse_context_file(data)
|
|
1247
1333
|
write_context_yaml(context, context_path)
|
|
1248
1334
|
|
|
1249
|
-
print(f"✅ Context updated from code annotations")
|
|
1250
|
-
print(f" Added {len(new_invs)} new invariants")
|
|
1251
|
-
print(f" Added {len(assumptions) - len(existing_assumption_texts)} new assumptions")
|
|
1252
1335
|
print()
|
|
1253
|
-
|
|
1336
|
+
success("Context updated from code annotations")
|
|
1337
|
+
info(f"Added {len(new_invs)} new invariants")
|
|
1338
|
+
info(f"Added {len(assumptions) - len(existing_assumption_texts)} new assumptions")
|
|
1339
|
+
print()
|
|
1340
|
+
info("Tip: Run 'voodocs context view' to see the updated context")
|
|
1254
1341
|
|
|
1255
1342
|
else:
|
|
1256
1343
|
# Create new context (should not reach here, but handle gracefully)
|
|
1257
|
-
|
|
1258
|
-
|
|
1344
|
+
error("Context generation requires an existing context file.")
|
|
1345
|
+
info("Run 'voodocs context init' first, then use 'voodocs context generate --update'")
|
|
1259
1346
|
return 1
|
|
1260
1347
|
|
|
1261
1348
|
return 0
|
|
1262
1349
|
|
|
1350
|
+
except InvalidContextError as e:
|
|
1351
|
+
error(str(e))
|
|
1352
|
+
return 1
|
|
1353
|
+
except ContextFileNotFoundError as e:
|
|
1354
|
+
error(str(e))
|
|
1355
|
+
return 1
|
|
1356
|
+
except InvalidContextError as e:
|
|
1357
|
+
error(str(e))
|
|
1358
|
+
return 1
|
|
1263
1359
|
except ImportError as e:
|
|
1264
|
-
|
|
1265
|
-
|
|
1360
|
+
error(f"Failed to import annotation parser: {e}")
|
|
1361
|
+
warning("Make sure VooDocs is properly installed")
|
|
1266
1362
|
return 1
|
|
1267
1363
|
except Exception as e:
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
traceback.print_exc()
|
|
1364
|
+
error(f"Failed to generate context: {e}")
|
|
1365
|
+
warning("Run with --debug for full stack trace")
|
|
1271
1366
|
return 1
|
|
1272
1367
|
|
|
1273
1368
|
|
|
@@ -1283,29 +1378,26 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1283
1378
|
Returns:
|
|
1284
1379
|
Exit code (0 for success, 1 for error)
|
|
1285
1380
|
"""
|
|
1286
|
-
if not context_file_exists():
|
|
1287
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
1288
|
-
print(" Run 'voodocs context init' to create one.")
|
|
1289
|
-
return 1
|
|
1290
|
-
|
|
1291
1381
|
try:
|
|
1382
|
+
# Check if context file exists
|
|
1383
|
+
context_path = get_context_file_path()
|
|
1384
|
+
if not context_file_exists():
|
|
1385
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
1386
|
+
|
|
1292
1387
|
import re
|
|
1293
1388
|
import json
|
|
1294
1389
|
|
|
1295
1390
|
# Read context
|
|
1296
|
-
context_path = get_context_file_path()
|
|
1297
1391
|
data = read_context_yaml(context_path)
|
|
1298
1392
|
|
|
1299
1393
|
if not data:
|
|
1300
|
-
|
|
1301
|
-
return 1
|
|
1394
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
1302
1395
|
|
|
1303
1396
|
# Compile regex pattern (case-insensitive)
|
|
1304
1397
|
try:
|
|
1305
1398
|
pattern = re.compile(query, re.IGNORECASE)
|
|
1306
1399
|
except re.error as e:
|
|
1307
|
-
|
|
1308
|
-
return 1
|
|
1400
|
+
raise ValueError(f"Invalid regex pattern: {e}")
|
|
1309
1401
|
|
|
1310
1402
|
# Define searchable sections
|
|
1311
1403
|
searchable_sections = {
|
|
@@ -1327,8 +1419,8 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1327
1419
|
# Filter by section if specified
|
|
1328
1420
|
if section:
|
|
1329
1421
|
if section not in searchable_sections:
|
|
1330
|
-
|
|
1331
|
-
|
|
1422
|
+
error(f"Unknown section '{section}'")
|
|
1423
|
+
info(f"Available sections: {', '.join(searchable_sections.keys())}")
|
|
1332
1424
|
return 1
|
|
1333
1425
|
searchable_sections = {section: searchable_sections[section]}
|
|
1334
1426
|
|
|
@@ -1358,9 +1450,9 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1358
1450
|
|
|
1359
1451
|
# Display results
|
|
1360
1452
|
if not all_results:
|
|
1361
|
-
|
|
1453
|
+
info(f"No results found for: {query}")
|
|
1362
1454
|
if section:
|
|
1363
|
-
|
|
1455
|
+
info(f"(searched in: {section})")
|
|
1364
1456
|
return 0
|
|
1365
1457
|
|
|
1366
1458
|
if format == 'json':
|
|
@@ -1401,10 +1493,18 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1401
1493
|
|
|
1402
1494
|
return 0
|
|
1403
1495
|
|
|
1496
|
+
except ContextFileNotFoundError as e:
|
|
1497
|
+
error(str(e))
|
|
1498
|
+
return 1
|
|
1499
|
+
except InvalidContextError as e:
|
|
1500
|
+
error(str(e))
|
|
1501
|
+
return 1
|
|
1502
|
+
except ValueError as e:
|
|
1503
|
+
error(str(e))
|
|
1504
|
+
return 1
|
|
1404
1505
|
except Exception as e:
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
traceback.print_exc()
|
|
1506
|
+
error(f"Failed to query context: {e}")
|
|
1507
|
+
warning("Run with --debug for full stack trace")
|
|
1408
1508
|
return 1
|
|
1409
1509
|
|
|
1410
1510
|
|
|
@@ -1499,30 +1599,28 @@ def cmd_context_check(
|
|
|
1499
1599
|
Returns:
|
|
1500
1600
|
Exit code (0 for all passed, 1 for violations found, 2 for error)
|
|
1501
1601
|
"""
|
|
1502
|
-
if not context_file_exists():
|
|
1503
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
1504
|
-
print(" Run 'voodocs context init' to create one.")
|
|
1505
|
-
return 2
|
|
1506
|
-
|
|
1507
1602
|
try:
|
|
1603
|
+
# Check if context file exists
|
|
1604
|
+
context_path = get_context_file_path()
|
|
1605
|
+
if not context_file_exists():
|
|
1606
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
1607
|
+
|
|
1508
1608
|
from .checker import InvariantChecker, ViolationSeverity
|
|
1509
1609
|
import json
|
|
1510
1610
|
|
|
1511
1611
|
# Read context
|
|
1512
|
-
context_path = get_context_file_path()
|
|
1513
1612
|
data = read_context_yaml(context_path)
|
|
1514
1613
|
|
|
1515
1614
|
if not data:
|
|
1516
|
-
|
|
1517
|
-
return 2
|
|
1615
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
1518
1616
|
|
|
1519
1617
|
# Get invariants
|
|
1520
1618
|
invariants_data = data.get('invariants', {})
|
|
1521
1619
|
global_invariants = invariants_data.get('global', [])
|
|
1522
1620
|
|
|
1523
1621
|
if not global_invariants:
|
|
1524
|
-
|
|
1525
|
-
|
|
1622
|
+
warning("No invariants found in context file.")
|
|
1623
|
+
info("Add invariants to the 'invariants.global' section.")
|
|
1526
1624
|
return 0
|
|
1527
1625
|
|
|
1528
1626
|
# Filter invariants if specified
|
|
@@ -1637,13 +1735,19 @@ def cmd_context_check(
|
|
|
1637
1735
|
return 1
|
|
1638
1736
|
return 0
|
|
1639
1737
|
|
|
1738
|
+
except ContextFileNotFoundError as e:
|
|
1739
|
+
error(str(e))
|
|
1740
|
+
return 2
|
|
1741
|
+
except InvalidContextError as e:
|
|
1742
|
+
error(str(e))
|
|
1743
|
+
return 2
|
|
1640
1744
|
except ImportError as e:
|
|
1641
|
-
|
|
1745
|
+
error(f"Failed to import checker: {e}")
|
|
1746
|
+
warning("Make sure all dependencies are installed")
|
|
1642
1747
|
return 2
|
|
1643
1748
|
except Exception as e:
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
traceback.print_exc()
|
|
1749
|
+
error(f"Failed to check invariants: {e}")
|
|
1750
|
+
warning("Run with --debug for full stack trace")
|
|
1647
1751
|
return 2
|
|
1648
1752
|
|
|
1649
1753
|
|
|
@@ -1666,21 +1770,19 @@ def cmd_context_diagram(
|
|
|
1666
1770
|
Returns:
|
|
1667
1771
|
Exit code (0 for success, 1 for error)
|
|
1668
1772
|
"""
|
|
1669
|
-
if not context_file_exists():
|
|
1670
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
1671
|
-
print(" Run 'voodocs context init' to create one.")
|
|
1672
|
-
return 1
|
|
1673
|
-
|
|
1674
1773
|
try:
|
|
1774
|
+
# Check if context file exists
|
|
1775
|
+
context_path = get_context_file_path()
|
|
1776
|
+
if not context_file_exists():
|
|
1777
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
1778
|
+
|
|
1675
1779
|
from .diagram import DiagramGenerator
|
|
1676
1780
|
|
|
1677
1781
|
# Read context
|
|
1678
|
-
context_path = get_context_file_path()
|
|
1679
1782
|
data = read_context_yaml(context_path)
|
|
1680
1783
|
|
|
1681
1784
|
if not data:
|
|
1682
|
-
|
|
1683
|
-
return 1
|
|
1785
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
1684
1786
|
|
|
1685
1787
|
# Initialize generator
|
|
1686
1788
|
generator = DiagramGenerator()
|
|
@@ -1689,20 +1791,20 @@ def cmd_context_diagram(
|
|
|
1689
1791
|
diagrams = {}
|
|
1690
1792
|
|
|
1691
1793
|
if diagram_type in ['modules', 'all']:
|
|
1692
|
-
|
|
1794
|
+
step("Generating module diagram...")
|
|
1693
1795
|
diagrams['modules'] = generator.generate_module_diagram(data, output_format)
|
|
1694
1796
|
|
|
1695
1797
|
if diagram_type in ['dependencies', 'all']:
|
|
1696
|
-
|
|
1798
|
+
step("Generating dependency diagram...")
|
|
1697
1799
|
diagrams['dependencies'] = generator.generate_dependency_diagram(data, output_format)
|
|
1698
1800
|
|
|
1699
1801
|
if diagram_type in ['flow', 'all']:
|
|
1700
|
-
|
|
1802
|
+
step("Generating flow diagram...")
|
|
1701
1803
|
diagrams['flow'] = generator.generate_flow_diagram(data, output_format)
|
|
1702
1804
|
|
|
1703
1805
|
if not diagrams:
|
|
1704
|
-
|
|
1705
|
-
|
|
1806
|
+
error(f"Unknown diagram type: {diagram_type}")
|
|
1807
|
+
info("Valid types: modules, dependencies, flow, all")
|
|
1706
1808
|
return 1
|
|
1707
1809
|
|
|
1708
1810
|
# Output diagrams
|
|
@@ -1747,7 +1849,7 @@ def cmd_context_diagram(
|
|
|
1747
1849
|
else:
|
|
1748
1850
|
# Save source only
|
|
1749
1851
|
output_path.write_text(diagrams[diagram_type])
|
|
1750
|
-
|
|
1852
|
+
success(f"Diagram saved to: {output_path}")
|
|
1751
1853
|
else:
|
|
1752
1854
|
# Print to console
|
|
1753
1855
|
for dtype, diagram_source in diagrams.items():
|
|
@@ -1760,11 +1862,17 @@ def cmd_context_diagram(
|
|
|
1760
1862
|
|
|
1761
1863
|
return 0
|
|
1762
1864
|
|
|
1865
|
+
except ContextFileNotFoundError as e:
|
|
1866
|
+
error(str(e))
|
|
1867
|
+
return 1
|
|
1868
|
+
except InvalidContextError as e:
|
|
1869
|
+
error(str(e))
|
|
1870
|
+
return 1
|
|
1763
1871
|
except ImportError as e:
|
|
1764
|
-
|
|
1872
|
+
error(f"Failed to import diagram generator: {e}")
|
|
1873
|
+
warning("Make sure all dependencies are installed")
|
|
1765
1874
|
return 1
|
|
1766
1875
|
except Exception as e:
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
traceback.print_exc()
|
|
1876
|
+
error(f"Failed to generate diagram: {e}")
|
|
1877
|
+
warning("Run with --debug for full stack trace")
|
|
1770
1878
|
return 1
|