Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions code_puppy/agents/agent_code_puppy.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def get_available_tools(self) -> list[str]:
"delete_file",
"agent_run_shell_command",
"agent_share_your_reasoning",
"ask_user_question",
]

def get_system_prompt(self) -> str:
Expand Down Expand Up @@ -134,6 +135,24 @@ def get_system_prompt(self) -> str:
- To CONTINUE a session: use the session_id from the previous invocation's response
- For one-off tasks: leave session_id as None (auto-generates)

User Interaction:
- ask_user_question(questions): Ask the user interactive multiple-choice questions through a TUI.
Use this when you need user input to make decisions, gather preferences, or confirm actions.
Each question has a header (short label), question text, and 2-6 options with descriptions.
Supports single-select (pick one) and multi-select (pick many) modes.
Returns answers, or indicates if the user cancelled.
Example:
```python
ask_user_question(questions=[{{
"question": "Which database should we use?",
"header": "Database",
"options": [
{{"label": "PostgreSQL", "description": "Relational, ACID compliant"}},
{{"label": "MongoDB", "description": "Document store, flexible schema"}}
]
}}])
```

Important rules:
- You MUST use tools to accomplish tasks - DO NOT just output code or descriptions
- Before every other tool use, you must use "share_your_reasoning" to explain your thought process and planned next steps
Expand Down
3 changes: 3 additions & 0 deletions code_puppy/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
register_open_terminal,
register_start_api_server,
)
from code_puppy.tools.ask_user_question import register_ask_user_question
from code_puppy.tools.command_runner import (
register_agent_run_shell_command,
register_agent_share_your_reasoning,
Expand Down Expand Up @@ -103,6 +104,8 @@
# Command Runner
"agent_run_shell_command": register_agent_run_shell_command,
"agent_share_your_reasoning": register_agent_share_your_reasoning,
# User Interaction
"ask_user_question": register_ask_user_question,
# Browser Control
"browser_initialize": register_initialize_browser,
"browser_close": register_close_browser,
Expand Down
26 changes: 26 additions & 0 deletions code_puppy/tools/ask_user_question/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Ask User Question tool for code-puppy.

This tool allows agents to ask users interactive multiple-choice questions
through a terminal TUI interface. Uses prompt_toolkit for the split-panel
UI similar to the /colors command.
"""

from .handler import ask_user_question
from .models import (
AskUserQuestionInput,
AskUserQuestionOutput,
Question,
QuestionAnswer,
QuestionOption,
)
from .registration import register_ask_user_question

__all__ = [
"ask_user_question",
"register_ask_user_question",
"AskUserQuestionInput",
"AskUserQuestionOutput",
"Question",
"QuestionAnswer",
"QuestionOption",
]
73 changes: 73 additions & 0 deletions code_puppy/tools/ask_user_question/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Constants for the ask_user_question tool."""

from typing import Final

# Question constraints
MAX_QUESTIONS_PER_CALL: Final[int] = 10 # Reasonable limit for a single TUI interaction
MIN_OPTIONS_PER_QUESTION: Final[int] = 2
MAX_OPTIONS_PER_QUESTION: Final[int] = 6
MAX_HEADER_LENGTH: Final[int] = 12
MAX_LABEL_LENGTH: Final[int] = 50
MAX_DESCRIPTION_LENGTH: Final[int] = 200
MAX_QUESTION_LENGTH: Final[int] = 500
MAX_OTHER_TEXT_LENGTH: Final[int] = 500

# UI settings
DEFAULT_TIMEOUT_SECONDS: Final[int] = 300 # 5 minutes
TIMEOUT_WARNING_SECONDS: Final[int] = 60 # Show warning at 60s remaining
AUTO_ADD_OTHER_OPTION: Final[bool] = True

# Other option configuration
OTHER_OPTION_LABEL: Final[str] = "Other"
OTHER_OPTION_DESCRIPTION: Final[str] = "Enter a custom option"

# Left panel width magic numbers (extracted for clarity)
LEFT_PANEL_PADDING: Final[int] = (
14 # left(2) + cursor(2) + checkmark(2) + right(2) + buffer(6)
)
MIN_LEFT_PANEL_WIDTH: Final[int] = 21
MAX_LEFT_PANEL_WIDTH: Final[int] = 36

# Horizontal padding for panel content (matches left panel's " " prefix)
PANEL_CONTENT_PADDING: Final[str] = " "

# CI environment variables to check for non-interactive detection
# Use tuple for true immutability (Final only prevents reassignment, not mutation)
CI_ENV_VARS: Final[tuple[str, ...]] = (
"CI",
"GITHUB_ACTIONS",
"GITLAB_CI",
"JENKINS_URL",
"TRAVIS",
"CIRCLECI",
"BUILDKITE",
"AZURE_PIPELINES",
"TEAMCITY_VERSION",
)

# Terminal escape sequences for alternate screen buffer
ENTER_ALT_SCREEN: Final[str] = "\033[?1049h"
EXIT_ALT_SCREEN: Final[str] = "\033[?1049l"
CLEAR_AND_HOME: Final[str] = "\033[2J\033[H"

# Unicode symbols for TUI rendering
CURSOR_POINTER: Final[str] = "\u276f" # ❯
CURSOR_TRIANGLE: Final[str] = "\u25b6" # ▶
CHECK_MARK: Final[str] = "\u2713" # ✓
RADIO_FILLED: Final[str] = "\u25cf" # ●
BORDER_DOUBLE: Final[str] = "\u2550" # ═
ARROW_LEFT: Final[str] = "\u2190" # ←
ARROW_RIGHT: Final[str] = "\u2192" # →
ARROW_UP: Final[str] = "\u2191" # ↑
ARROW_DOWN: Final[str] = "\u2193" # ↓
PIPE_SEPARATOR: Final[str] = "\u2502" # │

# Panel rendering
MAX_READABLE_WIDTH: Final[int] = 120
HELP_BORDER_WIDTH: Final[int] = 50

# Error formatting
MAX_VALIDATION_ERRORS_SHOWN: Final[int] = 3

# Terminal synchronization delay (seconds)
TERMINAL_SYNC_DELAY: Final[float] = 0.05
55 changes: 55 additions & 0 deletions code_puppy/tools/ask_user_question/demo_tui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python
"""Manual demo script for the ask_user_question TUI.

This is NOT an automated test - it's for interactive visual testing.
Run this script directly to demo the TUI:
python -m code_puppy.tools.ask_user_question.demo_tui
"""

from .handler import ask_user_question


def main():
"""Run a test of the ask_user_question TUI."""
print("Testing ask_user_question TUI...")
print("=" * 50)

# Test single question, single select
result = ask_user_question(
[
{
"question": "Which database should we use for this project?",
"header": "Database",
"multi_select": False,
"options": [
{
"label": "PostgreSQL",
"description": "Relational database, ACID compliant, great for complex queries",
},
{
"label": "MongoDB",
"description": "Document store, flexible schema, good for rapid iteration",
},
{
"label": "Redis",
"description": "In-memory store, ultra-fast, best for caching",
},
{
"label": "SQLite",
"description": "Lightweight, file-based, perfect for local development",
},
],
}
]
)

print("\n" + "=" * 50)
print("Result:")
print(f" Answers: {result.answers}")
print(f" Cancelled: {result.cancelled}")
print(f" Error: {result.error}")
print(f" Timed out: {result.timed_out}")


if __name__ == "__main__":
main()
Loading
Loading