tapps-agents 3.5.38__py3-none-any.whl → 3.5.40__py3-none-any.whl

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 (75) hide show
  1. tapps_agents/__init__.py +2 -2
  2. tapps_agents/agents/cleanup/__init__.py +7 -0
  3. tapps_agents/agents/cleanup/agent.py +445 -0
  4. tapps_agents/agents/enhancer/agent.py +2 -2
  5. tapps_agents/agents/implementer/agent.py +35 -13
  6. tapps_agents/agents/reviewer/agent.py +43 -10
  7. tapps_agents/agents/reviewer/scoring.py +59 -68
  8. tapps_agents/agents/reviewer/tools/__init__.py +24 -0
  9. tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -0
  10. tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -0
  11. tapps_agents/beads/__init__.py +11 -0
  12. tapps_agents/beads/hydration.py +213 -0
  13. tapps_agents/beads/specs.py +206 -0
  14. tapps_agents/cli/commands/cleanup_agent.py +92 -0
  15. tapps_agents/cli/commands/health.py +19 -3
  16. tapps_agents/cli/commands/simple_mode.py +842 -676
  17. tapps_agents/cli/commands/task.py +219 -0
  18. tapps_agents/cli/commands/top_level.py +13 -0
  19. tapps_agents/cli/main.py +15 -2
  20. tapps_agents/cli/parsers/cleanup_agent.py +228 -0
  21. tapps_agents/cli/parsers/top_level.py +1978 -1881
  22. tapps_agents/core/config.py +43 -0
  23. tapps_agents/core/init_project.py +3012 -2896
  24. tapps_agents/epic/markdown_sync.py +105 -0
  25. tapps_agents/epic/orchestrator.py +1 -2
  26. tapps_agents/epic/parser.py +427 -423
  27. tapps_agents/experts/adaptive_domain_detector.py +0 -2
  28. tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +15 -15
  29. tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +19 -44
  30. tapps_agents/health/checks/outcomes.backup_20260204_064058.py +324 -0
  31. tapps_agents/health/checks/outcomes.backup_20260204_064256.py +324 -0
  32. tapps_agents/health/checks/outcomes.backup_20260204_064600.py +324 -0
  33. tapps_agents/health/checks/outcomes.py +134 -46
  34. tapps_agents/health/orchestrator.py +12 -4
  35. tapps_agents/hooks/__init__.py +33 -0
  36. tapps_agents/hooks/config.py +140 -0
  37. tapps_agents/hooks/events.py +135 -0
  38. tapps_agents/hooks/executor.py +128 -0
  39. tapps_agents/hooks/manager.py +143 -0
  40. tapps_agents/session/__init__.py +19 -0
  41. tapps_agents/session/manager.py +256 -0
  42. tapps_agents/simple_mode/code_snippet_handler.py +382 -0
  43. tapps_agents/simple_mode/intent_parser.py +29 -4
  44. tapps_agents/simple_mode/orchestrators/base.py +185 -59
  45. tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2667 -2642
  46. tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
  47. tapps_agents/simple_mode/workflow_suggester.py +37 -3
  48. tapps_agents/workflow/agent_handlers/implementer_handler.py +18 -3
  49. tapps_agents/workflow/cursor_executor.py +2196 -2118
  50. tapps_agents/workflow/direct_execution_fallback.py +16 -3
  51. tapps_agents/workflow/enforcer.py +36 -23
  52. tapps_agents/workflow/message_formatter.py +188 -0
  53. tapps_agents/workflow/parallel_executor.py +43 -4
  54. tapps_agents/workflow/parser.py +375 -357
  55. tapps_agents/workflow/rules_generator.py +337 -331
  56. tapps_agents/workflow/skill_invoker.py +9 -3
  57. {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/METADATA +9 -5
  58. {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/RECORD +62 -53
  59. tapps_agents/agents/analyst/SKILL.md +0 -85
  60. tapps_agents/agents/architect/SKILL.md +0 -80
  61. tapps_agents/agents/debugger/SKILL.md +0 -66
  62. tapps_agents/agents/designer/SKILL.md +0 -78
  63. tapps_agents/agents/documenter/SKILL.md +0 -95
  64. tapps_agents/agents/enhancer/SKILL.md +0 -189
  65. tapps_agents/agents/implementer/SKILL.md +0 -117
  66. tapps_agents/agents/improver/SKILL.md +0 -55
  67. tapps_agents/agents/ops/SKILL.md +0 -64
  68. tapps_agents/agents/orchestrator/SKILL.md +0 -238
  69. tapps_agents/agents/planner/story_template.md +0 -37
  70. tapps_agents/agents/reviewer/templates/quality-dashboard.html.j2 +0 -150
  71. tapps_agents/agents/tester/SKILL.md +0 -71
  72. {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/WHEEL +0 -0
  73. {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/entry_points.txt +0 -0
  74. {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/licenses/LICENSE +0 -0
  75. {tapps_agents-3.5.38.dist-info → tapps_agents-3.5.40.dist-info}/top_level.txt +0 -0
@@ -257,8 +257,15 @@ class DirectExecutionFallback:
257
257
  if skill_command.startswith("@"):
258
258
  skill_command = skill_command[1:]
259
259
 
260
- # Split into parts
261
- parts = skill_command.split()
260
+ # Split into parts, preserving quoted strings
261
+ # Use shlex.split() to handle complex quotes properly
262
+ # Use posix=True to properly handle quote removal (even on Windows)
263
+ try:
264
+ parts = shlex.split(skill_command, posix=True)
265
+ except ValueError as e:
266
+ # Provide helpful error message with truncated command
267
+ cmd_preview = skill_command[:200] + "..." if len(skill_command) > 200 else skill_command
268
+ raise ValueError(f"Invalid skill command (malformed quotes): {cmd_preview}") from e
262
269
 
263
270
  if not parts:
264
271
  raise ValueError(f"Invalid skill command: {skill_command}")
@@ -282,7 +289,13 @@ class DirectExecutionFallback:
282
289
  if command_name:
283
290
  cli_parts.append(command_name)
284
291
 
285
- cli_parts.extend(remaining_args)
292
+ # Safely quote arguments that may contain spaces or special characters
293
+ for arg in remaining_args:
294
+ # Check if arg needs quoting (contains spaces, quotes, or special chars)
295
+ if ' ' in arg or '"' in arg or "'" in arg:
296
+ cli_parts.append(shlex.quote(arg))
297
+ else:
298
+ cli_parts.append(arg)
286
299
 
287
300
  return " ".join(cli_parts)
288
301
 
@@ -22,6 +22,8 @@ from pathlib import Path
22
22
  from typing import Literal, TypedDict
23
23
 
24
24
  from tapps_agents.core.llm_behavior import EnforcementConfig
25
+ from tapps_agents.workflow.intent_detector import IntentDetector, WorkflowType
26
+ from tapps_agents.workflow.message_formatter import MessageConfig, MessageFormatter
25
27
 
26
28
  logger = logging.getLogger(__name__)
27
29
 
@@ -140,6 +142,14 @@ class WorkflowEnforcer:
140
142
  # Load config from file
141
143
  self.config = self._load_config(config_path)
142
144
 
145
+ # Initialize intent detector and message formatter (ENH-001-S2, S3)
146
+ self._intent_detector = IntentDetector()
147
+ self._message_formatter = MessageFormatter(MessageConfig(
148
+ use_emoji=True,
149
+ show_benefits=self.config.suggest_workflows,
150
+ show_override=True,
151
+ ))
152
+
143
153
  logger.info(
144
154
  f"WorkflowEnforcer initialized with mode={self.config.mode}, "
145
155
  f"confidence_threshold={self.config.confidence_threshold}"
@@ -322,55 +332,58 @@ class WorkflowEnforcer:
322
332
  - allow: Empty message
323
333
 
324
334
  Note:
325
- Story 1 uses basic message templates.
326
- Story 3 will add MessageFormatter for rich, context-aware messages.
335
+ Uses MessageFormatter (ENH-001-S3) for rich, context-aware messages.
336
+ Uses IntentDetector (ENH-001-S2) for workflow detection and confidence.
327
337
  """
328
338
  # Determine should_block flag
329
339
  should_block = action == "block" and self.config.block_direct_edits
330
340
 
331
- # Generate message based on action
341
+ # Detect intent and get confidence (ENH-001-S2 integration)
342
+ detection_result = self._intent_detector.detect_workflow(
343
+ user_intent=user_intent,
344
+ file_path=file_path if file_path.exists() else None,
345
+ )
346
+ workflow = detection_result.workflow_type
347
+ confidence = detection_result.confidence
348
+
349
+ # Generate message using MessageFormatter (ENH-001-S3 integration)
332
350
  if action == "block":
333
351
  if self.config.suggest_workflows:
334
- message = (
335
- f"⚠️ Direct file edit blocked: {file_path}\n\n"
336
- f"TappsCodingAgents workflows provide:\n"
337
- f" • Automatic testing (80%+ coverage)\n"
338
- f" • Quality gates (75+ score required)\n"
339
- f" • Comprehensive documentation\n"
340
- f" • Early bug detection\n\n"
341
- f"Suggested workflow:\n"
342
- f" @simple-mode *build \"{user_intent or 'Implement feature'}\"\n\n"
343
- f"To bypass enforcement, use: --skip-enforcement flag"
352
+ message = self._message_formatter.format_blocking_message(
353
+ workflow=workflow,
354
+ user_intent=user_intent,
355
+ file_path=file_path,
356
+ confidence=confidence,
344
357
  )
345
358
  else:
346
359
  message = (
347
- f"⚠️ Direct file edit blocked: {file_path}\n"
360
+ f"Direct file edit blocked: {file_path}\n"
348
361
  f"Use --skip-enforcement flag to bypass."
349
362
  )
350
363
  elif action == "warn":
351
364
  if self.config.suggest_workflows:
352
- message = (
353
- f"💡 Consider using a workflow for: {file_path}\n"
354
- f"Suggested: @simple-mode *build \"{user_intent or 'Implement feature'}\"\n"
355
- f"(Proceeding with direct edit...)"
365
+ message = self._message_formatter.format_warning_message(
366
+ workflow=workflow,
367
+ user_intent=user_intent,
368
+ confidence=confidence,
356
369
  )
357
370
  else:
358
- message = f"💡 Consider using a workflow for: {file_path}"
371
+ message = f"Consider using a workflow for: {file_path}"
359
372
  else: # allow
360
- message = ""
373
+ message = self._message_formatter.format_allow_message()
361
374
 
362
- # Create decision
375
+ # Create decision with actual confidence from intent detection
363
376
  decision: EnforcementDecision = {
364
377
  "action": action,
365
378
  "message": message,
366
379
  "should_block": should_block,
367
- "confidence": 0.0, # Story 1: No intent detection yet
380
+ "confidence": confidence,
368
381
  }
369
382
 
370
383
  # Log decision
371
384
  logger.debug(
372
385
  f"Enforcement decision: action={action}, should_block={should_block}, "
373
- f"file_path={file_path}, user_intent={user_intent[:100]}"
386
+ f"file_path={file_path}, workflow={workflow.value}, confidence={confidence:.1f}%"
374
387
  )
375
388
 
376
389
  return decision
@@ -0,0 +1,188 @@
1
+ """
2
+ Message Formatter - ENH-001-S3
3
+
4
+ Formats enforcement messages for the Workflow Enforcement System.
5
+ Provides rich, context-aware messages for blocking and warning modes
6
+ with support for CLI and IDE output formats.
7
+
8
+ Design Principles:
9
+ - Separation of Concerns: Message formatting separate from enforcement logic
10
+ - Configurable: Emoji support, output format selection
11
+ - Context-Aware: Messages tailored to detected workflow type
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from dataclasses import dataclass
18
+ from enum import Enum
19
+ from pathlib import Path
20
+ from typing import Literal
21
+
22
+ from tapps_agents.workflow.intent_detector import WorkflowType
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class OutputFormat(str, Enum):
28
+ """Output format for messages."""
29
+
30
+ CLI = "cli"
31
+ IDE = "ide"
32
+
33
+
34
+ @dataclass
35
+ class MessageConfig:
36
+ """Configuration for message formatting."""
37
+
38
+ use_emoji: bool = True
39
+ output_format: OutputFormat = OutputFormat.CLI
40
+ show_benefits: bool = True
41
+ show_override: bool = True
42
+
43
+
44
+ # Workflow-specific benefits
45
+ WORKFLOW_BENEFITS: dict[WorkflowType, list[str]] = {
46
+ WorkflowType.BUILD: [
47
+ "Automatic testing (80%+ coverage)",
48
+ "Quality gates (75+ score required)",
49
+ "Comprehensive documentation",
50
+ "Early bug detection",
51
+ ],
52
+ WorkflowType.FIX: [
53
+ "Root cause analysis",
54
+ "Regression test generation",
55
+ "Fix verification",
56
+ "Related issue detection",
57
+ ],
58
+ WorkflowType.REFACTOR: [
59
+ "Behavior preservation tests",
60
+ "Code quality improvement",
61
+ "Technical debt reduction",
62
+ "Safe incremental changes",
63
+ ],
64
+ WorkflowType.REVIEW: [
65
+ "Security vulnerability detection",
66
+ "Code quality scoring",
67
+ "Best practice suggestions",
68
+ "Maintainability analysis",
69
+ ],
70
+ }
71
+
72
+
73
+ class MessageFormatter:
74
+ """
75
+ Formats enforcement messages for workflow enforcement system.
76
+
77
+ Provides rich, context-aware messages for blocking and warning modes
78
+ with configurable emoji support and output format selection.
79
+ """
80
+
81
+ def __init__(self, config: MessageConfig | None = None) -> None:
82
+ """Initialize formatter with optional configuration."""
83
+ self.config = config or MessageConfig()
84
+
85
+ def format_blocking_message(
86
+ self,
87
+ workflow: WorkflowType,
88
+ user_intent: str,
89
+ file_path: Path,
90
+ confidence: float,
91
+ ) -> str:
92
+ """
93
+ Format blocking mode message with benefits and override instructions.
94
+
95
+ Args:
96
+ workflow: Detected workflow type
97
+ user_intent: User's intent description
98
+ file_path: Path to file being edited
99
+ confidence: Detection confidence (0-100)
100
+
101
+ Returns:
102
+ Formatted blocking message
103
+ """
104
+ emoji = self._get_emoji("block") if self.config.use_emoji else ""
105
+ intent_display = user_intent or "Implement feature"
106
+ path_display = Path(file_path).as_posix()
107
+
108
+ lines = [
109
+ f"{emoji}Direct file edit blocked: {path_display}".strip(),
110
+ "",
111
+ f"Detected intent: {workflow.value} (confidence: {confidence:.0f}%)",
112
+ "",
113
+ ]
114
+
115
+ if self.config.show_benefits:
116
+ lines.append("TappsCodingAgents workflows provide:")
117
+ for benefit in self._get_workflow_benefits(workflow):
118
+ bullet = " * " if self.config.output_format == OutputFormat.IDE else " - "
119
+ lines.append(f"{bullet}{benefit}")
120
+ lines.append("")
121
+
122
+ lines.append("Suggested workflow:")
123
+ lines.append(f' @simple-mode {workflow.value} "{intent_display}"')
124
+ lines.append("")
125
+
126
+ if self.config.show_override:
127
+ lines.extend(self._get_override_instructions())
128
+
129
+ return "\n".join(lines)
130
+
131
+ def format_warning_message(
132
+ self,
133
+ workflow: WorkflowType,
134
+ user_intent: str,
135
+ confidence: float,
136
+ ) -> str:
137
+ """
138
+ Format warning mode message (lighter suggestion).
139
+
140
+ Args:
141
+ workflow: Detected workflow type
142
+ user_intent: User's intent description
143
+ confidence: Detection confidence (0-100)
144
+
145
+ Returns:
146
+ Formatted warning message
147
+ """
148
+ emoji = self._get_emoji("warn") if self.config.use_emoji else ""
149
+ intent_display = user_intent or "Implement feature"
150
+
151
+ lines = [
152
+ f"{emoji}Consider using a workflow (confidence: {confidence:.0f}%)".strip(),
153
+ f'Suggested: @simple-mode {workflow.value} "{intent_display}"',
154
+ "(Proceeding with direct edit...)",
155
+ ]
156
+
157
+ return "\n".join(lines)
158
+
159
+ def format_allow_message(self) -> str:
160
+ """Format allow message (empty for silent mode)."""
161
+ return ""
162
+
163
+ def _get_emoji(self, action: Literal["block", "warn", "allow"]) -> str:
164
+ """Get emoji for action type."""
165
+ emojis = {
166
+ "block": "\u26a0\ufe0f ", # Warning sign
167
+ "warn": "\U0001f4a1 ", # Light bulb
168
+ "allow": "\u2705 ", # Check mark
169
+ }
170
+ return emojis.get(action, "")
171
+
172
+ def _get_workflow_benefits(self, workflow: WorkflowType) -> list[str]:
173
+ """Get benefits list for workflow type."""
174
+ return WORKFLOW_BENEFITS.get(workflow, WORKFLOW_BENEFITS[WorkflowType.BUILD])
175
+
176
+ def _get_override_instructions(self) -> list[str]:
177
+ """Get override instructions based on output format."""
178
+ if self.config.output_format == OutputFormat.IDE:
179
+ return [
180
+ "To bypass enforcement:",
181
+ " * Use --skip-enforcement flag",
182
+ " * Or set enforcement.mode: silent in config",
183
+ ]
184
+ return [
185
+ "To bypass enforcement:",
186
+ " - Use --skip-enforcement flag",
187
+ " - Or set enforcement.mode: silent in .tapps-agents/config.yaml",
188
+ ]
@@ -127,21 +127,60 @@ class ParallelStepExecutor:
127
127
  """
128
128
  Find steps that are ready to execute (dependencies met).
129
129
 
130
+ This method respects the workflow's sequential 'next:' chain to prevent
131
+ premature step execution. A step is only ready if:
132
+ 1. It's the 'next:' step from a completed step, OR it's the first step
133
+ 2. All its artifact dependencies are met
134
+
135
+ This ensures workflows follow the intended sequence (enhance→planning→
136
+ implementation→review→testing→complete) instead of jumping to the final
137
+ step prematurely.
138
+
139
+ Fix for: Workflow Auto-Continuation Issue (BUG - Workflow stops after 1 step)
140
+ Root cause: Previous logic only checked artifact dependencies, allowing
141
+ 'complete' step to execute immediately after 'enhance' because it had
142
+ no 'requires' dependencies.
143
+
130
144
  Args:
131
145
  workflow_steps: All workflow steps
132
146
  completed_step_ids: Set of completed step IDs
133
147
  running_step_ids: Set of currently running step IDs
134
148
  available_artifacts: Set of available artifact names (from state.artifacts)
135
- Returns:
136
- List of steps ready to execute
149
+
150
+ Returns:
151
+ List of steps ready to execute
137
152
  """
138
153
  ready: list[WorkflowStep] = []
139
154
  artifacts = available_artifacts or set()
140
155
 
156
+ # Build set of next steps from completed steps' next: fields
157
+ next_step_ids: set[str] = set()
158
+ for step in workflow_steps:
159
+ if step.id in completed_step_ids and step.next:
160
+ next_step_ids.add(step.next)
161
+
162
+ # If no completed steps yet, first step is ready
163
+ if not completed_step_ids:
164
+ first_step = workflow_steps[0] if workflow_steps else None
165
+ if first_step and first_step.id not in running_step_ids:
166
+ # Still check artifact dependencies for first step
167
+ if first_step.requires:
168
+ all_met = all(req in artifacts for req in first_step.requires)
169
+ if all_met:
170
+ return [first_step]
171
+ else:
172
+ return [first_step]
173
+ return []
174
+
175
+ # Find steps that are in the next_step_ids set (sequential progression)
141
176
  for step in workflow_steps:
142
177
  if step.id in completed_step_ids or step.id in running_step_ids:
143
178
  continue
144
179
 
180
+ # Must be in the next_step_ids set (respects sequential workflow chain)
181
+ if step.id not in next_step_ids:
182
+ continue
183
+
145
184
  # Check if all required artifacts exist and are available
146
185
  if step.requires:
147
186
  all_met = all(req in artifacts for req in step.requires)
@@ -342,7 +381,7 @@ class ParallelStepExecutor:
342
381
  tasks_map[task] = step
343
382
 
344
383
  # All tasks completed successfully - collect results
345
- for task, step in tasks_map.items():
384
+ for task in tasks_map:
346
385
  result = await task
347
386
  results.append(result)
348
387
 
@@ -416,7 +455,7 @@ class ParallelStepExecutor:
416
455
 
417
456
  # Re-raise the first exception for upstream handling
418
457
  if eg.exceptions:
419
- raise eg.exceptions[0]
458
+ raise eg.exceptions[0] from None
420
459
 
421
460
  # Sort by step.id for deterministic ordering
422
461
  results.sort(key=lambda r: r.step.id)