jeanclode 0.1.0__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.
- jeanclode-0.1.0/PKG-INFO +21 -0
- jeanclode-0.1.0/jeanclode.egg-info/PKG-INFO +21 -0
- jeanclode-0.1.0/jeanclode.egg-info/SOURCES.txt +183 -0
- jeanclode-0.1.0/jeanclode.egg-info/dependency_links.txt +1 -0
- jeanclode-0.1.0/jeanclode.egg-info/entry_points.txt +2 -0
- jeanclode-0.1.0/jeanclode.egg-info/requires.txt +4 -0
- jeanclode-0.1.0/jeanclode.egg-info/top_level.txt +1 -0
- jeanclode-0.1.0/pyproject.toml +166 -0
- jeanclode-0.1.0/setup.cfg +4 -0
- jeanclode-0.1.0/src/__init__.py +8 -0
- jeanclode-0.1.0/src/activities/__init__.py +5 -0
- jeanclode-0.1.0/src/activities/ci_watch/__init__.py +19 -0
- jeanclode-0.1.0/src/activities/ci_watch/github.py +157 -0
- jeanclode-0.1.0/src/activities/ci_watch/gitlab.py +120 -0
- jeanclode-0.1.0/src/activities/ci_watch/poll.py +137 -0
- jeanclode-0.1.0/src/activities/ci_watch/schemas.py +43 -0
- jeanclode-0.1.0/src/activities/decorator.py +111 -0
- jeanclode-0.1.0/src/activities/git/__init__.py +29 -0
- jeanclode-0.1.0/src/activities/git/ops.py +195 -0
- jeanclode-0.1.0/src/activities/git/pr.py +178 -0
- jeanclode-0.1.0/src/activities/git/schemas.py +22 -0
- jeanclode-0.1.0/src/activities/git/targets.py +79 -0
- jeanclode-0.1.0/src/activities/issue/__init__.py +11 -0
- jeanclode-0.1.0/src/activities/issue/comment.py +73 -0
- jeanclode-0.1.0/src/activities/issue/fetch.py +188 -0
- jeanclode-0.1.0/src/activities/issue/schemas.py +17 -0
- jeanclode-0.1.0/src/activities/issue/utils.py +12 -0
- jeanclode-0.1.0/src/activities/notify.py +130 -0
- jeanclode-0.1.0/src/activities/respond/__init__.py +21 -0
- jeanclode-0.1.0/src/activities/respond/relabel.py +58 -0
- jeanclode-0.1.0/src/activities/respond/schemas.py +42 -0
- jeanclode-0.1.0/src/activities/respond/threads.py +149 -0
- jeanclode-0.1.0/src/activities/review/__init__.py +16 -0
- jeanclode-0.1.0/src/activities/review/dedup.py +18 -0
- jeanclode-0.1.0/src/activities/review/guardrail.py +44 -0
- jeanclode-0.1.0/src/activities/review/posting.py +407 -0
- jeanclode-0.1.0/src/activities/review/recovery.py +128 -0
- jeanclode-0.1.0/src/activities/review/routing.py +50 -0
- jeanclode-0.1.0/src/activities/review/schemas.py +111 -0
- jeanclode-0.1.0/src/activities/review/styling.py +25 -0
- jeanclode-0.1.0/src/activities/sentry/__init__.py +47 -0
- jeanclode-0.1.0/src/activities/sentry/fetch.py +208 -0
- jeanclode-0.1.0/src/activities/sentry/git.py +9 -0
- jeanclode-0.1.0/src/activities/sentry/pr.py +3 -0
- jeanclode-0.1.0/src/activities/sentry/route.py +139 -0
- jeanclode-0.1.0/src/activities/sentry/schemas.py +152 -0
- jeanclode-0.1.0/src/activities/sentry/secrets.py +57 -0
- jeanclode-0.1.0/src/activities/summary/__init__.py +17 -0
- jeanclode-0.1.0/src/activities/summary/format_summary.py +13 -0
- jeanclode-0.1.0/src/activities/summary/schemas.py +54 -0
- jeanclode-0.1.0/src/activities/summary/update_description.py +78 -0
- jeanclode-0.1.0/src/adaptors/__init__.py +5 -0
- jeanclode-0.1.0/src/adaptors/base.py +85 -0
- jeanclode-0.1.0/src/adaptors/diffn.py +68 -0
- jeanclode-0.1.0/src/adaptors/discussions.py +103 -0
- jeanclode-0.1.0/src/adaptors/display.py +50 -0
- jeanclode-0.1.0/src/adaptors/github/__init__.py +1 -0
- jeanclode-0.1.0/src/adaptors/github/adaptor.py +83 -0
- jeanclode-0.1.0/src/adaptors/github/auth.py +36 -0
- jeanclode-0.1.0/src/adaptors/github/client.py +92 -0
- jeanclode-0.1.0/src/adaptors/github/context.py +477 -0
- jeanclode-0.1.0/src/adaptors/github/display.py +338 -0
- jeanclode-0.1.0/src/adaptors/github/resolver.py +24 -0
- jeanclode-0.1.0/src/adaptors/github/sha.py +100 -0
- jeanclode-0.1.0/src/adaptors/gitlab/__init__.py +1 -0
- jeanclode-0.1.0/src/adaptors/gitlab/adaptor.py +79 -0
- jeanclode-0.1.0/src/adaptors/gitlab/auth.py +49 -0
- jeanclode-0.1.0/src/adaptors/gitlab/client.py +77 -0
- jeanclode-0.1.0/src/adaptors/gitlab/context.py +388 -0
- jeanclode-0.1.0/src/adaptors/gitlab/display.py +9 -0
- jeanclode-0.1.0/src/adaptors/gitlab/resolver.py +24 -0
- jeanclode-0.1.0/src/adaptors/gitlab/sha.py +73 -0
- jeanclode-0.1.0/src/adaptors/registry.py +31 -0
- jeanclode-0.1.0/src/adaptors/sentry/__init__.py +5 -0
- jeanclode-0.1.0/src/adaptors/sentry/adaptor.py +71 -0
- jeanclode-0.1.0/src/adaptors/sentry/auth.py +31 -0
- jeanclode-0.1.0/src/adaptors/sentry/client.py +78 -0
- jeanclode-0.1.0/src/adaptors/sentry/display.py +41 -0
- jeanclode-0.1.0/src/adaptors/sentry/resolver.py +71 -0
- jeanclode-0.1.0/src/agents/__init__.py +6 -0
- jeanclode-0.1.0/src/agents/_memory_backend.py +151 -0
- jeanclode-0.1.0/src/agents/base.py +430 -0
- jeanclode-0.1.0/src/agents/hooks.py +481 -0
- jeanclode-0.1.0/src/agents/issue/__init__.py +21 -0
- jeanclode-0.1.0/src/agents/issue/base.py +21 -0
- jeanclode-0.1.0/src/agents/issue/fixer.py +22 -0
- jeanclode-0.1.0/src/agents/issue/schemas.py +84 -0
- jeanclode-0.1.0/src/agents/issue/triage.py +20 -0
- jeanclode-0.1.0/src/agents/memory_tool.py +248 -0
- jeanclode-0.1.0/src/agents/respond/__init__.py +10 -0
- jeanclode-0.1.0/src/agents/respond/base.py +21 -0
- jeanclode-0.1.0/src/agents/respond/planner.py +31 -0
- jeanclode-0.1.0/src/agents/respond/schemas.py +47 -0
- jeanclode-0.1.0/src/agents/review/__init__.py +21 -0
- jeanclode-0.1.0/src/agents/review/analyzer.py +24 -0
- jeanclode-0.1.0/src/agents/review/base.py +12 -0
- jeanclode-0.1.0/src/agents/review/deduplicator.py +22 -0
- jeanclode-0.1.0/src/agents/review/fact_checker.py +26 -0
- jeanclode-0.1.0/src/agents/review/issue_explorer.py +22 -0
- jeanclode-0.1.0/src/agents/review/schemas.py +22 -0
- jeanclode-0.1.0/src/agents/review/styler.py +21 -0
- jeanclode-0.1.0/src/agents/review/synthesizer.py +23 -0
- jeanclode-0.1.0/src/agents/schemas.py +46 -0
- jeanclode-0.1.0/src/agents/sentry/__init__.py +17 -0
- jeanclode-0.1.0/src/agents/sentry/base.py +21 -0
- jeanclode-0.1.0/src/agents/sentry/fixer.py +43 -0
- jeanclode-0.1.0/src/agents/sentry/synthesis.py +24 -0
- jeanclode-0.1.0/src/agents/sentry/triage.py +29 -0
- jeanclode-0.1.0/src/agents/summary/__init__.py +9 -0
- jeanclode-0.1.0/src/agents/summary/base.py +21 -0
- jeanclode-0.1.0/src/agents/summary/parser.py +20 -0
- jeanclode-0.1.0/src/agents/summary/schemas.py +15 -0
- jeanclode-0.1.0/src/agents/summary/summarizer.py +21 -0
- jeanclode-0.1.0/src/agents/utils.py +84 -0
- jeanclode-0.1.0/src/cli.py +182 -0
- jeanclode-0.1.0/src/config.py +142 -0
- jeanclode-0.1.0/src/config_file.py +179 -0
- jeanclode-0.1.0/src/log.py +50 -0
- jeanclode-0.1.0/src/main.py +38 -0
- jeanclode-0.1.0/src/output.py +429 -0
- jeanclode-0.1.0/src/plugin_paths.py +25 -0
- jeanclode-0.1.0/src/runner/__init__.py +5 -0
- jeanclode-0.1.0/src/runner/display_subscriber.py +128 -0
- jeanclode-0.1.0/src/runner/preflight.py +238 -0
- jeanclode-0.1.0/src/runner/run.py +310 -0
- jeanclode-0.1.0/src/runtime/__init__.py +25 -0
- jeanclode-0.1.0/src/runtime/bots.py +29 -0
- jeanclode-0.1.0/src/runtime/bus.py +39 -0
- jeanclode-0.1.0/src/runtime/context.py +53 -0
- jeanclode-0.1.0/src/runtime/events.py +65 -0
- jeanclode-0.1.0/src/runtime/mcp_connectors.py +74 -0
- jeanclode-0.1.0/src/runtime/notify.py +94 -0
- jeanclode-0.1.0/src/skills/__init__.py +30 -0
- jeanclode-0.1.0/src/skills/discovery.py +99 -0
- jeanclode-0.1.0/src/skills/prompt.py +123 -0
- jeanclode-0.1.0/src/skills/schemas.py +23 -0
- jeanclode-0.1.0/src/skills/thirdparty.py +154 -0
- jeanclode-0.1.0/src/workflows/__init__.py +29 -0
- jeanclode-0.1.0/src/workflows/_smoke/__init__.py +0 -0
- jeanclode-0.1.0/src/workflows/_smoke/echo.py +55 -0
- jeanclode-0.1.0/src/workflows/base.py +97 -0
- jeanclode-0.1.0/src/workflows/code_review/__init__.py +1 -0
- jeanclode-0.1.0/src/workflows/code_review/runner.py +315 -0
- jeanclode-0.1.0/src/workflows/code_review/utils.py +205 -0
- jeanclode-0.1.0/src/workflows/issue_resolve/__init__.py +1 -0
- jeanclode-0.1.0/src/workflows/issue_resolve/runner.py +362 -0
- jeanclode-0.1.0/src/workflows/issue_resolve/utils.py +141 -0
- jeanclode-0.1.0/src/workflows/jeanclode_respond/__init__.py +5 -0
- jeanclode-0.1.0/src/workflows/jeanclode_respond/runner.py +212 -0
- jeanclode-0.1.0/src/workflows/jeanclode_respond/utils.py +150 -0
- jeanclode-0.1.0/src/workflows/pr_summary/__init__.py +5 -0
- jeanclode-0.1.0/src/workflows/pr_summary/runner.py +126 -0
- jeanclode-0.1.0/src/workflows/pr_summary/utils.py +138 -0
- jeanclode-0.1.0/src/workflows/schemas.py +13 -0
- jeanclode-0.1.0/src/workflows/sentry_fix/__init__.py +5 -0
- jeanclode-0.1.0/src/workflows/sentry_fix/runner.py +425 -0
- jeanclode-0.1.0/src/workflows/sentry_fix/utils.py +235 -0
- jeanclode-0.1.0/src/workflows/utils.py +9 -0
- jeanclode-0.1.0/src/workspace.py +241 -0
- jeanclode-0.1.0/tests/test_activity_decorator.py +85 -0
- jeanclode-0.1.0/tests/test_base_agent.py +368 -0
- jeanclode-0.1.0/tests/test_base_agent_continuity.py +97 -0
- jeanclode-0.1.0/tests/test_base_agent_mcp_connectors.py +190 -0
- jeanclode-0.1.0/tests/test_base_agent_memory.py +140 -0
- jeanclode-0.1.0/tests/test_base_agent_skills.py +153 -0
- jeanclode-0.1.0/tests/test_cli.py +248 -0
- jeanclode-0.1.0/tests/test_config.py +209 -0
- jeanclode-0.1.0/tests/test_config_file.py +111 -0
- jeanclode-0.1.0/tests/test_conftest_eval_isolation.py +35 -0
- jeanclode-0.1.0/tests/test_display_subscriber.py +101 -0
- jeanclode-0.1.0/tests/test_event_bus.py +47 -0
- jeanclode-0.1.0/tests/test_mcp_connectors.py +86 -0
- jeanclode-0.1.0/tests/test_notify.py +97 -0
- jeanclode-0.1.0/tests/test_output.py +259 -0
- jeanclode-0.1.0/tests/test_parallel_agents.py +69 -0
- jeanclode-0.1.0/tests/test_preflight.py +205 -0
- jeanclode-0.1.0/tests/test_run.py +104 -0
- jeanclode-0.1.0/tests/test_run_context.py +54 -0
- jeanclode-0.1.0/tests/test_skills_discovery.py +101 -0
- jeanclode-0.1.0/tests/test_skills_prompt.py +108 -0
- jeanclode-0.1.0/tests/test_skills_thirdparty.py +180 -0
- jeanclode-0.1.0/tests/test_smoke_workflow.py +110 -0
- jeanclode-0.1.0/tests/test_tool_summary.py +44 -0
- jeanclode-0.1.0/tests/test_workflow_registry.py +94 -0
- jeanclode-0.1.0/tests/test_workspace.py +119 -0
jeanclode-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jeanclode
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multi-plugin CLI — auto-detects what to do from URLs and invokes Claude Code skills
|
|
5
|
+
Author: Jeanclode Contributors
|
|
6
|
+
License-Expression: AGPL-3.0
|
|
7
|
+
Project-URL: Homepage, https://jeanclode.com
|
|
8
|
+
Project-URL: Repository, https://github.com/jeanclode-hq/jeanclode
|
|
9
|
+
Project-URL: Issues, https://github.com/jeanclode-hq/jeanclode/issues
|
|
10
|
+
Project-URL: Documentation, https://jeanclode.com/docs
|
|
11
|
+
Keywords: sentry,ai,llm,bug-fix,automation,cli
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Python: >=3.14
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: claude-agent-sdk>=0.2.144
|
|
19
|
+
Requires-Dist: jinja2>=3.1.0
|
|
20
|
+
Requires-Dist: pydantic>=2.9.0
|
|
21
|
+
Requires-Dist: rich>=13.0.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jeanclode
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multi-plugin CLI — auto-detects what to do from URLs and invokes Claude Code skills
|
|
5
|
+
Author: Jeanclode Contributors
|
|
6
|
+
License-Expression: AGPL-3.0
|
|
7
|
+
Project-URL: Homepage, https://jeanclode.com
|
|
8
|
+
Project-URL: Repository, https://github.com/jeanclode-hq/jeanclode
|
|
9
|
+
Project-URL: Issues, https://github.com/jeanclode-hq/jeanclode/issues
|
|
10
|
+
Project-URL: Documentation, https://jeanclode.com/docs
|
|
11
|
+
Keywords: sentry,ai,llm,bug-fix,automation,cli
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Python: >=3.14
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: claude-agent-sdk>=0.2.144
|
|
19
|
+
Requires-Dist: jinja2>=3.1.0
|
|
20
|
+
Requires-Dist: pydantic>=2.9.0
|
|
21
|
+
Requires-Dist: rich>=13.0.0
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
jeanclode.egg-info/PKG-INFO
|
|
3
|
+
jeanclode.egg-info/SOURCES.txt
|
|
4
|
+
jeanclode.egg-info/dependency_links.txt
|
|
5
|
+
jeanclode.egg-info/entry_points.txt
|
|
6
|
+
jeanclode.egg-info/requires.txt
|
|
7
|
+
jeanclode.egg-info/top_level.txt
|
|
8
|
+
src/__init__.py
|
|
9
|
+
src/cli.py
|
|
10
|
+
src/config.py
|
|
11
|
+
src/config_file.py
|
|
12
|
+
src/log.py
|
|
13
|
+
src/main.py
|
|
14
|
+
src/output.py
|
|
15
|
+
src/plugin_paths.py
|
|
16
|
+
src/workspace.py
|
|
17
|
+
src/activities/__init__.py
|
|
18
|
+
src/activities/decorator.py
|
|
19
|
+
src/activities/notify.py
|
|
20
|
+
src/activities/ci_watch/__init__.py
|
|
21
|
+
src/activities/ci_watch/github.py
|
|
22
|
+
src/activities/ci_watch/gitlab.py
|
|
23
|
+
src/activities/ci_watch/poll.py
|
|
24
|
+
src/activities/ci_watch/schemas.py
|
|
25
|
+
src/activities/git/__init__.py
|
|
26
|
+
src/activities/git/ops.py
|
|
27
|
+
src/activities/git/pr.py
|
|
28
|
+
src/activities/git/schemas.py
|
|
29
|
+
src/activities/git/targets.py
|
|
30
|
+
src/activities/issue/__init__.py
|
|
31
|
+
src/activities/issue/comment.py
|
|
32
|
+
src/activities/issue/fetch.py
|
|
33
|
+
src/activities/issue/schemas.py
|
|
34
|
+
src/activities/issue/utils.py
|
|
35
|
+
src/activities/respond/__init__.py
|
|
36
|
+
src/activities/respond/relabel.py
|
|
37
|
+
src/activities/respond/schemas.py
|
|
38
|
+
src/activities/respond/threads.py
|
|
39
|
+
src/activities/review/__init__.py
|
|
40
|
+
src/activities/review/dedup.py
|
|
41
|
+
src/activities/review/guardrail.py
|
|
42
|
+
src/activities/review/posting.py
|
|
43
|
+
src/activities/review/recovery.py
|
|
44
|
+
src/activities/review/routing.py
|
|
45
|
+
src/activities/review/schemas.py
|
|
46
|
+
src/activities/review/styling.py
|
|
47
|
+
src/activities/sentry/__init__.py
|
|
48
|
+
src/activities/sentry/fetch.py
|
|
49
|
+
src/activities/sentry/git.py
|
|
50
|
+
src/activities/sentry/pr.py
|
|
51
|
+
src/activities/sentry/route.py
|
|
52
|
+
src/activities/sentry/schemas.py
|
|
53
|
+
src/activities/sentry/secrets.py
|
|
54
|
+
src/activities/summary/__init__.py
|
|
55
|
+
src/activities/summary/format_summary.py
|
|
56
|
+
src/activities/summary/schemas.py
|
|
57
|
+
src/activities/summary/update_description.py
|
|
58
|
+
src/adaptors/__init__.py
|
|
59
|
+
src/adaptors/base.py
|
|
60
|
+
src/adaptors/diffn.py
|
|
61
|
+
src/adaptors/discussions.py
|
|
62
|
+
src/adaptors/display.py
|
|
63
|
+
src/adaptors/registry.py
|
|
64
|
+
src/adaptors/github/__init__.py
|
|
65
|
+
src/adaptors/github/adaptor.py
|
|
66
|
+
src/adaptors/github/auth.py
|
|
67
|
+
src/adaptors/github/client.py
|
|
68
|
+
src/adaptors/github/context.py
|
|
69
|
+
src/adaptors/github/display.py
|
|
70
|
+
src/adaptors/github/resolver.py
|
|
71
|
+
src/adaptors/github/sha.py
|
|
72
|
+
src/adaptors/gitlab/__init__.py
|
|
73
|
+
src/adaptors/gitlab/adaptor.py
|
|
74
|
+
src/adaptors/gitlab/auth.py
|
|
75
|
+
src/adaptors/gitlab/client.py
|
|
76
|
+
src/adaptors/gitlab/context.py
|
|
77
|
+
src/adaptors/gitlab/display.py
|
|
78
|
+
src/adaptors/gitlab/resolver.py
|
|
79
|
+
src/adaptors/gitlab/sha.py
|
|
80
|
+
src/adaptors/sentry/__init__.py
|
|
81
|
+
src/adaptors/sentry/adaptor.py
|
|
82
|
+
src/adaptors/sentry/auth.py
|
|
83
|
+
src/adaptors/sentry/client.py
|
|
84
|
+
src/adaptors/sentry/display.py
|
|
85
|
+
src/adaptors/sentry/resolver.py
|
|
86
|
+
src/agents/__init__.py
|
|
87
|
+
src/agents/_memory_backend.py
|
|
88
|
+
src/agents/base.py
|
|
89
|
+
src/agents/hooks.py
|
|
90
|
+
src/agents/memory_tool.py
|
|
91
|
+
src/agents/schemas.py
|
|
92
|
+
src/agents/utils.py
|
|
93
|
+
src/agents/issue/__init__.py
|
|
94
|
+
src/agents/issue/base.py
|
|
95
|
+
src/agents/issue/fixer.py
|
|
96
|
+
src/agents/issue/schemas.py
|
|
97
|
+
src/agents/issue/triage.py
|
|
98
|
+
src/agents/respond/__init__.py
|
|
99
|
+
src/agents/respond/base.py
|
|
100
|
+
src/agents/respond/planner.py
|
|
101
|
+
src/agents/respond/schemas.py
|
|
102
|
+
src/agents/review/__init__.py
|
|
103
|
+
src/agents/review/analyzer.py
|
|
104
|
+
src/agents/review/base.py
|
|
105
|
+
src/agents/review/deduplicator.py
|
|
106
|
+
src/agents/review/fact_checker.py
|
|
107
|
+
src/agents/review/issue_explorer.py
|
|
108
|
+
src/agents/review/schemas.py
|
|
109
|
+
src/agents/review/styler.py
|
|
110
|
+
src/agents/review/synthesizer.py
|
|
111
|
+
src/agents/sentry/__init__.py
|
|
112
|
+
src/agents/sentry/base.py
|
|
113
|
+
src/agents/sentry/fixer.py
|
|
114
|
+
src/agents/sentry/synthesis.py
|
|
115
|
+
src/agents/sentry/triage.py
|
|
116
|
+
src/agents/summary/__init__.py
|
|
117
|
+
src/agents/summary/base.py
|
|
118
|
+
src/agents/summary/parser.py
|
|
119
|
+
src/agents/summary/schemas.py
|
|
120
|
+
src/agents/summary/summarizer.py
|
|
121
|
+
src/runner/__init__.py
|
|
122
|
+
src/runner/display_subscriber.py
|
|
123
|
+
src/runner/preflight.py
|
|
124
|
+
src/runner/run.py
|
|
125
|
+
src/runtime/__init__.py
|
|
126
|
+
src/runtime/bots.py
|
|
127
|
+
src/runtime/bus.py
|
|
128
|
+
src/runtime/context.py
|
|
129
|
+
src/runtime/events.py
|
|
130
|
+
src/runtime/mcp_connectors.py
|
|
131
|
+
src/runtime/notify.py
|
|
132
|
+
src/skills/__init__.py
|
|
133
|
+
src/skills/discovery.py
|
|
134
|
+
src/skills/prompt.py
|
|
135
|
+
src/skills/schemas.py
|
|
136
|
+
src/skills/thirdparty.py
|
|
137
|
+
src/workflows/__init__.py
|
|
138
|
+
src/workflows/base.py
|
|
139
|
+
src/workflows/schemas.py
|
|
140
|
+
src/workflows/utils.py
|
|
141
|
+
src/workflows/_smoke/__init__.py
|
|
142
|
+
src/workflows/_smoke/echo.py
|
|
143
|
+
src/workflows/code_review/__init__.py
|
|
144
|
+
src/workflows/code_review/runner.py
|
|
145
|
+
src/workflows/code_review/utils.py
|
|
146
|
+
src/workflows/issue_resolve/__init__.py
|
|
147
|
+
src/workflows/issue_resolve/runner.py
|
|
148
|
+
src/workflows/issue_resolve/utils.py
|
|
149
|
+
src/workflows/jeanclode_respond/__init__.py
|
|
150
|
+
src/workflows/jeanclode_respond/runner.py
|
|
151
|
+
src/workflows/jeanclode_respond/utils.py
|
|
152
|
+
src/workflows/pr_summary/__init__.py
|
|
153
|
+
src/workflows/pr_summary/runner.py
|
|
154
|
+
src/workflows/pr_summary/utils.py
|
|
155
|
+
src/workflows/sentry_fix/__init__.py
|
|
156
|
+
src/workflows/sentry_fix/runner.py
|
|
157
|
+
src/workflows/sentry_fix/utils.py
|
|
158
|
+
tests/test_activity_decorator.py
|
|
159
|
+
tests/test_base_agent.py
|
|
160
|
+
tests/test_base_agent_continuity.py
|
|
161
|
+
tests/test_base_agent_mcp_connectors.py
|
|
162
|
+
tests/test_base_agent_memory.py
|
|
163
|
+
tests/test_base_agent_skills.py
|
|
164
|
+
tests/test_cli.py
|
|
165
|
+
tests/test_config.py
|
|
166
|
+
tests/test_config_file.py
|
|
167
|
+
tests/test_conftest_eval_isolation.py
|
|
168
|
+
tests/test_display_subscriber.py
|
|
169
|
+
tests/test_event_bus.py
|
|
170
|
+
tests/test_mcp_connectors.py
|
|
171
|
+
tests/test_notify.py
|
|
172
|
+
tests/test_output.py
|
|
173
|
+
tests/test_parallel_agents.py
|
|
174
|
+
tests/test_preflight.py
|
|
175
|
+
tests/test_run.py
|
|
176
|
+
tests/test_run_context.py
|
|
177
|
+
tests/test_skills_discovery.py
|
|
178
|
+
tests/test_skills_prompt.py
|
|
179
|
+
tests/test_skills_thirdparty.py
|
|
180
|
+
tests/test_smoke_workflow.py
|
|
181
|
+
tests/test_tool_summary.py
|
|
182
|
+
tests/test_workflow_registry.py
|
|
183
|
+
tests/test_workspace.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
src
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "jeanclode"
|
|
3
|
+
version = "0.1.0" # x-release-please-version
|
|
4
|
+
description = "Multi-plugin CLI — auto-detects what to do from URLs and invokes Claude Code skills"
|
|
5
|
+
authors = [{ name = "Jeanclode Contributors" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.14"
|
|
8
|
+
license = "AGPL-3.0"
|
|
9
|
+
keywords = ["sentry", "ai", "llm", "bug-fix", "automation", "cli"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.14",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
dependencies = [
|
|
18
|
+
"claude-agent-sdk>=0.2.144",
|
|
19
|
+
"jinja2>=3.1.0",
|
|
20
|
+
"pydantic>=2.9.0",
|
|
21
|
+
"rich>=13.0.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
jeanclode = "src.main:cli_entry"
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://jeanclode.com"
|
|
29
|
+
Repository = "https://github.com/jeanclode-hq/jeanclode"
|
|
30
|
+
Issues = "https://github.com/jeanclode-hq/jeanclode/issues"
|
|
31
|
+
Documentation = "https://jeanclode.com/docs"
|
|
32
|
+
|
|
33
|
+
[build-system]
|
|
34
|
+
requires = ["setuptools>=75.0.0", "wheel"]
|
|
35
|
+
build-backend = "setuptools.build_meta"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
include = ["src", "src.*"]
|
|
39
|
+
exclude = ["tests*"]
|
|
40
|
+
|
|
41
|
+
# Ruff Configuration
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
target-version = "py314"
|
|
44
|
+
line-length = 100
|
|
45
|
+
fix = true
|
|
46
|
+
|
|
47
|
+
lint.select = [
|
|
48
|
+
"E", # pycodestyle errors
|
|
49
|
+
"W", # pycodestyle warnings
|
|
50
|
+
"F", # pyflakes
|
|
51
|
+
"I", # isort
|
|
52
|
+
"B", # flake8-bugbear
|
|
53
|
+
"C4", # flake8-comprehensions
|
|
54
|
+
"UP", # pyupgrade
|
|
55
|
+
"ARG", # flake8-unused-arguments
|
|
56
|
+
"SIM", # flake8-simplify
|
|
57
|
+
"PTH", # flake8-use-pathlib
|
|
58
|
+
"RUF", # Ruff-specific rules
|
|
59
|
+
"ASYNC", # flake8-async
|
|
60
|
+
]
|
|
61
|
+
lint.ignore = [
|
|
62
|
+
"E501", # line too long (handled by formatter)
|
|
63
|
+
"B008", # do not perform function calls in argument defaults
|
|
64
|
+
"UP007", # Use X | Y for type annotations (pydantic compatibility)
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.per-file-ignores]
|
|
68
|
+
"__init__.py" = ["F401"] # Unused imports in __init__.py
|
|
69
|
+
"tests/**/*.py" = ["ARG", "S101"] # Pytest uses assertions
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint.isort]
|
|
72
|
+
known-first-party = ["src"]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.format]
|
|
75
|
+
quote-style = "double"
|
|
76
|
+
indent-style = "space"
|
|
77
|
+
skip-magic-trailing-comma = false
|
|
78
|
+
line-ending = "auto"
|
|
79
|
+
|
|
80
|
+
# MyPy Configuration
|
|
81
|
+
[tool.mypy]
|
|
82
|
+
python_version = "3.14"
|
|
83
|
+
strict = true
|
|
84
|
+
pretty = true
|
|
85
|
+
show_error_codes = true
|
|
86
|
+
warn_return_any = true
|
|
87
|
+
warn_unused_configs = true
|
|
88
|
+
allow_redefinition = true
|
|
89
|
+
disallow_untyped_defs = true
|
|
90
|
+
disallow_any_generics = true
|
|
91
|
+
disallow_subclassing_any = true
|
|
92
|
+
disallow_untyped_calls = true
|
|
93
|
+
disallow_incomplete_defs = true
|
|
94
|
+
check_untyped_defs = true
|
|
95
|
+
no_implicit_optional = true
|
|
96
|
+
no_implicit_reexport = true
|
|
97
|
+
warn_redundant_casts = true
|
|
98
|
+
warn_unused_ignores = true
|
|
99
|
+
warn_no_return = true
|
|
100
|
+
warn_unreachable = true
|
|
101
|
+
strict_equality = true
|
|
102
|
+
plugins = ["pydantic.mypy"]
|
|
103
|
+
mypy_path = "$MYPY_CONFIG_FILE_DIR"
|
|
104
|
+
namespace_packages = true
|
|
105
|
+
explicit_package_bases = true
|
|
106
|
+
local_partial_types = true
|
|
107
|
+
|
|
108
|
+
exclude = "^(tests|docs|venv|env|scripts)/"
|
|
109
|
+
|
|
110
|
+
[[tool.mypy.overrides]]
|
|
111
|
+
module = ["claude_agent_sdk.*"]
|
|
112
|
+
ignore_missing_imports = true
|
|
113
|
+
|
|
114
|
+
[tool.pydantic-mypy]
|
|
115
|
+
init_forbid_extra = true
|
|
116
|
+
init_typed = true
|
|
117
|
+
warn_required_dynamic_aliases = true
|
|
118
|
+
|
|
119
|
+
# Pytest Configuration
|
|
120
|
+
[tool.pytest.ini_options]
|
|
121
|
+
minversion = "8.0"
|
|
122
|
+
addopts = [
|
|
123
|
+
"-ra",
|
|
124
|
+
"--strict-markers",
|
|
125
|
+
"--strict-config",
|
|
126
|
+
"--cov=src",
|
|
127
|
+
"--cov-report=term-missing",
|
|
128
|
+
"-m not eval",
|
|
129
|
+
]
|
|
130
|
+
testpaths = ["tests"]
|
|
131
|
+
pythonpath = ["."]
|
|
132
|
+
asyncio_mode = "auto"
|
|
133
|
+
markers = [
|
|
134
|
+
"eval: LLM evaluation tests — opt-in, require GOOGLE_API_KEY or GEMINI_API_KEY",
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
# Coverage Configuration
|
|
138
|
+
[tool.coverage.run]
|
|
139
|
+
source = ["src"]
|
|
140
|
+
branch = true
|
|
141
|
+
omit = ["*/tests/*", "*/__init__.py"]
|
|
142
|
+
|
|
143
|
+
[tool.coverage.report]
|
|
144
|
+
exclude_lines = [
|
|
145
|
+
"pragma: no cover",
|
|
146
|
+
"def __repr__",
|
|
147
|
+
"raise AssertionError",
|
|
148
|
+
"raise NotImplementedError",
|
|
149
|
+
"if __name__ == .__main__.:",
|
|
150
|
+
"if TYPE_CHECKING:",
|
|
151
|
+
"class .*\\bProtocol\\):",
|
|
152
|
+
"@(abc\\.)?abstractmethod",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
[dependency-groups]
|
|
156
|
+
dev = [
|
|
157
|
+
"pytest>=9.0.2",
|
|
158
|
+
"pytest-asyncio>=1.2.0",
|
|
159
|
+
"pytest-cov>=7.0.0",
|
|
160
|
+
"pytest-mock>=3.15.1",
|
|
161
|
+
"ruff>=0.15.4",
|
|
162
|
+
"mypy>=1.19.1",
|
|
163
|
+
"setuptools>=82.0.0",
|
|
164
|
+
"deepeval>=2.0.0",
|
|
165
|
+
"litellm>=1.0.0",
|
|
166
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""ci-watch — verify a pushed fix against the target repo's own CI.
|
|
2
|
+
|
|
3
|
+
Enforced via ``src.agents.hooks.require_ci_pass_hook`` — a PreToolUse hook on
|
|
4
|
+
the schema-bound fixer's ``StructuredOutput`` call, not an agent-callable
|
|
5
|
+
tool (a tool call is something a model can skip; this hook fires on every
|
|
6
|
+
attempt to finalize the turn and can't be opted out of). It gates
|
|
7
|
+
``StructuredOutput`` rather than ``Stop`` because a ``Stop`` block lands
|
|
8
|
+
after the structured output is already submitted and the CLI drops it. See
|
|
9
|
+
``docs/adr/008-ci-gated-fix-verification.md`` for the design rationale.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from src.activities.ci_watch.poll import check_ci
|
|
13
|
+
from src.activities.ci_watch.schemas import CheckResult, CiWatchResult
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"CheckResult",
|
|
17
|
+
"CiWatchResult",
|
|
18
|
+
"check_ci",
|
|
19
|
+
]
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""GitHub CI discovery — Checks API + legacy Status API, via `gh`.
|
|
2
|
+
|
|
3
|
+
Both are queried and merged: modern checks (Actions, most integrations) live
|
|
4
|
+
in the Checks API, older/third-party CI in the legacy Status API. `gh`
|
|
5
|
+
infers `{owner}/{repo}` from the git remote via its own placeholder
|
|
6
|
+
substitution, so no repo slug is threaded through these functions.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import subprocess
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from src.activities.ci_watch.schemas import CheckBucket, CheckResult, truncate_log
|
|
18
|
+
|
|
19
|
+
_BLOCKED_CONCLUSIONS = {"action_required", "stale", "skipped", "neutral"}
|
|
20
|
+
_FAILURE_CONCLUSIONS = {"failure", "cancelled", "timed_out"}
|
|
21
|
+
|
|
22
|
+
# GitHub's check-runs/status list endpoints default to 30 per page; 100 is
|
|
23
|
+
# the max. Same failure mode verified live on GitLab's job list (a 25-job
|
|
24
|
+
# pipeline silently lost its one real failure past the default page of 20).
|
|
25
|
+
_MAX_PER_PAGE = 100
|
|
26
|
+
|
|
27
|
+
# An Actions check-run's details_url looks like
|
|
28
|
+
# https://github.com/{owner}/{repo}/actions/runs/{run_id}/job/{job_id} —
|
|
29
|
+
# verified against a live check-run. Non-Actions apps (CodeQL, third-party
|
|
30
|
+
# CI) point elsewhere and simply won't match.
|
|
31
|
+
_ACTIONS_RUN_JOB_RE = re.compile(r"/actions/runs/(?P<run_id>\d+)/job/(?P<job_id>\d+)")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _run_gh(args: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str]:
|
|
35
|
+
return subprocess.run(["gh", *args], cwd=cwd, capture_output=True, text=True, check=False)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _api_json(args: list[str], *, cwd: Path) -> Any | None:
|
|
39
|
+
"""Run `gh api ...` and return parsed JSON, or None on any failure."""
|
|
40
|
+
proc = _run_gh(args, cwd=cwd)
|
|
41
|
+
if proc.returncode != 0 or not proc.stdout.strip():
|
|
42
|
+
return None
|
|
43
|
+
try:
|
|
44
|
+
return json.loads(proc.stdout)
|
|
45
|
+
except json.JSONDecodeError:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _bucket_for_conclusion(conclusion: str) -> CheckBucket:
|
|
50
|
+
if conclusion == "success":
|
|
51
|
+
return "success"
|
|
52
|
+
if conclusion in _FAILURE_CONCLUSIONS:
|
|
53
|
+
return "failure"
|
|
54
|
+
if conclusion in _BLOCKED_CONCLUSIONS:
|
|
55
|
+
return "blocked"
|
|
56
|
+
return "running"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def list_check_runs(ref: str, *, cwd: Path) -> list[dict[str, Any]]:
|
|
60
|
+
"""Return every check-run on `ref` (a SHA or branch name)."""
|
|
61
|
+
data = _api_json(
|
|
62
|
+
["api", f"repos/{{owner}}/{{repo}}/commits/{ref}/check-runs?per_page={_MAX_PER_PAGE}"],
|
|
63
|
+
cwd=cwd,
|
|
64
|
+
)
|
|
65
|
+
return data.get("check_runs", []) if isinstance(data, dict) else []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def combined_status(ref: str, *, cwd: Path) -> list[dict[str, Any]]:
|
|
69
|
+
"""Return every legacy Status API entry on `ref`."""
|
|
70
|
+
data = _api_json(
|
|
71
|
+
["api", f"repos/{{owner}}/{{repo}}/commits/{ref}/status?per_page={_MAX_PER_PAGE}"], cwd=cwd
|
|
72
|
+
)
|
|
73
|
+
return data.get("statuses", []) if isinstance(data, dict) else []
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def required_check_names(branch: str, *, cwd: Path) -> set[str] | None:
|
|
77
|
+
"""Best-effort: names of checks that actually block merging on `branch`.
|
|
78
|
+
|
|
79
|
+
Requires admin-level read access to branch protection, which we may not
|
|
80
|
+
have. Returns None on any failure (403/404/malformed) so the caller can
|
|
81
|
+
fall back to treating every discovered check as required.
|
|
82
|
+
"""
|
|
83
|
+
data = _api_json(
|
|
84
|
+
["api", f"repos/{{owner}}/{{repo}}/branches/{branch}/protection/required_status_checks"],
|
|
85
|
+
cwd=cwd,
|
|
86
|
+
)
|
|
87
|
+
if not isinstance(data, dict):
|
|
88
|
+
return None
|
|
89
|
+
contexts = data.get("contexts") or []
|
|
90
|
+
return set(contexts) if contexts else None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def failed_run_log(details_url: str, *, cwd: Path) -> str:
|
|
94
|
+
"""Best-effort failing-step log for a GitHub Actions check-run.
|
|
95
|
+
|
|
96
|
+
Parses the run/job id straight out of the check-run's own `details_url`
|
|
97
|
+
rather than matching by name: a check-run's `name` is per-job (e.g.
|
|
98
|
+
"test-backend"), but `gh run list` only exposes per-*workflow* names
|
|
99
|
+
(e.g. "CI") — they never match. `gh run view --log-failed` then handles
|
|
100
|
+
the zip/redirect dance the raw Actions logs API otherwise needs.
|
|
101
|
+
Non-Actions checks (CodeQL, third-party apps) have a different
|
|
102
|
+
`details_url` shape and simply return "".
|
|
103
|
+
"""
|
|
104
|
+
match = _ACTIONS_RUN_JOB_RE.search(details_url)
|
|
105
|
+
if not match:
|
|
106
|
+
return ""
|
|
107
|
+
proc = _run_gh(
|
|
108
|
+
["run", "view", match["run_id"], "--job", match["job_id"], "--log-failed"],
|
|
109
|
+
cwd=cwd,
|
|
110
|
+
)
|
|
111
|
+
if proc.returncode != 0:
|
|
112
|
+
return ""
|
|
113
|
+
return truncate_log(proc.stdout)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def discover(ref: str, *, cwd: Path, fetch_logs: bool = True) -> list[CheckResult]:
|
|
117
|
+
"""Discover every check on `ref` (the head commit's SHA) across both
|
|
118
|
+
GitHub signal APIs.
|
|
119
|
+
|
|
120
|
+
Required-check weighting is applied by the caller (`poll.py`), which
|
|
121
|
+
knows the actual PR branch — required-status-checks are keyed by branch,
|
|
122
|
+
not SHA, so that step can't happen in here.
|
|
123
|
+
"""
|
|
124
|
+
results: list[CheckResult] = []
|
|
125
|
+
for run in list_check_runs(ref, cwd=cwd):
|
|
126
|
+
status = run.get("status")
|
|
127
|
+
conclusion = run.get("conclusion") or ""
|
|
128
|
+
bucket: CheckBucket = (
|
|
129
|
+
"running" if status != "completed" else _bucket_for_conclusion(conclusion)
|
|
130
|
+
)
|
|
131
|
+
name = run.get("name", "")
|
|
132
|
+
details_url = run.get("details_url", "")
|
|
133
|
+
log_excerpt = ""
|
|
134
|
+
if fetch_logs and bucket == "failure":
|
|
135
|
+
log_excerpt = failed_run_log(details_url, cwd=cwd)
|
|
136
|
+
results.append(
|
|
137
|
+
CheckResult(
|
|
138
|
+
name=name,
|
|
139
|
+
bucket=bucket,
|
|
140
|
+
raw_conclusion=conclusion or status or "",
|
|
141
|
+
log_excerpt=log_excerpt,
|
|
142
|
+
details_url=details_url,
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
status_buckets: dict[str, CheckBucket] = {"success": "success", "pending": "running"}
|
|
146
|
+
for status_entry in combined_status(ref, cwd=cwd):
|
|
147
|
+
state = status_entry.get("state", "")
|
|
148
|
+
status_bucket = status_buckets.get(state, "failure")
|
|
149
|
+
results.append(
|
|
150
|
+
CheckResult(
|
|
151
|
+
name=status_entry.get("context", ""),
|
|
152
|
+
bucket=status_bucket,
|
|
153
|
+
raw_conclusion=state,
|
|
154
|
+
details_url=status_entry.get("target_url", ""),
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
return results
|