codee-agent 0.5.2__tar.gz → 0.5.3__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.5.2 → codee_agent-0.5.3}/PKG-INFO +1 -1
- {codee_agent-0.5.2 → codee_agent-0.5.3}/pyproject.toml +1 -1
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/admin.py +3 -4
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/admin_service.py +14 -5
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_admin_service.py +72 -17
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_azure_devops/provider.py +7 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_azure_devops/test.py +25 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/LICENSE +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/README.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/.gitignore +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/admin_api.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/admin_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/agent_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/coding_agents.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/executor.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/init_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/cron_describe.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/mcp_config.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/runs_db.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/test_mcp_config.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/test_runs_db.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/test_trigger_cron_skills.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/test_trigger_issue_skills.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/trigger_aws_sqs_skills.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/trigger_cron_skills.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/trigger_email_skills.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/lib/trigger_issue_skills.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/mail_server.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/setup_wizard.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/start_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/tasks_providers.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/AGENTS.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/CLAUDE.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/aws-sqs-alarm-response/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/cron-research-5xx-errors/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/story-code-reviewer/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/story-developer/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/story-planner/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/story-planner/assets/readme-template.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/story-qa/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/story-security-reviewer/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/task-code-reviewer/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/task-developer/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/task-qa/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/task-security-reviewer/SKILL.md +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_admin_api.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_admin_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_agent_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_executor.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_init_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_memory_index.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_setup_wizard.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/test_start_cli.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/workflow_graph.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_admin/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_admin/codee_admin.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_abstract/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_abstract/provider.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_claude_code/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_claude_code/provider.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_claude_code/test.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_github_copilot/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_github_copilot/provider.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_agent_github_copilot/test.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_database/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_database/database.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_database/oauth_tokens.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_main_context/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_main_context/context.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_main_context/logging.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_main_context/test_context.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_main_context/test_logging.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_abstract/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_abstract/provider.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_azure_devops/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_azure_devops/oauth.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_jira/__init__.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee_tasks_jira/provider.py +0 -0
- {codee_agent-0.5.2 → codee_agent-0.5.3}/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.5.
|
|
3
|
+
Version: 0.5.3
|
|
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,automation
|
|
6
6
|
Author: Denis Kibalko
|
|
@@ -2215,9 +2215,7 @@ def task_filter_setting() -> rx.Component:
|
|
|
2215
2215
|
rows="2", width="100%")),
|
|
2216
2216
|
hint=rx.text("Optional. Added to every task query as one more AND "
|
|
2217
2217
|
"condition, on top of the work items above. Leave it "
|
|
2218
|
-
"empty to pick up everything they match.
|
|
2219
|
-
"not filter on an assignee — put one here if you want "
|
|
2220
|
-
"one.",
|
|
2218
|
+
"empty to pick up everything they match.",
|
|
2221
2219
|
color=MUTED, font_size="0.8rem"))
|
|
2222
2220
|
|
|
2223
2221
|
|
|
@@ -2240,7 +2238,8 @@ def tasks_check_row(check: CheckResult) -> rx.Component:
|
|
|
2240
2238
|
color=rx.cond(check.status == "waiting", MUTED, TEXT)),
|
|
2241
2239
|
rx.cond(
|
|
2242
2240
|
check.message != "",
|
|
2243
|
-
rx.text(check.message, color=MUTED, font_size="0.8rem"
|
|
2241
|
+
rx.text(check.message, color=MUTED, font_size="0.8rem",
|
|
2242
|
+
white_space="pre-wrap", overflow_wrap="anywhere")),
|
|
2244
2243
|
spacing="1", align="start", width="100%"),
|
|
2245
2244
|
spacing="2", align="start", width="100%")
|
|
2246
2245
|
|
|
@@ -159,7 +159,9 @@ def _mcp_check_prompt(server_name: str, steps: list[str]) -> str:
|
|
|
159
159
|
'"status": "<the status you left it in>"}\n'
|
|
160
160
|
"If any step did not complete:\n"
|
|
161
161
|
'{"ok": false, "error": "<one sentence naming the step that failed '
|
|
162
|
-
'and why>"
|
|
162
|
+
'and why>", "request": "<the complete MCP tool request exactly as '
|
|
163
|
+
'sent, or null if none was sent>", "response": "<the complete MCP '
|
|
164
|
+
'response or error exactly as received, or null if none was received>"}'
|
|
163
165
|
)
|
|
164
166
|
|
|
165
167
|
|
|
@@ -177,11 +179,18 @@ def _mcp_check_result(server_name: str, response: str) -> dict[str, Any]:
|
|
|
177
179
|
raise ValueError
|
|
178
180
|
except ValueError:
|
|
179
181
|
return _check(MCP_CHECK, False, "The agent did not report a result: "
|
|
180
|
-
f"{response.strip()
|
|
182
|
+
f"{response.strip() or 'it said nothing'}")
|
|
181
183
|
if not payload.get("ok"):
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
message = str(payload.get("error")
|
|
185
|
+
or "The agent reported a failure.")
|
|
186
|
+
if "request" in payload or "response" in payload:
|
|
187
|
+
request = json.dumps(payload.get("request"), indent=2,
|
|
188
|
+
ensure_ascii=False)
|
|
189
|
+
response = json.dumps(payload.get("response"), indent=2,
|
|
190
|
+
ensure_ascii=False)
|
|
191
|
+
message += (f"\n\nMCP request:\n{request}"
|
|
192
|
+
f"\n\nMCP response:\n{response}")
|
|
193
|
+
return _check(MCP_CHECK, False, message)
|
|
185
194
|
task = str(payload.get("task") or "a task").strip()
|
|
186
195
|
status = str(payload.get("status") or "a closed status").strip()
|
|
187
196
|
return _check(MCP_CHECK, True,
|
|
@@ -911,7 +911,8 @@ class AdminServiceSkillExtraFrontmatterTest(unittest.TestCase):
|
|
|
911
911
|
|
|
912
912
|
def test_save_writes_the_extra_fields_into_the_frontmatter(self) -> None:
|
|
913
913
|
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
914
|
-
service = self._service(
|
|
914
|
+
service = self._service(
|
|
915
|
+
Path(temporary_directory), "name: nightly\n")
|
|
915
916
|
|
|
916
917
|
write, _ = self._save(
|
|
917
918
|
service, "allowed-tools: Bash\ncompatibility: Claude Code\n")
|
|
@@ -942,9 +943,11 @@ class AdminServiceSkillExtraFrontmatterTest(unittest.TestCase):
|
|
|
942
943
|
|
|
943
944
|
def test_invalid_yaml_is_reported_and_nothing_is_written(self) -> None:
|
|
944
945
|
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
945
|
-
service = self._service(
|
|
946
|
+
service = self._service(
|
|
947
|
+
Path(temporary_directory), "name: nightly\n")
|
|
946
948
|
|
|
947
|
-
write, (saved, _, message, slug) = self._save(
|
|
949
|
+
write, (saved, _, message, slug) = self._save(
|
|
950
|
+
service, "allowed-tools")
|
|
948
951
|
|
|
949
952
|
write.assert_not_called()
|
|
950
953
|
self.assertFalse(saved)
|
|
@@ -953,7 +956,8 @@ class AdminServiceSkillExtraFrontmatterTest(unittest.TestCase):
|
|
|
953
956
|
|
|
954
957
|
def test_a_field_that_has_its_own_control_is_rejected(self) -> None:
|
|
955
958
|
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
956
|
-
service = self._service(
|
|
959
|
+
service = self._service(
|
|
960
|
+
Path(temporary_directory), "name: nightly\n")
|
|
957
961
|
|
|
958
962
|
write, (saved, _, message, _) = self._save(
|
|
959
963
|
service, "model: claude-opus-5\n")
|
|
@@ -973,7 +977,8 @@ class AdminServiceSkillExtraFrontmatterTest(unittest.TestCase):
|
|
|
973
977
|
|
|
974
978
|
def test_load_returns_an_empty_string_when_there_are_none(self) -> None:
|
|
975
979
|
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
976
|
-
service = self._service(
|
|
980
|
+
service = self._service(
|
|
981
|
+
Path(temporary_directory), "name: nightly\n")
|
|
977
982
|
|
|
978
983
|
self.assertEqual(service.load_skill("nightly")["extra"], "")
|
|
979
984
|
|
|
@@ -991,12 +996,14 @@ class AdminServiceAgentModelsTest(unittest.TestCase):
|
|
|
991
996
|
service = self._service(CodingAgent.CLAUDE_CODE)
|
|
992
997
|
|
|
993
998
|
with patch.object(ClaudeCodeAgent, "list_models",
|
|
994
|
-
return_value=[AgentModel(
|
|
999
|
+
return_value=[AgentModel(
|
|
1000
|
+
"claude-opus-5", "Claude Opus 5")]
|
|
995
1001
|
) as list_models:
|
|
996
1002
|
first = service.list_agent_models()
|
|
997
1003
|
second = service.list_agent_models()
|
|
998
1004
|
|
|
999
|
-
self.assertEqual(
|
|
1005
|
+
self.assertEqual(
|
|
1006
|
+
first, [{"id": "claude-opus-5", "name": "Claude Opus 5"}])
|
|
1000
1007
|
self.assertEqual(second, first)
|
|
1001
1008
|
list_models.assert_called_once()
|
|
1002
1009
|
|
|
@@ -1092,10 +1099,12 @@ class AdminServiceRepositoriesTest(unittest.TestCase):
|
|
|
1092
1099
|
return result.stdout.strip()
|
|
1093
1100
|
|
|
1094
1101
|
def test_name_is_taken_from_every_url_form(self) -> None:
|
|
1095
|
-
self.assertEqual(repository_name(
|
|
1102
|
+
self.assertEqual(repository_name(
|
|
1103
|
+
"git@github.com:org/codee.git"), "codee")
|
|
1096
1104
|
self.assertEqual(repository_name("ssh://git@github.com/org/codee.git"),
|
|
1097
1105
|
"codee")
|
|
1098
|
-
self.assertEqual(repository_name(
|
|
1106
|
+
self.assertEqual(repository_name(
|
|
1107
|
+
"https://github.com/org/codee/"), "codee")
|
|
1099
1108
|
self.assertEqual(repository_name(" "), "")
|
|
1100
1109
|
|
|
1101
1110
|
def test_add_builds_the_bare_and_worktree_layout(self) -> None:
|
|
@@ -1197,7 +1206,8 @@ class AzureDevOpsOAuthTest(unittest.TestCase):
|
|
|
1197
1206
|
tasks_provider=TasksProvider.AZURE_DEVOPS,
|
|
1198
1207
|
credentials={"azure_devops": credentials
|
|
1199
1208
|
if credentials is not None else self.AZURE_CREDENTIALS})
|
|
1200
|
-
service.context = CodeeMainContext(
|
|
1209
|
+
service.context = CodeeMainContext(
|
|
1210
|
+
data_dir=data_dir, settings=settings)
|
|
1201
1211
|
save_settings(data_dir, settings)
|
|
1202
1212
|
return service
|
|
1203
1213
|
|
|
@@ -1241,14 +1251,16 @@ class AzureDevOpsOAuthTest(unittest.TestCase):
|
|
|
1241
1251
|
|
|
1242
1252
|
with patch.object(azure_oauth, "exchange_code", return_value=tokens) as exchange, \
|
|
1243
1253
|
patch.object(azure_oauth, "fetch_account", return_value="dev@acme.com"):
|
|
1244
|
-
connected, message = service.complete_azure_authorization(
|
|
1254
|
+
connected, message = service.complete_azure_authorization(
|
|
1255
|
+
"code-1", state)
|
|
1245
1256
|
|
|
1246
1257
|
self.assertTrue(started)
|
|
1247
1258
|
self.assertTrue(connected)
|
|
1248
1259
|
self.assertIn("dev@acme.com", message)
|
|
1249
1260
|
# The exchange must reuse the redirect URI the authorization was issued
|
|
1250
1261
|
# with — Entra rejects the code otherwise.
|
|
1251
|
-
self.assertEqual(
|
|
1262
|
+
self.assertEqual(
|
|
1263
|
+
exchange.call_args.args[1], service.azure_redirect_uri())
|
|
1252
1264
|
self.assertTrue(service.azure_connection()["connected"])
|
|
1253
1265
|
|
|
1254
1266
|
def test_callback_with_a_forged_state_stores_nothing(self) -> None:
|
|
@@ -1274,7 +1286,8 @@ class AzureDevOpsOAuthTest(unittest.TestCase):
|
|
|
1274
1286
|
with patch.object(azure_oauth, "exchange_code",
|
|
1275
1287
|
side_effect=azure_oauth.AzureDevOpsAuthError(
|
|
1276
1288
|
"AADSTS7000215: Invalid client secret.")):
|
|
1277
|
-
connected, message = service.complete_azure_authorization(
|
|
1289
|
+
connected, message = service.complete_azure_authorization(
|
|
1290
|
+
"code-1", state)
|
|
1278
1291
|
|
|
1279
1292
|
self.assertFalse(connected)
|
|
1280
1293
|
self.assertIn("Invalid client secret", message)
|
|
@@ -1312,7 +1325,8 @@ class VerifyTasksConnectionTest(unittest.TestCase):
|
|
|
1312
1325
|
service.skills_dir = _write_issue_skill(root)
|
|
1313
1326
|
service.data_dir = root / ".codee"
|
|
1314
1327
|
service.data_dir.mkdir()
|
|
1315
|
-
settings = Settings(
|
|
1328
|
+
settings = Settings(
|
|
1329
|
+
credentials={"jira": {"base_url": "https://stale.test"}})
|
|
1316
1330
|
service.context = CodeeMainContext(
|
|
1317
1331
|
data_dir=service.data_dir, settings=settings)
|
|
1318
1332
|
save_settings(service.data_dir, settings)
|
|
@@ -1463,6 +1477,10 @@ class VerifyTasksMcpCheckTest(unittest.TestCase):
|
|
|
1463
1477
|
self.prompt)
|
|
1464
1478
|
self.assertIn("2. Move that issue to a Done or Cancelled status",
|
|
1465
1479
|
self.prompt)
|
|
1480
|
+
self.assertIn("complete MCP tool request exactly as sent",
|
|
1481
|
+
self.prompt)
|
|
1482
|
+
self.assertIn("complete MCP response or error exactly as received",
|
|
1483
|
+
self.prompt)
|
|
1466
1484
|
|
|
1467
1485
|
def test_a_fenced_reply_is_still_read(self) -> None:
|
|
1468
1486
|
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
@@ -1484,6 +1502,40 @@ class VerifyTasksMcpCheckTest(unittest.TestCase):
|
|
|
1484
1502
|
self.assertEqual(check["message"],
|
|
1485
1503
|
"No create-issue tool was offered.")
|
|
1486
1504
|
|
|
1505
|
+
def test_a_reported_failure_includes_the_full_mcp_exchange(self) -> None:
|
|
1506
|
+
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
1507
|
+
service = self._service(Path(temporary_directory))
|
|
1508
|
+
request = {
|
|
1509
|
+
"tool": "ado-wit_work_item_write",
|
|
1510
|
+
"arguments": {"fields": [{"name": "System.Title",
|
|
1511
|
+
"value": "Codee check"}]},
|
|
1512
|
+
}
|
|
1513
|
+
response = {
|
|
1514
|
+
"error": {"code": "invalid_type",
|
|
1515
|
+
"message": "Input validation failed"}}
|
|
1516
|
+
|
|
1517
|
+
check = self._run(service, json.dumps({
|
|
1518
|
+
"ok": False,
|
|
1519
|
+
"error": "Creating the work item failed.",
|
|
1520
|
+
"request": request,
|
|
1521
|
+
"response": response,
|
|
1522
|
+
}))
|
|
1523
|
+
|
|
1524
|
+
self.assertFalse(check["ok"])
|
|
1525
|
+
self.assertIn("MCP request:\n" + json.dumps(
|
|
1526
|
+
request, indent=2), check["message"])
|
|
1527
|
+
self.assertIn("MCP response:\n" + json.dumps(
|
|
1528
|
+
response, indent=2), check["message"])
|
|
1529
|
+
|
|
1530
|
+
def test_a_non_json_failure_is_not_truncated(self) -> None:
|
|
1531
|
+
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
1532
|
+
service = self._service(Path(temporary_directory))
|
|
1533
|
+
response = "x" * 500
|
|
1534
|
+
|
|
1535
|
+
check = self._run(service, response)
|
|
1536
|
+
|
|
1537
|
+
self.assertTrue(check["message"].endswith(response))
|
|
1538
|
+
|
|
1487
1539
|
def test_an_answer_that_is_not_the_asked_for_json_fails_the_check(self) -> None:
|
|
1488
1540
|
# An agent that ignored the format can't be believed about the rest.
|
|
1489
1541
|
with tempfile.TemporaryDirectory() as temporary_directory:
|
|
@@ -1558,7 +1610,8 @@ class SetupTasksMcpTest(unittest.TestCase):
|
|
|
1558
1610
|
service.root = root
|
|
1559
1611
|
service.data_dir = root / ".codee"
|
|
1560
1612
|
service.data_dir.mkdir()
|
|
1561
|
-
settings = Settings(
|
|
1613
|
+
settings = Settings(
|
|
1614
|
+
credentials={"jira": {"base_url": "https://stale.test"}})
|
|
1562
1615
|
service.context = CodeeMainContext(
|
|
1563
1616
|
data_dir=service.data_dir, settings=settings)
|
|
1564
1617
|
save_settings(service.data_dir, settings)
|
|
@@ -1572,7 +1625,8 @@ class SetupTasksMcpTest(unittest.TestCase):
|
|
|
1572
1625
|
root = Path(temporary_directory)
|
|
1573
1626
|
service = self._service(root)
|
|
1574
1627
|
|
|
1575
|
-
done, message = service.setup_tasks_mcp(
|
|
1628
|
+
done, message = service.setup_tasks_mcp(
|
|
1629
|
+
"jira", self.JIRA_CREDENTIALS)
|
|
1576
1630
|
|
|
1577
1631
|
self.assertTrue(done, message)
|
|
1578
1632
|
config = self._config(root)
|
|
@@ -1670,7 +1724,8 @@ class SetupTasksMcpTest(unittest.TestCase):
|
|
|
1670
1724
|
service = self._service(root)
|
|
1671
1725
|
(root / ".mcp.json").write_text("{not json")
|
|
1672
1726
|
|
|
1673
|
-
done, message = service.setup_tasks_mcp(
|
|
1727
|
+
done, message = service.setup_tasks_mcp(
|
|
1728
|
+
"jira", self.JIRA_CREDENTIALS)
|
|
1674
1729
|
|
|
1675
1730
|
self.assertFalse(done)
|
|
1676
1731
|
self.assertIn("not valid JSON", message)
|
|
@@ -166,6 +166,13 @@ class AzureDevOpsTasksProvider(AbstractTasksProvider):
|
|
|
166
166
|
return (f"Azure DevOps {self._config.organization_url} "
|
|
167
167
|
f"(all projects, connected as {account}, types {types}{extra})")
|
|
168
168
|
|
|
169
|
+
def verify_connection(self, statuses: list[str]) -> tuple[bool, str]:
|
|
170
|
+
"""Pull tasks and include the exact WIQL in a successful check."""
|
|
171
|
+
verified, message = super().verify_connection(statuses)
|
|
172
|
+
if not verified:
|
|
173
|
+
return verified, message
|
|
174
|
+
return verified, f"{message}\n\nWIQL: {self._build_wiql(statuses)}"
|
|
175
|
+
|
|
169
176
|
def mcp_server(self) -> McpServer | None:
|
|
170
177
|
"""Microsoft's Azure DevOps MCP server, addressed at this organization.
|
|
171
178
|
|
|
@@ -558,6 +558,31 @@ class TasksProviderTest(unittest.TestCase):
|
|
|
558
558
|
|
|
559
559
|
self.assertTrue(verified)
|
|
560
560
|
self.assertIn("11 Fix the thing", message)
|
|
561
|
+
self.assertIn(
|
|
562
|
+
"WIQL: SELECT [System.Id] FROM WorkItems "
|
|
563
|
+
"WHERE [System.WorkItemType] IN ('User Story', 'Task') "
|
|
564
|
+
"AND [System.State] IN ('Ready') "
|
|
565
|
+
"ORDER BY [Microsoft.VSTS.Common.Priority] ASC, "
|
|
566
|
+
"[System.CreatedDate] ASC",
|
|
567
|
+
message)
|
|
568
|
+
|
|
569
|
+
def test_an_empty_check_prints_the_custom_filter_in_the_raw_wiql(self) -> None:
|
|
570
|
+
self._connect()
|
|
571
|
+
settings = _settings(organization_url="https://dev.azure.com/acme",
|
|
572
|
+
client_id="client-1", client_secret="secret-1")
|
|
573
|
+
settings.task_filters = {
|
|
574
|
+
"azure_devops": "[Custom.Assistant] = 'Codee Agent User Account'"}
|
|
575
|
+
provider = AzureDevOpsTasksProvider(settings, self.context)
|
|
576
|
+
|
|
577
|
+
with patch("codee_tasks_azure_devops.provider.requests.post",
|
|
578
|
+
return_value=_response({"workItems": []})):
|
|
579
|
+
verified, message = provider.verify_connection(["Ready"])
|
|
580
|
+
|
|
581
|
+
self.assertTrue(verified)
|
|
582
|
+
self.assertIn("No tasks are waiting for it right now.", message)
|
|
583
|
+
self.assertIn(
|
|
584
|
+
"AND ([Custom.Assistant] = 'Codee Agent User Account') ORDER BY",
|
|
585
|
+
message)
|
|
561
586
|
|
|
562
587
|
def test_a_rejected_query_reports_what_azure_devops_said(self) -> None:
|
|
563
588
|
self._connect()
|
|
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.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/aws-sqs-alarm-response/SKILL.md
RENAMED
|
File without changes
|
{codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/cron-research-5xx-errors/SKILL.md
RENAMED
|
File without changes
|
{codee_agent-0.5.2 → codee_agent-0.5.3}/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.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/story-security-reviewer/SKILL.md
RENAMED
|
File without changes
|
{codee_agent-0.5.2 → codee_agent-0.5.3}/src/codee/templates/skills/task-code-reviewer/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codee_agent-0.5.2 → codee_agent-0.5.3}/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
|