codee-agent 0.7.0__tar.gz → 0.7.1__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.
- {codee_agent-0.7.0 → codee_agent-0.7.1}/PKG-INFO +1 -1
- {codee_agent-0.7.0 → codee_agent-0.7.1}/pyproject.toml +1 -1
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/admin.py +8 -3
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/admin_service.py +38 -6
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_admin_service.py +89 -44
- {codee_agent-0.7.0 → codee_agent-0.7.1}/LICENSE +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/README.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/.gitignore +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/admin_api.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/admin_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/agent_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/coding_agents.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/executor.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/init_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/claude_key_rotation.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/cron_describe.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/mcp_config.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/runs_db.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/test_claude_key_rotation.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/test_mcp_config.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/test_runs_db.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/test_trigger_cron_skills.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/test_trigger_issue_skills.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/trigger_aws_sqs_skills.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/trigger_cron_skills.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/trigger_email_skills.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/lib/trigger_issue_skills.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/mail_server.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/setup_wizard.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/start_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/tasks_providers.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/AGENTS.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/CLAUDE.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/aws-sqs-alarm-response/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/cron-research-5xx-errors/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-code-reviewer/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-developer/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-planner/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-planner/assets/readme-template.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-qa/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-security-reviewer/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/task-code-reviewer/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/task-developer/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/task-qa/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/task-security-reviewer/SKILL.md +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_admin_api.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_admin_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_agent_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_coding_agents.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_executor.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_init_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_memory_index.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_setup_wizard.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/test_start_cli.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/workflow_graph.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_admin/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_admin/codee_admin.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_abstract/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_abstract/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_claude_code/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_claude_code/account.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_claude_code/credentials.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_claude_code/oauth.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_claude_code/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_claude_code/test.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_claude_code/usage.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_codex/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_codex/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_codex/test.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_github_copilot/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_github_copilot/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_github_copilot/test.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_opencode/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_opencode/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_agent_opencode/test.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_database/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_database/claude_code_accounts.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_database/database.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_database/oauth_tokens.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_main_context/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_main_context/context.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_main_context/logging.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_main_context/test_context.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_main_context/test_logging.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_abstract/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_abstract/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_azure_devops/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_azure_devops/oauth.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_azure_devops/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_azure_devops/test.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_jira/__init__.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_jira/provider.py +0 -0
- {codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee_tasks_jira/test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codee-agent
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: A virtual co-worker that picks up Jira and Azure DevOps tasks and solves them with coding agents.
|
|
5
5
|
Keywords: ai,agent,jira,azure-devops,claude-code,github-copilot,codex,opencode,automation
|
|
6
6
|
Author: Denis Kibalko
|
|
@@ -272,6 +272,7 @@ class RunRecord(BaseModel):
|
|
|
272
272
|
started_at: str
|
|
273
273
|
ended_at: str
|
|
274
274
|
duration_label: str
|
|
275
|
+
relative_age_label: str
|
|
275
276
|
session_id: str
|
|
276
277
|
message: str
|
|
277
278
|
user_message: str
|
|
@@ -863,6 +864,7 @@ class AdminState(rx.State):
|
|
|
863
864
|
started_at=run["started_at"],
|
|
864
865
|
ended_at=run["ended_at"],
|
|
865
866
|
duration_label=run["duration_label"],
|
|
867
|
+
relative_age_label=run["relative_age_label"],
|
|
866
868
|
session_id=run.get("session_id") or "",
|
|
867
869
|
message=message,
|
|
868
870
|
user_message=(run.get("user_message") or message).strip(),
|
|
@@ -2572,9 +2574,12 @@ def run_row(run: RunRecord) -> rx.Component:
|
|
|
2572
2574
|
rx.flex(
|
|
2573
2575
|
rx.vstack(rx.hstack(rx.text(run.skill_name, font_weight="600"),
|
|
2574
2576
|
rx.badge(run.status, color_scheme=rx.cond(run.status == "succeeded", "green", "red"))),
|
|
2575
|
-
rx.
|
|
2576
|
-
|
|
2577
|
-
|
|
2577
|
+
rx.hstack(
|
|
2578
|
+
rx.text(local_datetime(run.ended_at)),
|
|
2579
|
+
rx.text("(", run.relative_age_label, ")"),
|
|
2580
|
+
color=MUTED, font_size="0.8rem",
|
|
2581
|
+
font_family="IBM Plex Mono, monospace",
|
|
2582
|
+
spacing="2"),
|
|
2578
2583
|
rx.text("Duration: ", run.duration_label, color=MUTED,
|
|
2579
2584
|
font_size="0.8rem",
|
|
2580
2585
|
font_family="IBM Plex Mono, monospace"),
|
|
@@ -59,6 +59,26 @@ from codee_main_context.context import (
|
|
|
59
59
|
work_item_mappings,
|
|
60
60
|
)
|
|
61
61
|
|
|
62
|
+
|
|
63
|
+
def relative_age_label(value: str, now: datetime | None = None) -> str:
|
|
64
|
+
"""Format an ISO timestamp as a compact minute, hour, or day age."""
|
|
65
|
+
try:
|
|
66
|
+
timestamp = datetime.fromisoformat(value)
|
|
67
|
+
except (TypeError, ValueError):
|
|
68
|
+
return ""
|
|
69
|
+
if timestamp.tzinfo is None:
|
|
70
|
+
timestamp = timestamp.replace(tzinfo=timezone.utc)
|
|
71
|
+
current = now or datetime.now(timezone.utc)
|
|
72
|
+
elapsed = max(int((current - timestamp).total_seconds()), 0)
|
|
73
|
+
if elapsed < 3600:
|
|
74
|
+
amount, unit = max(elapsed // 60, 1), "minute"
|
|
75
|
+
elif elapsed < 86400:
|
|
76
|
+
amount, unit = elapsed // 3600, "hour"
|
|
77
|
+
else:
|
|
78
|
+
amount, unit = elapsed // 86400, "day"
|
|
79
|
+
return f"{amount} {unit}{'' if amount == 1 else 's'} ago"
|
|
80
|
+
|
|
81
|
+
|
|
62
82
|
load_dotenv()
|
|
63
83
|
|
|
64
84
|
MANAGED = {
|
|
@@ -1300,16 +1320,25 @@ class AdminService:
|
|
|
1300
1320
|
f"Generating workflow for work item {issue_type.capitalize()} "
|
|
1301
1321
|
f"from {_count(len(skills), 'issue-trigger skill')}..."
|
|
1302
1322
|
)
|
|
1323
|
+
session_id = str(uuid.uuid4())
|
|
1324
|
+
|
|
1325
|
+
def opened(agent_session_id: str) -> None:
|
|
1326
|
+
nonlocal session_id
|
|
1327
|
+
session_id = agent_session_id
|
|
1328
|
+
|
|
1303
1329
|
validation_error = ""
|
|
1304
1330
|
for attempt in range(2):
|
|
1305
|
-
request = prompt
|
|
1306
1331
|
if validation_error:
|
|
1307
|
-
|
|
1308
|
-
"
|
|
1309
|
-
f"{validation_error}. Return corrected JSON only."
|
|
1332
|
+
response = agent.continue_conversation(
|
|
1333
|
+
"Your previous response was invalid: "
|
|
1334
|
+
f"{validation_error}. Return corrected JSON only.",
|
|
1335
|
+
session_id,
|
|
1336
|
+
agent.best_model(),
|
|
1337
|
+
opened,
|
|
1310
1338
|
)
|
|
1311
|
-
|
|
1312
|
-
|
|
1339
|
+
else:
|
|
1340
|
+
response = agent.run(
|
|
1341
|
+
prompt, session_id, agent.best_model(), opened)
|
|
1313
1342
|
payload_text = _strip_code_fence(response)
|
|
1314
1343
|
try:
|
|
1315
1344
|
payload = json.loads(payload_text)
|
|
@@ -2050,6 +2079,7 @@ class AdminService:
|
|
|
2050
2079
|
search: str = "") -> list[dict[str, Any]]:
|
|
2051
2080
|
rows = runs_db.recent_runs(
|
|
2052
2081
|
limit, offset, search=search, main_context=self.context)
|
|
2082
|
+
now = datetime.now(timezone.utc)
|
|
2053
2083
|
for row in rows:
|
|
2054
2084
|
try:
|
|
2055
2085
|
elapsed = (datetime.fromisoformat(row["ended_at"])
|
|
@@ -2057,6 +2087,8 @@ class AdminService:
|
|
|
2057
2087
|
except (TypeError, ValueError):
|
|
2058
2088
|
elapsed = 0
|
|
2059
2089
|
row["duration_label"] = runs_db.fmt_elapsed(max(int(elapsed), 0))
|
|
2090
|
+
row["relative_age_label"] = relative_age_label(
|
|
2091
|
+
row["ended_at"], now)
|
|
2060
2092
|
return rows
|
|
2061
2093
|
|
|
2062
2094
|
def load_settings(self) -> Settings:
|
|
@@ -5,6 +5,7 @@ import tempfile
|
|
|
5
5
|
import threading
|
|
6
6
|
import time
|
|
7
7
|
import unittest
|
|
8
|
+
from datetime import datetime, timezone
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from unittest.mock import Mock, patch
|
|
10
11
|
from urllib.parse import parse_qs, urlparse
|
|
@@ -19,7 +20,7 @@ from codee.admin_service import (
|
|
|
19
20
|
USAGE_RATE_LIMIT_BACKOFF_SECONDS, WORKFLOW_CACHE_VERSION,
|
|
20
21
|
WORKFLOW_HUMAN_EDGE_COLOR, AdminService, WorkflowGeneration,
|
|
21
22
|
_remove_redundant_skill_transitions, azure_oauth, issue_prompt_task,
|
|
22
|
-
normalize_work_items, parse_skill, repository_name)
|
|
23
|
+
normalize_work_items, parse_skill, relative_age_label, repository_name)
|
|
23
24
|
from codee.lib import runs_db
|
|
24
25
|
from codee_agent_claude_code import oauth as claude_oauth
|
|
25
26
|
from codee_agent_claude_code.account import AccountUnavailable
|
|
@@ -61,6 +62,19 @@ def _write_issue_skill(root: Path) -> Path:
|
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
class RecentRunsTest(unittest.TestCase):
|
|
65
|
+
def test_formats_relative_age_in_minutes_hours_and_days(self) -> None:
|
|
66
|
+
now = datetime(2026, 9, 24, 12, 0, tzinfo=timezone.utc)
|
|
67
|
+
cases = [
|
|
68
|
+
("2026-09-24T11:59:30+00:00", "1 minute ago"),
|
|
69
|
+
("2026-09-24T11:58:00+00:00", "2 minutes ago"),
|
|
70
|
+
("2026-09-24T10:00:00+00:00", "2 hours ago"),
|
|
71
|
+
("2026-09-21T12:00:00+00:00", "3 days ago"),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
for timestamp, expected in cases:
|
|
75
|
+
with self.subTest(timestamp=timestamp):
|
|
76
|
+
self.assertEqual(relative_age_label(timestamp, now), expected)
|
|
77
|
+
|
|
64
78
|
def test_formats_duration_from_start_and_end_times(self) -> None:
|
|
65
79
|
with tempfile.TemporaryDirectory() as directory:
|
|
66
80
|
context = CodeeMainContext(data_dir=Path(directory))
|
|
@@ -1281,18 +1295,25 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1281
1295
|
"Move the story to CR Needed when every subtask is implemented.\n"
|
|
1282
1296
|
)
|
|
1283
1297
|
agent = Mock()
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1298
|
+
|
|
1299
|
+
def invalid_workflow(message, session_id, model, on_session_id):
|
|
1300
|
+
on_session_id("agent-thread")
|
|
1301
|
+
return (
|
|
1302
|
+
'{"statuses":["Ready","Subtask Review","CR Needed"],'
|
|
1303
|
+
'"transitions":[{"source":"Ready","target":"Subtask Review",'
|
|
1304
|
+
'"label":"story-developer","evidence":"Move each subtask to '
|
|
1305
|
+
'Subtask Review once its pull request is open."}],'
|
|
1306
|
+
'"final_statuses":["CR Needed"]}'
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
agent.run.side_effect = invalid_workflow
|
|
1310
|
+
agent.continue_conversation.return_value = (
|
|
1290
1311
|
'{"statuses":["Ready","CR Needed"],'
|
|
1291
1312
|
'"transitions":[{"source":"Ready","target":"CR Needed",'
|
|
1292
1313
|
'"label":"story-developer","evidence":"Move the story to CR '
|
|
1293
1314
|
'Needed when every subtask is implemented."}],'
|
|
1294
|
-
'"final_statuses":["CR Needed"]}'
|
|
1295
|
-
|
|
1315
|
+
'"final_statuses":["CR Needed"]}'
|
|
1316
|
+
)
|
|
1296
1317
|
service = AdminService.__new__(AdminService)
|
|
1297
1318
|
service.root = root
|
|
1298
1319
|
service.skills_dir = skills_dir
|
|
@@ -1305,14 +1326,23 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1305
1326
|
}):
|
|
1306
1327
|
workflow = service.generate_workflow()["story"]
|
|
1307
1328
|
|
|
1308
|
-
self.assertEqual(agent.run.call_count,
|
|
1329
|
+
self.assertEqual(agent.run.call_count, 1)
|
|
1330
|
+
self.assertEqual(agent.continue_conversation.call_count, 1)
|
|
1309
1331
|
self.assertIn(
|
|
1310
1332
|
"moves the subtask rather than the story",
|
|
1311
|
-
agent.
|
|
1333
|
+
agent.continue_conversation.call_args.args[0],
|
|
1312
1334
|
)
|
|
1313
1335
|
self.assertIn(
|
|
1314
1336
|
"The graph is the lifecycle of the story work item alone",
|
|
1315
|
-
agent.run.
|
|
1337
|
+
agent.run.call_args.args[0],
|
|
1338
|
+
)
|
|
1339
|
+
self.assertNotIn(
|
|
1340
|
+
"The graph is the lifecycle of the story work item alone",
|
|
1341
|
+
agent.continue_conversation.call_args.args[0],
|
|
1342
|
+
)
|
|
1343
|
+
self.assertEqual(
|
|
1344
|
+
agent.continue_conversation.call_args.args[1],
|
|
1345
|
+
"agent-thread",
|
|
1316
1346
|
)
|
|
1317
1347
|
self.assertEqual(
|
|
1318
1348
|
[node["data"]["label"] for node in workflow["nodes"]],
|
|
@@ -1382,12 +1412,14 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1382
1412
|
"After planning, move the story to [AI] Ready for human review.\n"
|
|
1383
1413
|
)
|
|
1384
1414
|
agent = Mock()
|
|
1385
|
-
agent.run.
|
|
1415
|
+
agent.run.return_value = (
|
|
1386
1416
|
'{"statuses":["[AI] Decomposition needed",'
|
|
1387
1417
|
'"[AI] Ready for development","[AI] Ready for human review"],'
|
|
1388
1418
|
'"transitions":[{"source":"[AI] Decomposition needed",'
|
|
1389
1419
|
'"target":"[AI] Ready for development","label":"",'
|
|
1390
|
-
'"evidence":""}],"final_statuses":[]}'
|
|
1420
|
+
'"evidence":""}],"final_statuses":[]}'
|
|
1421
|
+
)
|
|
1422
|
+
agent.continue_conversation.return_value = (
|
|
1391
1423
|
'{"statuses":["[AI] Decomposition needed",'
|
|
1392
1424
|
'"[AI] Ready for development","[AI] Ready for human review"],'
|
|
1393
1425
|
'"transitions":[{'
|
|
@@ -1395,8 +1427,8 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1395
1427
|
'"target":"[AI] Ready for human review",'
|
|
1396
1428
|
'"label":"story-planner",'
|
|
1397
1429
|
'"evidence":"After planning, move the story to [AI] Ready for human review."}],'
|
|
1398
|
-
'"final_statuses":["[AI] Ready for human review"]}'
|
|
1399
|
-
|
|
1430
|
+
'"final_statuses":["[AI] Ready for human review"]}'
|
|
1431
|
+
)
|
|
1400
1432
|
service = AdminService.__new__(AdminService)
|
|
1401
1433
|
service.root = root
|
|
1402
1434
|
service.skills_dir = skills_dir
|
|
@@ -1409,8 +1441,9 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1409
1441
|
}):
|
|
1410
1442
|
workflow = service.generate_workflow()["story"]
|
|
1411
1443
|
|
|
1412
|
-
self.assertEqual(agent.run.call_count,
|
|
1413
|
-
|
|
1444
|
+
self.assertEqual(agent.run.call_count, 1)
|
|
1445
|
+
self.assertEqual(agent.continue_conversation.call_count, 1)
|
|
1446
|
+
retry_prompt = agent.continue_conversation.call_args.args[0]
|
|
1414
1447
|
self.assertIn(
|
|
1415
1448
|
'transition 1 ("[AI] Decomposition needed" -> '
|
|
1416
1449
|
'"[AI] Ready for development", label "") is labelled with a '
|
|
@@ -1501,16 +1534,18 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1501
1534
|
"Move the issue to Review when work is done.\n"
|
|
1502
1535
|
)
|
|
1503
1536
|
agent = Mock()
|
|
1504
|
-
agent.run.
|
|
1537
|
+
agent.run.return_value = (
|
|
1505
1538
|
'{"statuses":["Ready","Review"],"transitions":['
|
|
1506
1539
|
'{"source":"Ready","target":"Review","label":"develop",'
|
|
1507
1540
|
'"evidence":"Work the issue until it is done."}],'
|
|
1508
|
-
'"final_statuses":["Review"]}'
|
|
1541
|
+
'"final_statuses":["Review"]}'
|
|
1542
|
+
)
|
|
1543
|
+
agent.continue_conversation.return_value = (
|
|
1509
1544
|
'{"statuses":["Ready","Review"],"transitions":['
|
|
1510
1545
|
'{"source":"Ready","target":"Review","label":"develop",'
|
|
1511
1546
|
'"evidence":"Move the issue to Review when work is done."}],'
|
|
1512
|
-
'"final_statuses":["Review"]}'
|
|
1513
|
-
|
|
1547
|
+
'"final_statuses":["Review"]}'
|
|
1548
|
+
)
|
|
1514
1549
|
service = AdminService.__new__(AdminService)
|
|
1515
1550
|
service.root = root
|
|
1516
1551
|
service.skills_dir = skills_dir
|
|
@@ -1554,18 +1589,20 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1554
1589
|
"Move the issue to Review when work is done.\n"
|
|
1555
1590
|
)
|
|
1556
1591
|
agent = Mock()
|
|
1557
|
-
agent.run.
|
|
1592
|
+
agent.run.return_value = (
|
|
1558
1593
|
'{"statuses":["Ready","Review"],"transitions":['
|
|
1559
1594
|
'{"source":"Blocked","target":"Review","label":"develop",'
|
|
1560
1595
|
'"evidence":"Move the issue to Review when work is done."},'
|
|
1561
1596
|
'{"source":"Ready","target":"Review","label":"develop",'
|
|
1562
1597
|
'"evidence":"Work the issue until it is done."}],'
|
|
1563
|
-
'"final_statuses":["Done"]}'
|
|
1598
|
+
'"final_statuses":["Done"]}'
|
|
1599
|
+
)
|
|
1600
|
+
agent.continue_conversation.return_value = (
|
|
1564
1601
|
'{"statuses":["Ready","Review"],"transitions":['
|
|
1565
1602
|
'{"source":"Ready","target":"Review","label":"develop",'
|
|
1566
1603
|
'"evidence":"Move the issue to Review when work is done."}],'
|
|
1567
|
-
'"final_statuses":["Review"]}'
|
|
1568
|
-
|
|
1604
|
+
'"final_statuses":["Review"]}'
|
|
1605
|
+
)
|
|
1569
1606
|
service = AdminService.__new__(AdminService)
|
|
1570
1607
|
service.root = root
|
|
1571
1608
|
service.skills_dir = skills_dir
|
|
@@ -1578,7 +1615,7 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1578
1615
|
}):
|
|
1579
1616
|
service.generate_workflow()
|
|
1580
1617
|
|
|
1581
|
-
retry_prompt = agent.
|
|
1618
|
+
retry_prompt = agent.continue_conversation.call_args.args[0]
|
|
1582
1619
|
self.assertIn('transition 1 ("Blocked" -> "Review", label '
|
|
1583
1620
|
'"develop") uses "Blocked", which is missing from '
|
|
1584
1621
|
'statuses "Ready", "Review"', retry_prompt)
|
|
@@ -1601,16 +1638,18 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1601
1638
|
"Move the issue to Done after approval.\n"
|
|
1602
1639
|
)
|
|
1603
1640
|
agent = Mock()
|
|
1604
|
-
agent.run.
|
|
1641
|
+
agent.run.return_value = (
|
|
1605
1642
|
'{"statuses":["Ready","Review","Done"],"transitions":['
|
|
1606
1643
|
'{"source":"Ready","target":"Done","label":"review",'
|
|
1607
1644
|
'"evidence":"Move the issue to Done after approval."}],'
|
|
1608
|
-
'"final_statuses":["Done"]}'
|
|
1645
|
+
'"final_statuses":["Done"]}'
|
|
1646
|
+
)
|
|
1647
|
+
agent.continue_conversation.return_value = (
|
|
1609
1648
|
'{"statuses":["Ready","Review","Done"],"transitions":['
|
|
1610
1649
|
'{"source":"Review","target":"Done","label":"review",'
|
|
1611
1650
|
'"evidence":"Move the issue to Done after approval."}],'
|
|
1612
|
-
'"final_statuses":["Done"]}'
|
|
1613
|
-
|
|
1651
|
+
'"final_statuses":["Done"]}'
|
|
1652
|
+
)
|
|
1614
1653
|
service = AdminService.__new__(AdminService)
|
|
1615
1654
|
service.root = root
|
|
1616
1655
|
service.skills_dir = skills_dir
|
|
@@ -1628,7 +1667,7 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1628
1667
|
'a status "review" never has the issue in: its entry statuses '
|
|
1629
1668
|
'are "Review" and no other transition of that skill moves the '
|
|
1630
1669
|
'issue to "Ready".',
|
|
1631
|
-
agent.
|
|
1670
|
+
agent.continue_conversation.call_args.args[0],
|
|
1632
1671
|
)
|
|
1633
1672
|
|
|
1634
1673
|
def test_generate_workflow_warns_when_statuses_have_no_edges(self) -> None:
|
|
@@ -1840,15 +1879,17 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1840
1879
|
"Move the issue to In Progress when work starts.\n"
|
|
1841
1880
|
)
|
|
1842
1881
|
agent = Mock()
|
|
1843
|
-
agent.run.
|
|
1882
|
+
agent.run.return_value = (
|
|
1844
1883
|
'{"statuses":["Ready","In Progress"],"transitions":[],'
|
|
1845
1884
|
'"final_statuses":[],"human_actions":[],'
|
|
1846
1885
|
'"human_transitions":[{"source":"Ready","target":"In Progress",'
|
|
1847
|
-
'"evidence":"Move the issue to In Progress when work starts."}]}'
|
|
1886
|
+
'"evidence":"Move the issue to In Progress when work starts."}]}'
|
|
1887
|
+
)
|
|
1888
|
+
agent.continue_conversation.return_value = (
|
|
1848
1889
|
'{"statuses":["Ready","In Progress"],"transitions":[],'
|
|
1849
1890
|
'"final_statuses":[],"human_actions":[],'
|
|
1850
|
-
'"human_transitions":[]}'
|
|
1851
|
-
|
|
1891
|
+
'"human_transitions":[]}'
|
|
1892
|
+
)
|
|
1852
1893
|
service = AdminService.__new__(AdminService)
|
|
1853
1894
|
service.root = root
|
|
1854
1895
|
service.skills_dir = skills_dir
|
|
@@ -1861,10 +1902,11 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1861
1902
|
}):
|
|
1862
1903
|
service.generate_workflow()
|
|
1863
1904
|
|
|
1864
|
-
self.assertEqual(agent.run.call_count,
|
|
1905
|
+
self.assertEqual(agent.run.call_count, 1)
|
|
1906
|
+
self.assertEqual(agent.continue_conversation.call_count, 1)
|
|
1865
1907
|
self.assertIn(
|
|
1866
1908
|
"human transition source Ready is an entry status of a skill",
|
|
1867
|
-
agent.
|
|
1909
|
+
agent.continue_conversation.call_args.args[0],
|
|
1868
1910
|
)
|
|
1869
1911
|
|
|
1870
1912
|
def test_generate_workflow_rejects_a_human_action_for_an_unknown_status(
|
|
@@ -1882,13 +1924,15 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1882
1924
|
"Move the issue to In Progress when work starts.\n"
|
|
1883
1925
|
)
|
|
1884
1926
|
agent = Mock()
|
|
1885
|
-
agent.run.
|
|
1927
|
+
agent.run.return_value = (
|
|
1886
1928
|
'{"statuses":["Ready","In Progress"],"transitions":[],'
|
|
1887
1929
|
'"final_statuses":[],"human_actions":[{"status":"Blocked",'
|
|
1888
|
-
'"action":"Unblock the story."}]}'
|
|
1930
|
+
'"action":"Unblock the story."}]}'
|
|
1931
|
+
)
|
|
1932
|
+
agent.continue_conversation.return_value = (
|
|
1889
1933
|
'{"statuses":["Ready","In Progress"],"transitions":[],'
|
|
1890
|
-
'"final_statuses":[],"human_actions":[]}'
|
|
1891
|
-
|
|
1934
|
+
'"final_statuses":[],"human_actions":[]}'
|
|
1935
|
+
)
|
|
1892
1936
|
service = AdminService.__new__(AdminService)
|
|
1893
1937
|
service.root = root
|
|
1894
1938
|
service.skills_dir = skills_dir
|
|
@@ -1901,10 +1945,11 @@ class AdminServiceIssueTriggerTest(unittest.TestCase):
|
|
|
1901
1945
|
}):
|
|
1902
1946
|
service.generate_workflow()
|
|
1903
1947
|
|
|
1904
|
-
self.assertEqual(agent.run.call_count,
|
|
1948
|
+
self.assertEqual(agent.run.call_count, 1)
|
|
1949
|
+
self.assertEqual(agent.continue_conversation.call_count, 1)
|
|
1905
1950
|
self.assertIn(
|
|
1906
1951
|
"each human action must name a declared status",
|
|
1907
|
-
agent.
|
|
1952
|
+
agent.continue_conversation.call_args.args[0],
|
|
1908
1953
|
)
|
|
1909
1954
|
|
|
1910
1955
|
def test_generate_workflow_returns_empty_without_issue_skills(self) -> None:
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
{codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/aws-sqs-alarm-response/SKILL.md
RENAMED
|
File without changes
|
{codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/cron-research-5xx-errors/SKILL.md
RENAMED
|
File without changes
|
{codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-code-reviewer/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/story-security-reviewer/SKILL.md
RENAMED
|
File without changes
|
{codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/task-code-reviewer/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codee_agent-0.7.0 → codee_agent-0.7.1}/src/codee/templates/skills/task-security-reviewer/SKILL.md
RENAMED
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|