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
132 changes: 132 additions & 0 deletions agent/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,138 @@ def add_test():

click.echo(f"\n✅ Test case '{test_id}' successfully added!\n")

# Command: update an existing test case
@cli.command()
@click.argument("test_id")
def update_test(test_id):
"""Update an existing test case. Ex: python agent/cli.py update-test TC-001"""
with open(TEST_CASES_PATH, "r") as f:
data = json.load(f)

test = next((tc for tc in data["test_cases"] if tc["id"] == test_id), None)

if test is None:
click.echo(f"\n❌ Test case '{test_id}' not found.\n")
return

click.echo(f"\n✏️ Updating test case '{test_id}' — {test['title']}")
click.echo(" Leave blank to keep the current value.\n")

title = click.prompt(f" Title", default=test["title"])
area_path = click.prompt(f" Area", default=test["area_path"])
assigned_to = click.prompt(f" Assigned to", default=test["assigned_to"])

test["title"] = title
test["area_path"] = area_path
test["assigned_to"] = assigned_to

with open(TEST_CASES_PATH, "w") as f:
json.dump(data, f, indent=2, ensure_ascii=False)

log_event("test_updated", test_id, f"Test case '{test_id}' updated")

click.echo(f"\n✅ Test case '{test_id}' successfully updated!\n")

# Command: search test cases
@cli.command()
@click.option("--keyword", "-k", default=None, help="Search by keyword")
@click.option("--assignee", "-a", default=None, help="Search by assignee")
@click.option("--area", "-ar", default=None, help="Search by area")
@click.option("--state", "-s", default=None, help="Search by state")
def search(keyword, assignee, area, state):
"""Search test cases by different criteria."""
with open(TEST_CASES_PATH, "r") as f:
data = json.load(f)

results = data["test_cases"]

if keyword:
keyword_lower = keyword.lower()
results = [
tc for tc in results
if keyword_lower in tc["title"].lower()
or keyword_lower in tc["area_path"].lower()
or keyword_lower in tc["assigned_to"].lower()
]

if assignee:
results = [
tc for tc in results
if assignee.lower() in tc["assigned_to"].lower()
]

if area:
results = [
tc for tc in results
if area.lower() in tc["area_path"].lower()
]

if state:
results = [
tc for tc in results
if state.lower() == tc["state"].lower()
]

if not results:
click.echo(f"\n❌ No test cases found.\n")
return

click.echo(f"\n🔍 Search results:\n")
for tc in results:
click.echo(f" {tc['id']} | {tc['title']} | {tc['state']} | {tc['assigned_to']}")
click.echo("")


# Command: show test cases statistics
@cli.command()
def stats():
"""Show test cases statistics."""
with open(TEST_CASES_PATH, "r") as f:
data = json.load(f)

total = len(data["test_cases"])
states = {}
for tc in data["test_cases"]:
state = tc["state"]
states[state] = states.get(state, 0) + 1

click.echo(f"\n📊 Test Cases Statistics\n")
click.echo(f" Total: {total}")
for state, count in states.items():
percentage = round((count / total) * 100)
click.echo(f" {state:<15} {count} ({percentage}%)")
click.echo("")

# Command: assign a test case to a team member
@cli.command()
@click.argument("test_id")
def assign(test_id):
"""Assign a test case to a team member. Ex: python agent/cli.py assign TC-001"""
with open(TEST_CASES_PATH, "r") as f:
data = json.load(f)

test = next((tc for tc in data["test_cases"] if tc["id"] == test_id), None)

if test is None:
click.echo(f"\n❌ Test case '{test_id}' not found.\n")
return

click.echo(f"\n👤 Assigning test case '{test_id}' — {test['title']}")
click.echo(f" Current assignee: {test['assigned_to']}\n")

new_assignee = click.prompt(" New assignee")

old_assignee = test["assigned_to"]
test["assigned_to"] = new_assignee

with open(TEST_CASES_PATH, "w") as f:
json.dump(data, f, indent=2, ensure_ascii=False)

log_event("test_assigned", test_id, f"Assigned from '{old_assignee}' to '{new_assignee}'")

click.echo(f"\n✅ TC '{test_id}' assigned to '{new_assignee}'!\n")


# Command: show the team roadmap
@cli.command()
def roadmap():
Expand Down
158 changes: 114 additions & 44 deletions agent/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,116 @@ def wait_for_menu():
"""Waits for the user to press Enter before returning to the menu."""
input("\nPress Enter to return to the menu...")

def test_cases_menu():
"""Submenu for Test Cases."""
while True:
print("\n📋 Test Cases\n")

choice = questionary.select(
"What would you like to do?",
choices=[
"👁️ View Test Cases",
"🔍 Search Test Cases",
"➕ Create Test Case",
"✏️ Update Test Case",
"👤 Assign Test Case",
"📊 Stats",
"📤 Export Test Cases History",
"⬅️ Back"
]
).ask()

if choice == "👁️ View Test Cases":
run_command(["list-tests"])
wait_for_menu()

elif choice == "🔍 Search Test Cases":
search_by = questionary.select(
"Search by:",
choices=[
"🔤 Keyword (searches everything)",
"👤 Assignee",
"📁 Area",
"🔵 State",
]
).ask()

if search_by == "🔤 Keyword (searches everything)":
value = questionary.text("Keyword:").ask()
run_command(["search", "--keyword", value])

elif search_by == "👤 Assignee":
value = questionary.text("Assignee name:").ask()
run_command(["search", "--assignee", value])

elif search_by == "📁 Area":
value = questionary.text("Area:").ask()
run_command(["search", "--area", value])

elif search_by == "🔵 State":
value = questionary.select(
"Select state:",
choices=["Active", "Passed", "Failed", "Blocked", "In Progress"]
).ask()
run_command(["search", "--state", value])

wait_for_menu()

elif choice == "➕ Create Test Case":
run_command(["add-test"])
wait_for_menu()

elif choice == "✏️ Update Test Case":
test_id = questionary.text("Test Case ID (e.g. TC-001):").ask()
run_command(["update-test", test_id])
wait_for_menu()

elif choice == "👤 Assign Test Case":
test_id = questionary.text("Test Case ID (e.g. TC-001):").ask()
run_command(["assign", test_id])
wait_for_menu()

elif choice == "📊 Stats":
run_command(["stats"])
wait_for_menu()

elif choice == "📤 Export Test Cases History":
run_command(["export-tests-history"])
wait_for_menu()

elif choice == "⬅️ Back":
break

def history_menu():
"""Submenu for History."""
while True:
print("\n📜 History\n")

choice = questionary.select(
"What would you like to do?",
choices=[
"📜 View History",
"📤 Export History",
"⬅️ Back"
]
).ask()

if choice == "📜 View History":
run_command(["history"])
wait_for_menu()

elif choice == "📤 Export History":
run_command(["export-history"])
wait_for_menu()

elif choice == "⬅️ Back":
break

def create_pull_request():
"""Interactive flow to create a Pull Request."""

print("\n🔀 Create Pull Request\n")

# Select platform
platform = questionary.select(
"Select platform:",
choices=["GitHub", "GitLab", "Cancel"]
Expand All @@ -26,13 +130,11 @@ def create_pull_request():
print("\n❌ PR creation cancelled.\n")
return

# PR Title
title = questionary.text("PR Title:").ask()
if not title:
print("\n❌ PR creation cancelled.\n")
return

# PR Description (pre-filled with template)
template_path = ".github/pull_request_template.md"
if os.path.exists(template_path):
with open(template_path, "r") as f:
Expand All @@ -55,7 +157,6 @@ def create_pull_request():
print("\n❌ PR creation cancelled.\n")
return

# Confirmation
confirm = questionary.select(
"\nWhat would you like to do?",
choices=["✅ Create Pull Request", "❌ Cancel"]
Expand All @@ -65,19 +166,6 @@ def create_pull_request():
print("\n❌ PR creation cancelled.\n")
return

# Check current branch
current_branch = subprocess.run(
["git", "branch", "--show-current"],
capture_output=True,
text=True
).stdout.strip()

if current_branch == "main":
print("\n⚠️ You are on the 'main' branch. Please switch to a feature branch before creating a PR.")
print("💡 Tip: Run 'git checkout -b feature/your-feature-name' to create a new branch.\n")
return

# Check current branch
current_branch = subprocess.run(
["git", "branch", "--show-current"],
capture_output=True,
Expand All @@ -89,12 +177,11 @@ def create_pull_request():
print("💡 Tip: Run 'git checkout -b feature/your-feature-name' to create a new branch.\n")
return

# git add, commit, push and create PR
print("\n⏳ Preparing your Pull Request...\n")

subprocess.run(["git", "add", "."])
subprocess.run(["git", "commit", "-m", title])
subprocess.run(["git", "push"])
subprocess.run(["git", "push", "--set-upstream", "origin", current_branch])

if platform == "GitHub":
result = subprocess.run(
Expand All @@ -118,41 +205,24 @@ def main():
choice = questionary.select(
"What would you like to do?",
choices=[
"📋 View Test Cases",
"➕ Create Test Case",
"🗺️ View Roadmap",
"📜 View History",
"📤 Export History",
"📤 Export Test Cases History",
"📋 Test Cases",
"🗺️ Roadmap",
"📜 History",
"🤖 Ask AI",
"🔀 Create Pull Request",
"❌ Exit"
]
).ask()

if choice == "📋 View Test Cases":
run_command(["list-tests"])
wait_for_menu()

elif choice == "➕ Create Test Case":
run_command(["add-test"])
wait_for_menu()
if choice == "📋 Test Cases":
test_cases_menu()

elif choice == "🗺️ View Roadmap":
elif choice == "🗺️ Roadmap":
run_command(["roadmap"])
wait_for_menu()

elif choice == "📜 View History":
run_command(["history"])
wait_for_menu()

elif choice == "📤 Export History":
run_command(["export-history"])
wait_for_menu()

elif choice == "📤 Export Test Cases History":
run_command(["export-tests-history"])
wait_for_menu()
elif choice == "📜 History":
history_menu()

elif choice == "🤖 Ask AI":
question = questionary.text("What would you like to ask?").ask()
Expand Down
18 changes: 18 additions & 0 deletions knowledge_base/history.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@
"type": "test_created",
"test_id": "TC-005",
"details": "Test case 'This is a test' created"
},
{
"timestamp": "2026-07-15 12:17",
"type": "test_updated",
"test_id": "TC-001",
"details": "Test case 'TC-001' updated"
},
{
"timestamp": "2026-07-15 14:11",
"type": "test_assigned",
"test_id": "TC-001",
"details": "Assigned from 'Diogo' to 'Tester'"
},
{
"timestamp": "2026-07-15 14:12",
"type": "test_updated",
"test_id": "TC-002",
"details": "Test case 'TC-002' updated"
}
]
}
Loading