domd 2.2.30__tar.gz → 2.2.32__tar.gz

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.
Files changed (125) hide show
  1. {domd-2.2.30 → domd-2.2.32}/PKG-INFO +1 -1
  2. {domd-2.2.30 → domd-2.2.32}/pyproject.toml +1 -1
  3. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/persistence/shell_command_executor.py +112 -28
  4. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/handlers/command_handler.py +161 -37
  5. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/handlers/config_handler.py +2 -2
  6. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/parsers/base_parser.py +1 -1
  7. {domd-2.2.30 → domd-2.2.32}/LICENSE +0 -0
  8. {domd-2.2.30 → domd-2.2.32}/README.md +0 -0
  9. {domd-2.2.30 → domd-2.2.32}/src/domd/__init__.py +0 -0
  10. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/__init__.py +0 -0
  11. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/api/__init__.py +0 -0
  12. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/api/flask_api.py +0 -0
  13. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/cli/__init__.py +0 -0
  14. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/cli/command_presenter.py +0 -0
  15. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/formatters/__init__.py +0 -0
  16. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/formatters/markdown_formatter.py +0 -0
  17. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/persistence/__init__.py +0 -0
  18. {domd-2.2.30 → domd-2.2.32}/src/domd/adapters/persistence/in_memory_command_repository.py +0 -0
  19. {domd-2.2.30 → domd-2.2.32}/src/domd/api.py +0 -0
  20. {domd-2.2.30 → domd-2.2.32}/src/domd/application/__init__.py +0 -0
  21. {domd-2.2.30 → domd-2.2.32}/src/domd/application/factory.py +0 -0
  22. {domd-2.2.30 → domd-2.2.32}/src/domd/cli.py +0 -0
  23. {domd-2.2.30 → domd-2.2.32}/src/domd/command_execution/__init__.py +0 -0
  24. {domd-2.2.30 → domd-2.2.32}/src/domd/core/__init__.py +0 -0
  25. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/__init__.py +0 -0
  26. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/base_detector.py +0 -0
  27. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/handlers/__init__.py +0 -0
  28. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/models.py +0 -0
  29. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/parsers/__init__.py +0 -0
  30. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/parsers/parser_registry.py +0 -0
  31. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/project_detector.py +0 -0
  32. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/utils/__init__.py +0 -0
  33. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/utils/file_utils.py +0 -0
  34. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_detection/utils/virtualenv.py +0 -0
  35. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_execution/__init__.py +0 -0
  36. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_execution/command_executor.py +0 -0
  37. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_execution/command_recorder.py +0 -0
  38. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_execution/command_runner.py +0 -0
  39. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_execution/environment_detector.py +0 -0
  40. {domd-2.2.30 → domd-2.2.32}/src/domd/core/command_execution/executor.py +0 -0
  41. {domd-2.2.30 → domd-2.2.32}/src/domd/core/commands/__init__.py +0 -0
  42. {domd-2.2.30 → domd-2.2.32}/src/domd/core/commands/command.py +0 -0
  43. {domd-2.2.30 → domd-2.2.32}/src/domd/core/commands/executor.py +0 -0
  44. {domd-2.2.30 → domd-2.2.32}/src/domd/core/detector.py +0 -0
  45. {domd-2.2.30 → domd-2.2.32}/src/domd/core/domain/__init__.py +0 -0
  46. {domd-2.2.30 → domd-2.2.32}/src/domd/core/domain/command.py +0 -0
  47. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/__init__.py +0 -0
  48. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/ansible_galaxy.py +0 -0
  49. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/ansible_inventory.py +0 -0
  50. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/ansible_playbook.py +0 -0
  51. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/ansible_role.py +0 -0
  52. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/ansible_vault.py +0 -0
  53. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/base.py +0 -0
  54. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/cargo_toml.py +0 -0
  55. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/composer_json.py +0 -0
  56. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/go_mod.py +0 -0
  57. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/makefile.py +0 -0
  58. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/package_json.py +0 -0
  59. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/pyproject_toml.py +0 -0
  60. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsers/tox_ini.py +0 -0
  61. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsing/__init__.py +0 -0
  62. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsing/base.py +0 -0
  63. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsing/file_processor.py +0 -0
  64. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsing/parser_registry.py +0 -0
  65. {domd-2.2.30 → domd-2.2.32}/src/domd/core/parsing/pattern_matcher.py +0 -0
  66. {domd-2.2.30 → domd-2.2.32}/src/domd/core/ports/__init__.py +0 -0
  67. {domd-2.2.30 → domd-2.2.32}/src/domd/core/ports/command_executor.py +0 -0
  68. {domd-2.2.30 → domd-2.2.32}/src/domd/core/ports/command_repository.py +0 -0
  69. {domd-2.2.30 → domd-2.2.32}/src/domd/core/ports/report_formatter.py +0 -0
  70. {domd-2.2.30 → domd-2.2.32}/src/domd/core/project_detection/__init__.py +0 -0
  71. {domd-2.2.30 → domd-2.2.32}/src/domd/core/project_detection/command_handling.py +0 -0
  72. {domd-2.2.30 → domd-2.2.32}/src/domd/core/project_detection/config_files.py +0 -0
  73. {domd-2.2.30 → domd-2.2.32}/src/domd/core/project_detection/detector.py +0 -0
  74. {domd-2.2.30 → domd-2.2.32}/src/domd/core/project_detection/virtualenv.py +0 -0
  75. {domd-2.2.30 → domd-2.2.32}/src/domd/core/reporters/__init__.py +0 -0
  76. {domd-2.2.30 → domd-2.2.32}/src/domd/core/reporters/base.py +0 -0
  77. {domd-2.2.30 → domd-2.2.32}/src/domd/core/reporters/done_md.py +0 -0
  78. {domd-2.2.30 → domd-2.2.32}/src/domd/core/reporters/todo_md.py +0 -0
  79. {domd-2.2.30 → domd-2.2.32}/src/domd/core/reporting/__init__.py +0 -0
  80. {domd-2.2.30 → domd-2.2.32}/src/domd/core/reporting/formatters.py +0 -0
  81. {domd-2.2.30 → domd-2.2.32}/src/domd/core/reporting/reporter.py +0 -0
  82. {domd-2.2.30 → domd-2.2.32}/src/domd/core/services/__init__.py +0 -0
  83. {domd-2.2.30 → domd-2.2.32}/src/domd/core/services/command_service.py +0 -0
  84. {domd-2.2.30 → domd-2.2.32}/src/domd/core/services/report_service.py +0 -0
  85. {domd-2.2.30 → domd-2.2.32}/src/domd/core/utils/__init__.py +0 -0
  86. {domd-2.2.30 → domd-2.2.32}/src/domd/core/utils/command_utils.py +0 -0
  87. {domd-2.2.30 → domd-2.2.32}/src/domd/core/utils/environment.py +0 -0
  88. {domd-2.2.30 → domd-2.2.32}/src/domd/core/utils/file_utils.py +0 -0
  89. {domd-2.2.30 → domd-2.2.32}/src/domd/core/utils/logging_utils.py +0 -0
  90. {domd-2.2.30 → domd-2.2.32}/src/domd/core/utils/virtualenv.py +0 -0
  91. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/__init__.py +0 -0
  92. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/ansible.py +0 -0
  93. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/base.py +0 -0
  94. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/build_systems.py +0 -0
  95. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/ci_cd.py +0 -0
  96. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/docker/__init__.py +0 -0
  97. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/docker/docker_compose.py +0 -0
  98. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/docker/dockerfile.py +0 -0
  99. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/docker.py +0 -0
  100. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/javascript/__init__.py +0 -0
  101. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/javascript.py +0 -0
  102. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/markdown_parser.py +0 -0
  103. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/package_json.py +0 -0
  104. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/python.py +0 -0
  105. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/test_build_systems.py +0 -0
  106. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/test_ci_cd.py +0 -0
  107. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/test_docker.py +0 -0
  108. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/test_javascript.py +0 -0
  109. {domd-2.2.30 → domd-2.2.32}/src/domd/parsers/test_python.py +0 -0
  110. {domd-2.2.30 → domd-2.2.32}/src/domd/parsing/__init__.py +0 -0
  111. {domd-2.2.30 → domd-2.2.32}/src/domd/parsing/base.py +0 -0
  112. {domd-2.2.30 → domd-2.2.32}/src/domd/reporters/__init__.py +0 -0
  113. {domd-2.2.30 → domd-2.2.32}/src/domd/reporters/console.py +0 -0
  114. {domd-2.2.30 → domd-2.2.32}/src/domd/reporters/json_reporter.py +0 -0
  115. {domd-2.2.30 → domd-2.2.32}/src/domd/reporters/test_console.py +0 -0
  116. {domd-2.2.30 → domd-2.2.32}/src/domd/reporters/test_json_reporter.py +0 -0
  117. {domd-2.2.30 → domd-2.2.32}/src/domd/reporters/test_todo_md.py +0 -0
  118. {domd-2.2.30 → domd-2.2.32}/src/domd/reporters/todo_md.py +0 -0
  119. {domd-2.2.30 → domd-2.2.32}/src/domd/reporting/__init__.py +0 -0
  120. {domd-2.2.30 → domd-2.2.32}/src/domd/utils/__init__.py +0 -0
  121. {domd-2.2.30 → domd-2.2.32}/src/domd/utils/command_runner.py +0 -0
  122. {domd-2.2.30 → domd-2.2.32}/src/domd/utils/file_utils.py +0 -0
  123. {domd-2.2.30 → domd-2.2.32}/src/domd/utils/path_utils.py +0 -0
  124. {domd-2.2.30 → domd-2.2.32}/src/domd/utils/test_command_runner.py +0 -0
  125. {domd-2.2.30 → domd-2.2.32}/src/domd/utils/test_file_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: domd
3
- Version: 2.2.30
3
+ Version: 2.2.32
4
4
  Summary: ✓ DoMD - Do Markdown Docs - automatically detect, run and show working commands in DONE.md and errors in TODO.md markdown files
5
5
  License: Apache-2.0
6
6
  Author: Tom Sapletta
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "domd"
3
- version = "2.2.30"
3
+ version = "2.2.32"
4
4
  description = " ✓ DoMD - Do Markdown Docs - automatically detect, run and show working commands in DONE.md and errors in TODO.md markdown files"
5
5
  authors = ["Tom Sapletta <info@softreck.dev>"]
6
6
  maintainers = ["WRONAI Team"]
@@ -141,6 +141,17 @@ class ShellCommandExecutor(CommandExecutor):
141
141
 
142
142
  cmd_args, needs_shell = self._parse_command(cmd_str)
143
143
 
144
+ # If cmd_args is empty, this is markdown content that should be ignored
145
+ if not cmd_args:
146
+ logger.debug(f"Skipping markdown content: {cmd_str[:100]}...")
147
+ return CommandResult(
148
+ success=True,
149
+ return_code=0,
150
+ execution_time=0,
151
+ stdout="",
152
+ stderr="",
153
+ )
154
+
144
155
  # Prepare execution environment
145
156
  execution_env = None
146
157
  if env:
@@ -282,44 +293,111 @@ class ShellCommandExecutor(CommandExecutor):
282
293
 
283
294
  def _is_markdown_content(self, command_str: str) -> bool:
284
295
  """
285
- Check if the given string appears to be markdown content.
296
+ Check if the given string appears to be markdown or other non-command content.
286
297
 
287
298
  Args:
288
299
  command_str: The string to check
289
300
 
290
301
  Returns:
291
- bool: True if the string appears to be markdown content
302
+ bool: True if the string appears to be non-command content
292
303
  """
304
+ # Skip empty strings
305
+ if not command_str or not command_str.strip():
306
+ return True
307
+
293
308
  # Common markdown patterns that shouldn't be executed as commands
294
- markdown_indicators = [
309
+ markdown_patterns = [
295
310
  # Headers
296
- r"^#+\s",
311
+ (r"^#+\s+", "Markdown header"),
297
312
  # Lists
298
- r"^\s*[-*+]\s+",
299
- r"^\s*\d+\.\s+",
313
+ (r"^[-*+]\s+", "Markdown list item"),
314
+ (r"^\d+\.\s+", "Numbered list item"),
300
315
  # Code blocks
301
- r"^\s*```",
316
+ (r"^```", "Markdown code block"),
317
+ (r"`[^`]+`", "Inline code"),
302
318
  # Tables
303
- r"\|.*\|",
319
+ (r"^\|.*\|$", "Markdown table"),
304
320
  # Links and images
305
- r"\[.*\]\(.*\)",
321
+ (r"\[.*\]\(.*\)", "Markdown link"),
322
+ (r"!\[.*\]\(.*\)", "Markdown image"),
323
+ # Text formatting
324
+ (r"\*\*[^*]+\*\*", "Bold text"),
325
+ (r"__[^_]+__", "Underlined text"),
326
+ (r"~~[^~]+~~", "Strikethrough"),
327
+ (r"\*[^*]+\*", "Italic text"),
328
+ (r"_[^_]+_", "Italic text (underscore)"),
306
329
  # Blockquotes
307
- r"^>",
330
+ (r"^>\s+", "Blockquote"),
308
331
  # Horizontal rules
309
- r"^[-*_]{3,}$",
310
- # Box-drawing characters (used in tree views)
311
- r"[├└│]",
312
- # Common markdown file extensions
313
- r"\.md$",
332
+ (r"^\s*[*_-]{3,}\s*$", "Horizontal rule"),
333
+ # HTML tags
334
+ (r"<[a-z][\s\S]*?>", "HTML tag"),
335
+ # Common documentation patterns
336
+ (r"^\s*[A-Z][A-Za-z0-9_\s-]+:", "Documentation line"),
337
+ (r"^\s*<!--.*-->\s*$", "HTML comment"),
338
+ (r"^\s*//", "Single-line comment"),
339
+ (r"^\s*#", "Comment"),
340
+ (r"^\s*/\*[\s\S]*?\*/\s*$", "Multi-line comment"),
341
+ # Common metadata patterns
342
+ (r"^\s*[\w-]+\s*:\s*.+", "YAML/JSON key-value pair"),
343
+ # Directory listing patterns
344
+ (r"^[│└├─]", "Tree-like directory structure"),
345
+ (r"\s+\d+\s+\w+\s+\d+\s+[\d:]+\s+", "File size and date"),
346
+ (r"\d+\s+[BKMGTPEZY]B\s*$", "File size"),
347
+ (r"^total \d+$", "Directory total"),
348
+ # Common documentation sections
349
+ (
350
+ r"^\s*(Installation|Usage|Configuration|Options|Examples):",
351
+ "Documentation section",
352
+ ),
353
+ # Empty or whitespace-only lines
354
+ (r"^\s*$", "Empty line"),
314
355
  ]
315
356
 
316
- # Check for markdown indicators
317
- for pattern in markdown_indicators:
318
- if re.search(pattern, command_str):
357
+ # Check for markdown and other non-command patterns
358
+ for pattern, description in markdown_patterns:
359
+ if re.search(pattern, command_str, re.MULTILINE):
360
+ logger.debug(f"Detected {description}: {command_str[:100]}")
319
361
  return True
320
362
 
321
- # Check if this looks like a documentation line (e.g., starts with a word and colon)
322
- if re.match(r"^\s*[A-Za-z][A-Za-z0-9_\s-]*:", command_str):
363
+ # Check for suspiciously long lines that aren't commands
364
+ if len(command_str) > 1000 and not any(
365
+ c in command_str for c in ["&&", "|", ">", "<", ";"]
366
+ ):
367
+ logger.debug(f"Suspiciously long line detected: {command_str[:100]}...")
368
+ return True
369
+
370
+ # Check for lines that are just special characters or numbers
371
+ if re.match(r"^[\d\s\W]+$", command_str):
372
+ logger.debug(
373
+ f"Line contains only numbers/special chars: {command_str[:100]}"
374
+ )
375
+ return True
376
+
377
+ # Check for common error messages or stack traces
378
+ error_indicators = [
379
+ "error:",
380
+ "warning:",
381
+ "exception:",
382
+ "traceback",
383
+ "stacktrace",
384
+ "at ",
385
+ 'File "',
386
+ r"line \d+", # Raw string for regex
387
+ "in <module>",
388
+ "^",
389
+ "~",
390
+ "SyntaxError",
391
+ "NameError",
392
+ "TypeError",
393
+ "ValueError",
394
+ "ImportError",
395
+ ]
396
+
397
+ if any(
398
+ indicator.lower() in command_str.lower() for indicator in error_indicators
399
+ ):
400
+ logger.debug(f"Error/warning message detected: {command_str[:100]}...")
323
401
  return True
324
402
 
325
403
  return False
@@ -334,21 +412,23 @@ class ShellCommandExecutor(CommandExecutor):
334
412
  Returns:
335
413
  Tuple of (command_args, needs_shell)
336
414
  """
337
- if not command_str.strip():
338
- return [], False
415
+ # Handle empty or whitespace-only commands
416
+ if not command_str or not command_str.strip():
417
+ logger.debug("Empty or whitespace-only command")
418
+ return [""], False # Return a non-empty list to avoid IndexError
419
+
420
+ command_str = command_str.strip()
339
421
 
340
422
  # Check if this is markdown content that shouldn't be executed
341
423
  if self._is_markdown_content(command_str):
342
424
  logger.debug(f"Ignoring markdown content: {command_str[:100]}...")
343
- return [], False
425
+ return [""], False # Return a non-empty list to avoid IndexError
344
426
 
345
427
  # Check if this is a shell built-in or needs shell features
346
428
  needs_shell = self._needs_shell(command_str)
347
429
 
348
430
  # Special handling for 'source' command
349
- if command_str.strip().startswith("source ") or command_str.strip().startswith(
350
- ". "
351
- ):
431
+ if command_str.startswith("source ") or command_str.startswith(". "):
352
432
  # For 'source' command, we need to use the shell to execute it
353
433
  return [command_str], True
354
434
 
@@ -358,9 +438,13 @@ class ShellCommandExecutor(CommandExecutor):
358
438
 
359
439
  # For non-shell commands, use shlex for proper argument splitting
360
440
  try:
361
- return shlex.split(command_str), False
441
+ args = shlex.split(command_str)
442
+ if not args: # Handle case where shlex.split returns empty list
443
+ return [""], False
444
+ return args, False
362
445
  except Exception as e:
363
446
  logger.warning(
364
447
  f"Error parsing command with shlex, falling back to simple split: {e}"
365
448
  )
366
- return command_str.split(), False
449
+ args = command_str.split()
450
+ return (args if args else [""]), False
@@ -6,7 +6,8 @@ import time
6
6
  from pathlib import Path
7
7
  from typing import Any, Dict, List, Optional, Union
8
8
 
9
- from ...services.command_runner import CommandRunner
9
+ from domd.core.command_execution.command_runner import CommandRunner
10
+
10
11
  from ..models import Command
11
12
 
12
13
  logger = logging.getLogger(__name__)
@@ -162,63 +163,186 @@ class CommandHandler:
162
163
  def is_valid_command(self, command: Union[str, Dict, Command]) -> tuple[bool, str]:
163
164
  """Check if a command is valid and should be executed.
164
165
 
166
+ This method performs multiple validations to ensure the command is a valid shell command
167
+ and not markdown, documentation, or other non-command content.
168
+
165
169
  Args:
166
170
  command: Command to validate (string, dict, or Command object)
167
171
 
168
172
  Returns:
169
173
  Tuple of (is_valid, reason) where is_valid is a boolean and reason is a string
170
174
  """
175
+ logger.debug(f"Validating command: {command}")
176
+
171
177
  cmd_str = self._extract_command_string(command)
172
178
  if not cmd_str or not cmd_str.strip():
179
+ logger.debug("Empty command string")
180
+ return False, "Empty command"
181
+
182
+ cmd_str = cmd_str.strip()
183
+ logger.debug(
184
+ f"Processing command: {cmd_str[:100]}"
185
+ + ("..." if len(cmd_str) > 100 else "")
186
+ )
187
+
188
+ # Check for empty or whitespace-only commands
189
+ if not cmd_str:
190
+ logger.debug("Command is empty after stripping")
173
191
  return False, "Empty command"
174
192
 
175
- # Check if the command looks like a markdown header, list item, or other content
176
- if any(markdown_indicator in cmd_str for markdown_indicator in [
177
- "# ", # Headers
178
- "- ", # List items
179
- "* ", # List items
180
- "1. ", # Numbered lists
181
- "| ", # Tables
182
- "```", # Code blocks
183
- "**", # Bold text
184
- "__", # Underlined text
185
- "~~", # Strikethrough
186
- "[]", # Links
187
- "![", # Images
188
- "<", # HTML tags
189
- "> " # Blockquotes
190
- ]):
191
- return False, "Appears to be markdown content"
192
-
193
- # Check against non-command patterns
193
+ # Check for very long commands (likely not actual commands)
194
+ if len(cmd_str) > 500:
195
+ logger.debug(f"Command too long ({len(cmd_str)} > 500 characters)")
196
+ return False, "Command is too long to be a valid shell command"
197
+
198
+ # Check for commands that are just numbers or special characters
199
+ if re.match(r"^[\d\s\W]+$", cmd_str):
200
+ logger.debug("Command contains only numbers or special characters")
201
+ return False, "Command contains only numbers or special characters"
202
+
203
+ # Check for common non-command patterns first (fast checks)
194
204
  for pattern in self._compiled_non_command_patterns:
195
205
  if pattern.search(cmd_str):
206
+ logger.debug(f"Matches non-command pattern: {pattern.pattern}")
196
207
  return False, f"Matches non-command pattern: {pattern.pattern}"
197
208
 
198
- # Check for common error patterns
199
- if any(s in cmd_str.lower() for s in ["error:", "warning:", "exception:", "traceback"]):
200
- return False, "Appears to be an error or warning message"
209
+ # Enhanced markdown and documentation detection with detailed logging
210
+ markdown_patterns = [
211
+ # Markdown patterns
212
+ (r"^#+\s+", "Markdown header"),
213
+ (r"^[-*+]\s+", "Markdown list item"),
214
+ (r"^\d+\.\s+", "Numbered list item"),
215
+ (r"^\|.*\|$", "Markdown table"),
216
+ (r"^```", "Markdown code block"),
217
+ (r"`[^`]+`", "Inline code"),
218
+ (r"\*\*[^*]+\*\*", "Bold text"),
219
+ (r"__[^_]+__", "Underlined text"),
220
+ (r"~~[^~]+~~", "Strikethrough"),
221
+ (r"\[.*\]\(.*\)", "Markdown link"),
222
+ (r"^>\s+", "Blockquote"),
223
+ (r"^\s*<!--.*-->\s*$", "HTML comment"),
224
+ # Documentation patterns
225
+ (r"^For\s+\w+\s+information", "Documentation line"),
226
+ (r"^To\s+\w+", "Documentation line"),
227
+ (r"^This\s+\w+", "Documentation line"),
228
+ (r"^The\s+\w+", "Documentation line"),
229
+ (r"^[A-Z][a-z]+\s+the\s+\w+", "Documentation line"),
230
+ (r"^[A-Z][a-z]+\s+[A-Z][a-z]+", "Documentation line"),
231
+ (r"^[A-Z][a-z]+\s+[a-z]+\s+[a-z]+", "Documentation line"),
232
+ # Directory tree patterns
233
+ (r"^\s*[│├└─]+\s+", "Directory tree"),
234
+ (r"^\s*[│├└─]+$", "Directory tree connector"),
235
+ (r"^\s*\d+\s+[a-z]+\s+\d+\s+\d{2}:\d{2}\s+", "Directory listing"),
236
+ ]
201
237
 
202
- # Check for documentation-style lines (e.g., "parameter: value")
203
- if re.match(r'^\s*[A-Za-z][A-Za-z0-9_\s-]*:', cmd_str):
204
- return False, "Appears to be documentation or configuration"
238
+ logger.debug(f"Checking against {len(markdown_patterns)} markdown patterns")
239
+
240
+ # Additional patterns to check with detailed logging
241
+ additional_patterns = [
242
+ # Common documentation phrases
243
+ (r"(?i)for more information", "Documentation phrase"),
244
+ (r"(?i)see also", "Documentation phrase"),
245
+ (r"(?i)example:", "Documentation phrase"),
246
+ (r"(?i)note:", "Documentation note"),
247
+ (r"(?i)warning:", "Warning message"),
248
+ (r"(?i)important:", "Important note"),
249
+ (r"(?i)tip:", "Tip note"),
250
+ (r"(?i)caution:", "Caution note"),
251
+ (r"(?i)see the", "Documentation reference"),
252
+ (r"(?i)refer to", "Documentation reference"),
253
+ # Common command-like patterns that should be ignored
254
+ (r"^\s*\w+\s*=\s*\S+\s*$", "Variable assignment"),
255
+ (r"^\s*\{\s*\}\s*$", "Empty code block"),
256
+ (r"^\s*\[\s*\]\s*$", "Empty array"),
257
+ (r"^\s*\{\s*$", "Opening brace"),
258
+ (r"^\s*\}\s*$", "Closing brace"),
259
+ # Common error patterns
260
+ (r"(?i)error:", "Error message"),
261
+ (r"(?i)exception:", "Exception message"),
262
+ (r"(?i)traceback", "Traceback message"),
263
+ (r"(?i)stacktrace", "Stack trace"),
264
+ # Path patterns
265
+ (r"^\s*/[\w/.-]+$", "File path"),
266
+ (r"^\s*~?/[\w/.-]+$", "Home-relative path"),
267
+ (r"^\s*\./[\w/.-]+$", "Relative path"),
268
+ # URL patterns
269
+ (r"https?://\S+", "URL"),
270
+ (r"www\.\S+\.\w+", "Web address"),
271
+ # Email patterns
272
+ (r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", "Email address"),
273
+ ]
205
274
 
206
- # Check for suspicious patterns that might indicate internal errors
207
- if any(s in cmd_str.lower() for s in ["traceback", "exception", "stacktrace"]):
208
- return False, "Appears to contain error output"
275
+ logger.debug(f"Checking against {len(additional_patterns)} additional patterns")
209
276
 
210
- # Check for internal tool paths that shouldn't be executed
211
- if any(s in cmd_str for s in ["/tmp/", "/var/", "/usr/local/", "~/.cache/", "/dev/"]):
212
- return False, "References internal tool paths"
277
+ # Combine all patterns
278
+ all_patterns = markdown_patterns + additional_patterns
279
+ logger.debug(f"Total patterns to check: {len(all_patterns)}")
213
280
 
214
- # Check for commands that are just numbers or special characters
215
- if re.match(r'^[\d\s\W]+$', cmd_str):
216
- return False, "Contains only numbers or special characters"
281
+ # Check all patterns with detailed logging
282
+ for pattern, description in all_patterns:
283
+ try:
284
+ if re.search(pattern, cmd_str, re.IGNORECASE):
285
+ logger.debug(
286
+ f"Pattern matched - Type: {description}, Pattern: {pattern}"
287
+ )
288
+ return False, f"{description} detected"
289
+ except Exception as e:
290
+ logger.error(f"Error checking pattern '{pattern}': {str(e)}")
291
+ continue
292
+
293
+ logger.debug("No markdown or documentation patterns matched")
294
+
295
+ # Check for command-like patterns that might be valid
296
+ valid_command_indicators = [
297
+ (r"^\s*[a-z][a-z0-9_-]+(\s+[a-z][a-z0-9_-]+)*\s*$", "Simple command"),
298
+ (r"^\s*[a-z][a-z0-9_-]+\s+[a-z][a-z0-9_-]+", "Command with argument"),
299
+ (r"^\s*[a-z][a-z0-9_-]+\s+--?[a-z0-9-]+", "Command with option"),
300
+ (
301
+ r"^\s*[a-z][a-z0-9_-]+\s+[a-z][a-z0-9_-]+\s+[a-z][a-z0-9_-]+",
302
+ "Command with multiple args",
303
+ ),
304
+ ]
305
+
306
+ logger.debug("Checking command against valid command patterns")
307
+
308
+ # Track if any command pattern matches
309
+ command_matched = False
310
+ for pattern, pattern_type in valid_command_indicators:
311
+ try:
312
+ if re.match(pattern, cmd_str, re.IGNORECASE):
313
+ logger.debug(f"Command matches {pattern_type} pattern: {pattern}")
314
+ command_matched = True
315
+ break
316
+ except Exception as e:
317
+ logger.error(f"Error checking command pattern '{pattern}': {str(e)}")
318
+
319
+ if not command_matched:
320
+ logger.debug("Command does not match any valid command patterns")
321
+ return False, "Does not match valid command patterns"
322
+
323
+ # Check for commands that are too short or too simple
324
+ if len(cmd_str.split()) == 1 and len(cmd_str) < 3:
325
+ logger.debug("Command is too short or simple")
326
+ return False, "Command is too short or simple"
217
327
 
218
- # Check for very long commands that are likely not actual commands
219
- if len(cmd_str) > 500: # Arbitrary length threshold
328
+ # Check for commands that are too long (likely not actual commands)
329
+ if len(cmd_str) > 500:
330
+ logger.debug(f"Command is too long ({len(cmd_str)} characters)")
220
331
  return False, "Command is too long to be a valid shell command"
221
332
 
333
+ # Check for commands that are just numbers or special characters
334
+ if re.match(r"^[\d\s\W]+$", cmd_str):
335
+ logger.debug("Command contains only numbers or special characters")
336
+ return False, "Command contains only numbers or special characters"
337
+
338
+ # Check for internal tool paths that shouldn't be executed
339
+ internal_paths = ["/tmp/", "/var/", "/usr/local/", "~/.cache/", "/dev/"]
340
+ for path in internal_paths:
341
+ if path in cmd_str:
342
+ logger.debug(f"Command references internal tool path: {path}")
343
+ return False, f"References internal tool path: {path}"
344
+
345
+ logger.debug("Command passed all validation checks")
222
346
  return True, ""
223
347
 
224
348
  def should_ignore_command(self, command: Union[str, Dict, Command]) -> bool:
@@ -4,8 +4,8 @@ import logging
4
4
  from pathlib import Path
5
5
  from typing import Any, List, Optional, Set, Union
6
6
 
7
- from ...services.file_processor import FileProcessor
8
- from ...services.pattern_matcher import PatternMatcher
7
+ from domd.core.parsing.file_processor import FileProcessor
8
+ from domd.core.parsing.pattern_matcher import PatternMatcher
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
@@ -5,7 +5,7 @@ import logging
5
5
  from pathlib import Path
6
6
  from typing import List, Optional, Set, Union
7
7
 
8
- from ...models import Command
8
+ from ..models import Command
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes