@voodocs/cli 0.3.2 → 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 +441 -0
- package/cli.py +31 -2
- 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/checker.py +290 -62
- package/lib/darkarts/context/commands.py +374 -290
- 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 +117 -33
- package/lib/darkarts/exceptions.py +5 -0
- package/lib/darkarts/plugins/voodocs/instruction_generator.py +8 -1
- package/package.json +1 -1
|
@@ -1,27 +1,10 @@
|
|
|
1
|
-
"""@
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"diagram: Architecture diagram generation"
|
|
9
|
-
]
|
|
10
|
-
assumptions: [
|
|
11
|
-
"Context file (.voodocs.context) is in project root",
|
|
12
|
-
"Git is available for commit/author detection",
|
|
13
|
-
"Python 3.11+ is installed",
|
|
14
|
-
"Project root is current working directory"
|
|
15
|
-
]
|
|
16
|
-
invariants: [
|
|
17
|
-
"Context file must be valid YAML",
|
|
18
|
-
"Version numbers must follow semver (major.minor)",
|
|
19
|
-
"All commands must return 0 (success) or 1 (error) exit code",
|
|
20
|
-
"Context updates must increment version number",
|
|
21
|
-
"Generated files must be UTF-8 encoded"
|
|
22
|
-
]
|
|
23
|
-
security_model: "Read context from disk, write only with user confirmation for init"
|
|
24
|
-
performance_model: "O(1) for most commands, O(n) for generate where n=number of source files"
|
|
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}
|
|
25
8
|
|
|
26
9
|
Context System Commands
|
|
27
10
|
|
|
@@ -39,6 +22,26 @@ from .yaml_utils import (
|
|
|
39
22
|
add_to_gitignore,
|
|
40
23
|
format_context_as_markdown
|
|
41
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
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
def get_project_root() -> Path:
|
|
@@ -206,63 +209,76 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
206
209
|
Returns:
|
|
207
210
|
Exit code (0 for success, 1 for error)
|
|
208
211
|
"""
|
|
209
|
-
print("🎯 VooDocs Context Initialization")
|
|
210
|
-
print()
|
|
211
|
-
|
|
212
|
-
# Check if context file already exists
|
|
213
|
-
if context_file_exists() and not force:
|
|
214
|
-
print("❌ Error: .voodocs.context already exists in this directory.")
|
|
215
|
-
print(" Use --force to overwrite the existing file.")
|
|
216
|
-
return 1
|
|
217
|
-
|
|
218
|
-
# Detect project information
|
|
219
|
-
detected_version = detect_code_version()
|
|
220
|
-
detected_repo = detect_repository()
|
|
221
|
-
|
|
222
|
-
print("Let's set up your project context. This will create a .voodocs.context file.")
|
|
223
|
-
print()
|
|
224
|
-
|
|
225
|
-
# Prompt for project information
|
|
226
|
-
project_name = prompt_user("Project name")
|
|
227
|
-
|
|
228
|
-
project_purpose = prompt_user("Project purpose (one sentence)")
|
|
229
|
-
|
|
230
|
-
if detected_version:
|
|
231
|
-
code_version = prompt_user("Current code version", detected_version)
|
|
232
|
-
else:
|
|
233
|
-
code_version = prompt_user("Current code version", "1.0.0")
|
|
234
|
-
|
|
235
|
-
# Validate version format
|
|
236
|
-
if not code_version.count('.') >= 2:
|
|
237
|
-
print(f"⚠️ Warning: '{code_version}' doesn't look like semantic versioning (MAJOR.MINOR.PATCH)")
|
|
238
|
-
if not prompt_yes_no("Continue anyway?", default=False):
|
|
239
|
-
print("Aborted.")
|
|
240
|
-
return 1
|
|
241
|
-
|
|
242
|
-
if detected_repo:
|
|
243
|
-
repository = prompt_user("Repository URL (leave empty to skip)", detected_repo, required=False)
|
|
244
|
-
else:
|
|
245
|
-
repository = prompt_user("Repository URL (leave empty to skip)", "", required=False)
|
|
246
|
-
|
|
247
|
-
license_name = prompt_user("License (leave empty to skip)", "", required=False)
|
|
248
|
-
|
|
249
|
-
print()
|
|
250
|
-
print("📝 Configuration Summary:")
|
|
251
|
-
print(f" Project Name: {project_name}")
|
|
252
|
-
print(f" Purpose: {project_purpose}")
|
|
253
|
-
print(f" Code Version: {code_version}")
|
|
254
|
-
if repository:
|
|
255
|
-
print(f" Repository: {repository}")
|
|
256
|
-
if license_name:
|
|
257
|
-
print(f" License: {license_name}")
|
|
258
|
-
print()
|
|
259
|
-
|
|
260
|
-
if not prompt_yes_no("Create context file?", default=True):
|
|
261
|
-
print("Aborted.")
|
|
262
|
-
return 1
|
|
263
|
-
|
|
264
|
-
# Create context file
|
|
265
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...")
|
|
266
282
|
context = create_minimal_context(
|
|
267
283
|
project_name=project_name,
|
|
268
284
|
project_purpose=project_purpose,
|
|
@@ -271,20 +287,20 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
271
287
|
license=license_name if license_name else None
|
|
272
288
|
)
|
|
273
289
|
|
|
274
|
-
|
|
290
|
+
step(f"Writing context file to {context_path}...")
|
|
275
291
|
write_context_yaml(context, context_path)
|
|
276
292
|
|
|
277
293
|
print()
|
|
278
|
-
|
|
279
|
-
|
|
294
|
+
success("Context file created successfully!")
|
|
295
|
+
info(f"Context version: {context.versioning.context_version}")
|
|
280
296
|
print()
|
|
281
297
|
|
|
282
298
|
# Ask about .gitignore
|
|
283
|
-
if
|
|
299
|
+
if confirm("Add .voodocs.context to .gitignore? (recommended for private projects)", default=True):
|
|
284
300
|
if add_to_gitignore(get_project_root()):
|
|
285
|
-
|
|
301
|
+
success("Added to .gitignore")
|
|
286
302
|
else:
|
|
287
|
-
|
|
303
|
+
info("Already in .gitignore")
|
|
288
304
|
|
|
289
305
|
print()
|
|
290
306
|
|
|
@@ -368,9 +384,9 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
368
384
|
print("ℹ️ Skipped AI integration")
|
|
369
385
|
|
|
370
386
|
print()
|
|
371
|
-
|
|
387
|
+
success("🎉 Context initialized successfully!")
|
|
372
388
|
print()
|
|
373
|
-
|
|
389
|
+
info("Next steps:")
|
|
374
390
|
print(" 1. Review the context file: voodocs context view")
|
|
375
391
|
print(" 2. Auto-generate from code: voodocs context generate --update")
|
|
376
392
|
print(" 3. Check invariants: voodocs context check")
|
|
@@ -380,8 +396,18 @@ def cmd_context_init(force: bool = False) -> int:
|
|
|
380
396
|
|
|
381
397
|
return 0
|
|
382
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
|
|
383
407
|
except Exception as e:
|
|
384
|
-
print(
|
|
408
|
+
print()
|
|
409
|
+
error(f"Failed to initialize context: {e}")
|
|
410
|
+
warning("Run with --debug for full stack trace")
|
|
385
411
|
return 1
|
|
386
412
|
|
|
387
413
|
|
|
@@ -395,19 +421,17 @@ def cmd_context_view(output_file: Optional[str] = None) -> int:
|
|
|
395
421
|
Returns:
|
|
396
422
|
Exit code (0 for success, 1 for error)
|
|
397
423
|
"""
|
|
398
|
-
if not context_file_exists():
|
|
399
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
400
|
-
print(" Run 'voodocs context init' to create one.")
|
|
401
|
-
return 1
|
|
402
|
-
|
|
403
424
|
try:
|
|
404
|
-
#
|
|
425
|
+
# Check if context file exists
|
|
405
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
|
|
406
431
|
data = read_context_yaml(context_path)
|
|
407
432
|
|
|
408
433
|
if not data:
|
|
409
|
-
|
|
410
|
-
return 1
|
|
434
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
411
435
|
|
|
412
436
|
# Parse and format as Markdown
|
|
413
437
|
from .yaml_utils import parse_context_file
|
|
@@ -417,17 +441,25 @@ def cmd_context_view(output_file: Optional[str] = None) -> int:
|
|
|
417
441
|
if output_file:
|
|
418
442
|
# Write to file
|
|
419
443
|
output_path = Path(output_file)
|
|
444
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
420
445
|
with open(output_path, 'w', encoding='utf-8') as f:
|
|
421
446
|
f.write(markdown)
|
|
422
|
-
|
|
447
|
+
success(f"Context exported to {output_file}")
|
|
423
448
|
return 0
|
|
424
449
|
else:
|
|
425
450
|
# Print to console
|
|
426
451
|
print(markdown)
|
|
427
452
|
return 0
|
|
428
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
|
|
429
460
|
except Exception as e:
|
|
430
|
-
|
|
461
|
+
error(f"Failed to view context: {e}")
|
|
462
|
+
warning("Run with --debug for full stack trace")
|
|
431
463
|
return 1
|
|
432
464
|
|
|
433
465
|
|
|
@@ -438,19 +470,17 @@ def cmd_context_status() -> int:
|
|
|
438
470
|
Returns:
|
|
439
471
|
Exit code (0 for success, 1 for error)
|
|
440
472
|
"""
|
|
441
|
-
if not context_file_exists():
|
|
442
|
-
print("❌ No context file found")
|
|
443
|
-
print(" Run 'voodocs context init' to create one.")
|
|
444
|
-
return 1
|
|
445
|
-
|
|
446
473
|
try:
|
|
447
|
-
#
|
|
474
|
+
# Check if context file exists
|
|
448
475
|
context_path = get_context_file_path()
|
|
476
|
+
if not context_file_exists():
|
|
477
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
478
|
+
|
|
479
|
+
# Read context file
|
|
449
480
|
data = read_context_yaml(context_path)
|
|
450
481
|
|
|
451
482
|
if not data:
|
|
452
|
-
|
|
453
|
-
return 1
|
|
483
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
454
484
|
|
|
455
485
|
# Extract key information
|
|
456
486
|
versioning = data.get('versioning', {})
|
|
@@ -479,12 +509,19 @@ def cmd_context_status() -> int:
|
|
|
479
509
|
print(f"Known Issues: {len(known_issues)}")
|
|
480
510
|
|
|
481
511
|
print()
|
|
482
|
-
|
|
512
|
+
info("Use 'voodocs context view' to see the full context.")
|
|
483
513
|
|
|
484
514
|
return 0
|
|
485
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
|
|
486
522
|
except Exception as e:
|
|
487
|
-
|
|
523
|
+
error(f"Failed to read context status: {e}")
|
|
524
|
+
warning("Run with --debug for full stack trace")
|
|
488
525
|
return 1
|
|
489
526
|
|
|
490
527
|
|
|
@@ -498,22 +535,20 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
498
535
|
Returns:
|
|
499
536
|
Exit code (0 for success, 1 for error)
|
|
500
537
|
"""
|
|
501
|
-
if not context_file_exists():
|
|
502
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
503
|
-
print(" Run 'voodocs context init' to create one.")
|
|
504
|
-
return 1
|
|
505
|
-
|
|
506
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
|
+
|
|
507
544
|
from datetime import date
|
|
508
545
|
from .models import Change
|
|
509
546
|
|
|
510
547
|
# Read current context
|
|
511
|
-
context_path = get_context_file_path()
|
|
512
548
|
data = read_context_yaml(context_path)
|
|
513
549
|
|
|
514
550
|
if not data:
|
|
515
|
-
|
|
516
|
-
return 1
|
|
551
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
517
552
|
|
|
518
553
|
# Get current versions
|
|
519
554
|
versioning = data.get('versioning', {})
|
|
@@ -523,14 +558,13 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
523
558
|
# Parse context version
|
|
524
559
|
parts = current_context_version.split('.')
|
|
525
560
|
if len(parts) != 2:
|
|
526
|
-
|
|
527
|
-
return 1
|
|
561
|
+
raise ValueError(f"Invalid context version format: {current_context_version}. Expected MAJOR.MINOR format.")
|
|
528
562
|
|
|
529
563
|
major, minor = parts
|
|
530
564
|
new_minor = int(minor) + 1
|
|
531
565
|
new_context_version = f"{major}.{new_minor}"
|
|
532
566
|
|
|
533
|
-
|
|
567
|
+
step("Updating context...")
|
|
534
568
|
print()
|
|
535
569
|
print(f"Current version: {current_context_version}")
|
|
536
570
|
print(f"New version: {new_context_version}")
|
|
@@ -538,9 +572,10 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
538
572
|
|
|
539
573
|
# Prompt for description if not provided
|
|
540
574
|
if not description:
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
575
|
+
try:
|
|
576
|
+
description = prompt("Change description", default="Context updated", required=False)
|
|
577
|
+
except KeyboardInterrupt:
|
|
578
|
+
raise UserInterruptError("Context update")
|
|
544
579
|
|
|
545
580
|
# Update versioning
|
|
546
581
|
today = date.today().isoformat()
|
|
@@ -571,20 +606,34 @@ def cmd_context_update(description: Optional[str] = None) -> int:
|
|
|
571
606
|
data['changes'].append(change_entry)
|
|
572
607
|
|
|
573
608
|
# Write updated context
|
|
609
|
+
step("Writing updated context...")
|
|
574
610
|
from .yaml_utils import parse_context_file
|
|
575
611
|
context = parse_context_file(data)
|
|
576
612
|
write_context_yaml(context, context_path)
|
|
577
613
|
|
|
578
|
-
print(
|
|
579
|
-
|
|
614
|
+
print()
|
|
615
|
+
success(f"Context updated to version {new_context_version}")
|
|
616
|
+
info(f"Description: {description}")
|
|
580
617
|
print()
|
|
581
618
|
|
|
582
619
|
return 0
|
|
583
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
|
|
584
634
|
except Exception as e:
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
traceback.print_exc()
|
|
635
|
+
error(f"Failed to update context: {e}")
|
|
636
|
+
warning("Run with --debug for full stack trace")
|
|
588
637
|
return 1
|
|
589
638
|
|
|
590
639
|
|
|
@@ -599,21 +648,19 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
599
648
|
Returns:
|
|
600
649
|
Exit code (0 for success, 1 for error)
|
|
601
650
|
"""
|
|
602
|
-
if not context_file_exists():
|
|
603
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
604
|
-
print(" Run 'voodocs context init' to create one.")
|
|
605
|
-
return 1
|
|
606
|
-
|
|
607
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
|
+
|
|
608
657
|
from datetime import date
|
|
609
658
|
|
|
610
659
|
# Read current context
|
|
611
|
-
context_path = get_context_file_path()
|
|
612
660
|
data = read_context_yaml(context_path)
|
|
613
661
|
|
|
614
662
|
if not data:
|
|
615
|
-
|
|
616
|
-
return 1
|
|
663
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
617
664
|
|
|
618
665
|
# Get current versions
|
|
619
666
|
versioning = data.get('versioning', {})
|
|
@@ -623,30 +670,33 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
623
670
|
# If no code version provided, try to detect it
|
|
624
671
|
if not code_version:
|
|
625
672
|
detected_version = detect_code_version()
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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")
|
|
637
687
|
|
|
638
688
|
# Validate version format
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
689
|
+
try:
|
|
690
|
+
code_version = validate_version(code_version, allow_empty=False)
|
|
691
|
+
except InvalidVersionError as e:
|
|
692
|
+
raise InvalidVersionError(code_version)
|
|
643
693
|
|
|
644
694
|
# Extract major versions
|
|
645
695
|
current_major = current_code_version.split('.')[0]
|
|
646
696
|
new_major = code_version.split('.')[0]
|
|
647
697
|
context_major = current_context_version.split('.')[0]
|
|
648
698
|
|
|
649
|
-
|
|
699
|
+
step("Syncing context with code version...")
|
|
650
700
|
print()
|
|
651
701
|
print(f"Current code version: {current_code_version}")
|
|
652
702
|
print(f"New code version: {code_version}")
|
|
@@ -663,9 +713,8 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
663
713
|
print()
|
|
664
714
|
|
|
665
715
|
if not force:
|
|
666
|
-
if not
|
|
667
|
-
|
|
668
|
-
return 1
|
|
716
|
+
if not confirm("Continue with reset?", default=True):
|
|
717
|
+
raise UserInterruptError("Context sync")
|
|
669
718
|
|
|
670
719
|
# Update versions
|
|
671
720
|
today = date.today().isoformat()
|
|
@@ -701,8 +750,8 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
701
750
|
context = parse_context_file(data)
|
|
702
751
|
write_context_yaml(context, context_path)
|
|
703
752
|
|
|
704
|
-
|
|
705
|
-
|
|
753
|
+
success(f"Context version reset to {new_context_version}")
|
|
754
|
+
info(f"Code version updated to {code_version}")
|
|
706
755
|
print()
|
|
707
756
|
|
|
708
757
|
elif context_major != new_major:
|
|
@@ -746,8 +795,8 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
746
795
|
context = parse_context_file(data)
|
|
747
796
|
write_context_yaml(context, context_path)
|
|
748
797
|
|
|
749
|
-
|
|
750
|
-
|
|
798
|
+
success(f"Context synced to {new_context_version}")
|
|
799
|
+
info(f"Code version updated to {code_version}")
|
|
751
800
|
print()
|
|
752
801
|
|
|
753
802
|
else:
|
|
@@ -760,16 +809,28 @@ def cmd_context_sync(code_version: Optional[str] = None, force: bool = False) ->
|
|
|
760
809
|
context = parse_context_file(data)
|
|
761
810
|
write_context_yaml(context, context_path)
|
|
762
811
|
|
|
763
|
-
|
|
764
|
-
|
|
812
|
+
success(f"Code version updated to {code_version}")
|
|
813
|
+
info(f"Context version unchanged: {current_context_version}")
|
|
765
814
|
print()
|
|
766
815
|
|
|
767
816
|
return 0
|
|
768
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
|
|
769
831
|
except Exception as e:
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
traceback.print_exc()
|
|
832
|
+
error(f"Failed to sync context: {e}")
|
|
833
|
+
warning("Run with --debug for full stack trace")
|
|
773
834
|
return 1
|
|
774
835
|
|
|
775
836
|
|
|
@@ -780,26 +841,24 @@ def cmd_context_history() -> int:
|
|
|
780
841
|
Returns:
|
|
781
842
|
Exit code (0 for success, 1 for error)
|
|
782
843
|
"""
|
|
783
|
-
if not context_file_exists():
|
|
784
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
785
|
-
print(" Run 'voodocs context init' to create one.")
|
|
786
|
-
return 1
|
|
787
|
-
|
|
788
844
|
try:
|
|
789
|
-
#
|
|
845
|
+
# Check if context file exists
|
|
790
846
|
context_path = get_context_file_path()
|
|
847
|
+
if not context_file_exists():
|
|
848
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
849
|
+
|
|
850
|
+
# Read current context
|
|
791
851
|
data = read_context_yaml(context_path)
|
|
792
852
|
|
|
793
853
|
if not data:
|
|
794
|
-
|
|
795
|
-
return 1
|
|
854
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
796
855
|
|
|
797
856
|
# Get changes
|
|
798
857
|
changes = data.get('changes', [])
|
|
799
858
|
|
|
800
859
|
if not changes:
|
|
801
|
-
|
|
802
|
-
|
|
860
|
+
warning("No change history found.")
|
|
861
|
+
info("Changes will be recorded when you run 'voodocs context update'.")
|
|
803
862
|
return 0
|
|
804
863
|
|
|
805
864
|
print("📜 Context Version History")
|
|
@@ -830,8 +889,15 @@ def cmd_context_history() -> int:
|
|
|
830
889
|
|
|
831
890
|
return 0
|
|
832
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
|
|
833
898
|
except Exception as e:
|
|
834
|
-
|
|
899
|
+
error(f"Failed to read history: {e}")
|
|
900
|
+
warning("Run with --debug for full stack trace")
|
|
835
901
|
return 1
|
|
836
902
|
|
|
837
903
|
|
|
@@ -846,19 +912,17 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
846
912
|
Returns:
|
|
847
913
|
Exit code (0 for success, 1 for error)
|
|
848
914
|
"""
|
|
849
|
-
if not context_file_exists():
|
|
850
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
851
|
-
print(" Run 'voodocs context init' to create one.")
|
|
852
|
-
return 1
|
|
853
|
-
|
|
854
915
|
try:
|
|
855
|
-
#
|
|
916
|
+
# Check if context file exists
|
|
856
917
|
context_path = get_context_file_path()
|
|
918
|
+
if not context_file_exists():
|
|
919
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
920
|
+
|
|
921
|
+
# Read current context
|
|
857
922
|
data = read_context_yaml(context_path)
|
|
858
923
|
|
|
859
924
|
if not data:
|
|
860
|
-
|
|
861
|
-
return 1
|
|
925
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
862
926
|
|
|
863
927
|
# Get current version
|
|
864
928
|
versioning = data.get('versioning', {})
|
|
@@ -872,8 +936,8 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
872
936
|
if not version1:
|
|
873
937
|
changes = data.get('changes', [])
|
|
874
938
|
if len(changes) < 2:
|
|
875
|
-
|
|
876
|
-
|
|
939
|
+
warning("Not enough version history to compare.")
|
|
940
|
+
info("Need at least 2 versions. Run 'voodocs context history' to see available versions.")
|
|
877
941
|
return 0
|
|
878
942
|
|
|
879
943
|
# Get the two most recent versions
|
|
@@ -905,7 +969,7 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
905
969
|
continue
|
|
906
970
|
|
|
907
971
|
if not relevant_changes:
|
|
908
|
-
|
|
972
|
+
info(f"No changes found between v{version1} and v{version2}")
|
|
909
973
|
return 0
|
|
910
974
|
|
|
911
975
|
print(f"Changes ({len(relevant_changes)}):")
|
|
@@ -924,14 +988,19 @@ def cmd_context_diff(version1: Optional[str] = None, version2: Optional[str] = N
|
|
|
924
988
|
print(f" Code: v{code_version}")
|
|
925
989
|
print()
|
|
926
990
|
|
|
927
|
-
|
|
991
|
+
info("Tip: Use 'voodocs context history' to see the full timeline.")
|
|
928
992
|
|
|
929
993
|
return 0
|
|
930
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
|
|
931
1001
|
except Exception as e:
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
traceback.print_exc()
|
|
1002
|
+
error(f"Failed to compare versions: {e}")
|
|
1003
|
+
warning("Run with --debug for full stack trace")
|
|
935
1004
|
return 1
|
|
936
1005
|
|
|
937
1006
|
|
|
@@ -946,21 +1015,19 @@ def cmd_context_validate(check_version: bool = False, check_invariants: bool = F
|
|
|
946
1015
|
Returns:
|
|
947
1016
|
Exit code (0 for success, 1 for warnings, 2 for errors)
|
|
948
1017
|
"""
|
|
949
|
-
if not context_file_exists():
|
|
950
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
951
|
-
print(" Run 'voodocs context init' to create one.")
|
|
952
|
-
return 2
|
|
953
|
-
|
|
954
1018
|
try:
|
|
955
|
-
#
|
|
1019
|
+
# Check if context file exists
|
|
956
1020
|
context_path = get_context_file_path()
|
|
1021
|
+
if not context_file_exists():
|
|
1022
|
+
raise ContextFileNotFoundError(str(context_path))
|
|
1023
|
+
|
|
1024
|
+
# Read current context
|
|
957
1025
|
data = read_context_yaml(context_path)
|
|
958
1026
|
|
|
959
1027
|
if not data:
|
|
960
|
-
|
|
961
|
-
return 2
|
|
1028
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
962
1029
|
|
|
963
|
-
|
|
1030
|
+
step("Validating context file...")
|
|
964
1031
|
print()
|
|
965
1032
|
|
|
966
1033
|
errors = []
|
|
@@ -1080,7 +1147,7 @@ def cmd_context_validate(check_version: bool = False, check_invariants: bool = F
|
|
|
1080
1147
|
break
|
|
1081
1148
|
|
|
1082
1149
|
if not errors and not warnings:
|
|
1083
|
-
|
|
1150
|
+
success("Context file is valid!")
|
|
1084
1151
|
print()
|
|
1085
1152
|
|
|
1086
1153
|
# Show summary
|
|
@@ -1093,23 +1160,28 @@ def cmd_context_validate(check_version: bool = False, check_invariants: bool = F
|
|
|
1093
1160
|
|
|
1094
1161
|
# Show suggestions if any
|
|
1095
1162
|
if suggestions:
|
|
1096
|
-
|
|
1163
|
+
info("Suggestions:")
|
|
1097
1164
|
for suggestion in suggestions[:5]: # Limit to 5
|
|
1098
1165
|
print(f" • {suggestion}")
|
|
1099
1166
|
print()
|
|
1100
1167
|
|
|
1101
1168
|
return 0
|
|
1102
1169
|
elif errors:
|
|
1103
|
-
|
|
1170
|
+
error(f"Validation failed with {len(errors)} error(s) and {len(warnings)} warning(s)")
|
|
1104
1171
|
return 2
|
|
1105
1172
|
else:
|
|
1106
|
-
|
|
1173
|
+
warning(f"Validation passed with {len(warnings)} warning(s)")
|
|
1107
1174
|
return 1
|
|
1108
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
|
|
1109
1182
|
except Exception as e:
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
traceback.print_exc()
|
|
1183
|
+
error(f"Failed to validate context: {e}")
|
|
1184
|
+
warning("Run with --debug for full stack trace")
|
|
1113
1185
|
return 2
|
|
1114
1186
|
|
|
1115
1187
|
|
|
@@ -1127,20 +1199,16 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1127
1199
|
from pathlib import Path
|
|
1128
1200
|
import sys
|
|
1129
1201
|
|
|
1130
|
-
# Determine source directory
|
|
1131
|
-
if source_dir:
|
|
1132
|
-
scan_path = Path(source_dir).resolve()
|
|
1133
|
-
else:
|
|
1134
|
-
scan_path = Path.cwd()
|
|
1135
|
-
|
|
1136
|
-
if not scan_path.exists():
|
|
1137
|
-
print(f"❌ Error: Directory not found: {scan_path}")
|
|
1138
|
-
return 1
|
|
1139
|
-
|
|
1140
|
-
print(f"🔍 Scanning for @voodocs annotations in: {scan_path}")
|
|
1141
|
-
print()
|
|
1142
|
-
|
|
1143
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
|
+
|
|
1144
1212
|
# Import the annotation parser
|
|
1145
1213
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
1146
1214
|
from annotations.parser import AnnotationParser
|
|
@@ -1150,14 +1218,16 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1150
1218
|
results = parser.parse_directory(scan_path)
|
|
1151
1219
|
|
|
1152
1220
|
if not results:
|
|
1153
|
-
|
|
1154
|
-
|
|
1221
|
+
warning("No @voodocs annotations found.")
|
|
1222
|
+
info("Add @voodocs annotations to your code first.")
|
|
1155
1223
|
return 1
|
|
1156
1224
|
|
|
1157
1225
|
# Extract global invariants from all annotations
|
|
1158
1226
|
global_invariants = set()
|
|
1159
1227
|
assumptions = []
|
|
1160
|
-
|
|
1228
|
+
|
|
1229
|
+
# Extract modules using helper function
|
|
1230
|
+
modules_info = extract_modules_from_results(results, scan_path)
|
|
1161
1231
|
|
|
1162
1232
|
for parsed in results:
|
|
1163
1233
|
# Get module annotation
|
|
@@ -1176,14 +1246,6 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1176
1246
|
'source': Path(parsed.source_file).relative_to(scan_path).as_posix()
|
|
1177
1247
|
})
|
|
1178
1248
|
|
|
1179
|
-
# Store module info
|
|
1180
|
-
if hasattr(module_ann, 'module_purpose') and module_ann.module_purpose:
|
|
1181
|
-
rel_path = Path(parsed.source_file).relative_to(scan_path).as_posix()
|
|
1182
|
-
modules_info[rel_path] = {
|
|
1183
|
-
'purpose': module_ann.module_purpose,
|
|
1184
|
-
'dependencies': getattr(module_ann, 'dependencies', [])
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
1249
|
# Extract function-level invariants that might be global
|
|
1188
1250
|
for func_ann in module_ann.functions:
|
|
1189
1251
|
if hasattr(func_ann, 'invariants') and func_ann.invariants:
|
|
@@ -1270,28 +1332,37 @@ def cmd_context_generate(source_dir: Optional[str] = None, update_existing: bool
|
|
|
1270
1332
|
context = parse_context_file(data)
|
|
1271
1333
|
write_context_yaml(context, context_path)
|
|
1272
1334
|
|
|
1273
|
-
print(f"✅ Context updated from code annotations")
|
|
1274
|
-
print(f" Added {len(new_invs)} new invariants")
|
|
1275
|
-
print(f" Added {len(assumptions) - len(existing_assumption_texts)} new assumptions")
|
|
1276
1335
|
print()
|
|
1277
|
-
|
|
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")
|
|
1278
1341
|
|
|
1279
1342
|
else:
|
|
1280
1343
|
# Create new context (should not reach here, but handle gracefully)
|
|
1281
|
-
|
|
1282
|
-
|
|
1344
|
+
error("Context generation requires an existing context file.")
|
|
1345
|
+
info("Run 'voodocs context init' first, then use 'voodocs context generate --update'")
|
|
1283
1346
|
return 1
|
|
1284
1347
|
|
|
1285
1348
|
return 0
|
|
1286
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
|
|
1287
1359
|
except ImportError as e:
|
|
1288
|
-
|
|
1289
|
-
|
|
1360
|
+
error(f"Failed to import annotation parser: {e}")
|
|
1361
|
+
warning("Make sure VooDocs is properly installed")
|
|
1290
1362
|
return 1
|
|
1291
1363
|
except Exception as e:
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
traceback.print_exc()
|
|
1364
|
+
error(f"Failed to generate context: {e}")
|
|
1365
|
+
warning("Run with --debug for full stack trace")
|
|
1295
1366
|
return 1
|
|
1296
1367
|
|
|
1297
1368
|
|
|
@@ -1307,29 +1378,26 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1307
1378
|
Returns:
|
|
1308
1379
|
Exit code (0 for success, 1 for error)
|
|
1309
1380
|
"""
|
|
1310
|
-
if not context_file_exists():
|
|
1311
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
1312
|
-
print(" Run 'voodocs context init' to create one.")
|
|
1313
|
-
return 1
|
|
1314
|
-
|
|
1315
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
|
+
|
|
1316
1387
|
import re
|
|
1317
1388
|
import json
|
|
1318
1389
|
|
|
1319
1390
|
# Read context
|
|
1320
|
-
context_path = get_context_file_path()
|
|
1321
1391
|
data = read_context_yaml(context_path)
|
|
1322
1392
|
|
|
1323
1393
|
if not data:
|
|
1324
|
-
|
|
1325
|
-
return 1
|
|
1394
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
1326
1395
|
|
|
1327
1396
|
# Compile regex pattern (case-insensitive)
|
|
1328
1397
|
try:
|
|
1329
1398
|
pattern = re.compile(query, re.IGNORECASE)
|
|
1330
1399
|
except re.error as e:
|
|
1331
|
-
|
|
1332
|
-
return 1
|
|
1400
|
+
raise ValueError(f"Invalid regex pattern: {e}")
|
|
1333
1401
|
|
|
1334
1402
|
# Define searchable sections
|
|
1335
1403
|
searchable_sections = {
|
|
@@ -1351,8 +1419,8 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1351
1419
|
# Filter by section if specified
|
|
1352
1420
|
if section:
|
|
1353
1421
|
if section not in searchable_sections:
|
|
1354
|
-
|
|
1355
|
-
|
|
1422
|
+
error(f"Unknown section '{section}'")
|
|
1423
|
+
info(f"Available sections: {', '.join(searchable_sections.keys())}")
|
|
1356
1424
|
return 1
|
|
1357
1425
|
searchable_sections = {section: searchable_sections[section]}
|
|
1358
1426
|
|
|
@@ -1382,9 +1450,9 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1382
1450
|
|
|
1383
1451
|
# Display results
|
|
1384
1452
|
if not all_results:
|
|
1385
|
-
|
|
1453
|
+
info(f"No results found for: {query}")
|
|
1386
1454
|
if section:
|
|
1387
|
-
|
|
1455
|
+
info(f"(searched in: {section})")
|
|
1388
1456
|
return 0
|
|
1389
1457
|
|
|
1390
1458
|
if format == 'json':
|
|
@@ -1425,10 +1493,18 @@ def cmd_context_query(query: str, section: Optional[str] = None, format: str = '
|
|
|
1425
1493
|
|
|
1426
1494
|
return 0
|
|
1427
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
|
|
1428
1505
|
except Exception as e:
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
traceback.print_exc()
|
|
1506
|
+
error(f"Failed to query context: {e}")
|
|
1507
|
+
warning("Run with --debug for full stack trace")
|
|
1432
1508
|
return 1
|
|
1433
1509
|
|
|
1434
1510
|
|
|
@@ -1523,30 +1599,28 @@ def cmd_context_check(
|
|
|
1523
1599
|
Returns:
|
|
1524
1600
|
Exit code (0 for all passed, 1 for violations found, 2 for error)
|
|
1525
1601
|
"""
|
|
1526
|
-
if not context_file_exists():
|
|
1527
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
1528
|
-
print(" Run 'voodocs context init' to create one.")
|
|
1529
|
-
return 2
|
|
1530
|
-
|
|
1531
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
|
+
|
|
1532
1608
|
from .checker import InvariantChecker, ViolationSeverity
|
|
1533
1609
|
import json
|
|
1534
1610
|
|
|
1535
1611
|
# Read context
|
|
1536
|
-
context_path = get_context_file_path()
|
|
1537
1612
|
data = read_context_yaml(context_path)
|
|
1538
1613
|
|
|
1539
1614
|
if not data:
|
|
1540
|
-
|
|
1541
|
-
return 2
|
|
1615
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
1542
1616
|
|
|
1543
1617
|
# Get invariants
|
|
1544
1618
|
invariants_data = data.get('invariants', {})
|
|
1545
1619
|
global_invariants = invariants_data.get('global', [])
|
|
1546
1620
|
|
|
1547
1621
|
if not global_invariants:
|
|
1548
|
-
|
|
1549
|
-
|
|
1622
|
+
warning("No invariants found in context file.")
|
|
1623
|
+
info("Add invariants to the 'invariants.global' section.")
|
|
1550
1624
|
return 0
|
|
1551
1625
|
|
|
1552
1626
|
# Filter invariants if specified
|
|
@@ -1661,13 +1735,19 @@ def cmd_context_check(
|
|
|
1661
1735
|
return 1
|
|
1662
1736
|
return 0
|
|
1663
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
|
|
1664
1744
|
except ImportError as e:
|
|
1665
|
-
|
|
1745
|
+
error(f"Failed to import checker: {e}")
|
|
1746
|
+
warning("Make sure all dependencies are installed")
|
|
1666
1747
|
return 2
|
|
1667
1748
|
except Exception as e:
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
traceback.print_exc()
|
|
1749
|
+
error(f"Failed to check invariants: {e}")
|
|
1750
|
+
warning("Run with --debug for full stack trace")
|
|
1671
1751
|
return 2
|
|
1672
1752
|
|
|
1673
1753
|
|
|
@@ -1690,21 +1770,19 @@ def cmd_context_diagram(
|
|
|
1690
1770
|
Returns:
|
|
1691
1771
|
Exit code (0 for success, 1 for error)
|
|
1692
1772
|
"""
|
|
1693
|
-
if not context_file_exists():
|
|
1694
|
-
print("❌ Error: No .voodocs.context file found in this directory.")
|
|
1695
|
-
print(" Run 'voodocs context init' to create one.")
|
|
1696
|
-
return 1
|
|
1697
|
-
|
|
1698
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
|
+
|
|
1699
1779
|
from .diagram import DiagramGenerator
|
|
1700
1780
|
|
|
1701
1781
|
# Read context
|
|
1702
|
-
context_path = get_context_file_path()
|
|
1703
1782
|
data = read_context_yaml(context_path)
|
|
1704
1783
|
|
|
1705
1784
|
if not data:
|
|
1706
|
-
|
|
1707
|
-
return 1
|
|
1785
|
+
raise InvalidContextError(str(context_path), "Failed to read YAML data")
|
|
1708
1786
|
|
|
1709
1787
|
# Initialize generator
|
|
1710
1788
|
generator = DiagramGenerator()
|
|
@@ -1713,20 +1791,20 @@ def cmd_context_diagram(
|
|
|
1713
1791
|
diagrams = {}
|
|
1714
1792
|
|
|
1715
1793
|
if diagram_type in ['modules', 'all']:
|
|
1716
|
-
|
|
1794
|
+
step("Generating module diagram...")
|
|
1717
1795
|
diagrams['modules'] = generator.generate_module_diagram(data, output_format)
|
|
1718
1796
|
|
|
1719
1797
|
if diagram_type in ['dependencies', 'all']:
|
|
1720
|
-
|
|
1798
|
+
step("Generating dependency diagram...")
|
|
1721
1799
|
diagrams['dependencies'] = generator.generate_dependency_diagram(data, output_format)
|
|
1722
1800
|
|
|
1723
1801
|
if diagram_type in ['flow', 'all']:
|
|
1724
|
-
|
|
1802
|
+
step("Generating flow diagram...")
|
|
1725
1803
|
diagrams['flow'] = generator.generate_flow_diagram(data, output_format)
|
|
1726
1804
|
|
|
1727
1805
|
if not diagrams:
|
|
1728
|
-
|
|
1729
|
-
|
|
1806
|
+
error(f"Unknown diagram type: {diagram_type}")
|
|
1807
|
+
info("Valid types: modules, dependencies, flow, all")
|
|
1730
1808
|
return 1
|
|
1731
1809
|
|
|
1732
1810
|
# Output diagrams
|
|
@@ -1771,7 +1849,7 @@ def cmd_context_diagram(
|
|
|
1771
1849
|
else:
|
|
1772
1850
|
# Save source only
|
|
1773
1851
|
output_path.write_text(diagrams[diagram_type])
|
|
1774
|
-
|
|
1852
|
+
success(f"Diagram saved to: {output_path}")
|
|
1775
1853
|
else:
|
|
1776
1854
|
# Print to console
|
|
1777
1855
|
for dtype, diagram_source in diagrams.items():
|
|
@@ -1784,11 +1862,17 @@ def cmd_context_diagram(
|
|
|
1784
1862
|
|
|
1785
1863
|
return 0
|
|
1786
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
|
|
1787
1871
|
except ImportError as e:
|
|
1788
|
-
|
|
1872
|
+
error(f"Failed to import diagram generator: {e}")
|
|
1873
|
+
warning("Make sure all dependencies are installed")
|
|
1789
1874
|
return 1
|
|
1790
1875
|
except Exception as e:
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
traceback.print_exc()
|
|
1876
|
+
error(f"Failed to generate diagram: {e}")
|
|
1877
|
+
warning("Run with --debug for full stack trace")
|
|
1794
1878
|
return 1
|