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.
Files changed (185) hide show
  1. jeanclode-0.1.0/PKG-INFO +21 -0
  2. jeanclode-0.1.0/jeanclode.egg-info/PKG-INFO +21 -0
  3. jeanclode-0.1.0/jeanclode.egg-info/SOURCES.txt +183 -0
  4. jeanclode-0.1.0/jeanclode.egg-info/dependency_links.txt +1 -0
  5. jeanclode-0.1.0/jeanclode.egg-info/entry_points.txt +2 -0
  6. jeanclode-0.1.0/jeanclode.egg-info/requires.txt +4 -0
  7. jeanclode-0.1.0/jeanclode.egg-info/top_level.txt +1 -0
  8. jeanclode-0.1.0/pyproject.toml +166 -0
  9. jeanclode-0.1.0/setup.cfg +4 -0
  10. jeanclode-0.1.0/src/__init__.py +8 -0
  11. jeanclode-0.1.0/src/activities/__init__.py +5 -0
  12. jeanclode-0.1.0/src/activities/ci_watch/__init__.py +19 -0
  13. jeanclode-0.1.0/src/activities/ci_watch/github.py +157 -0
  14. jeanclode-0.1.0/src/activities/ci_watch/gitlab.py +120 -0
  15. jeanclode-0.1.0/src/activities/ci_watch/poll.py +137 -0
  16. jeanclode-0.1.0/src/activities/ci_watch/schemas.py +43 -0
  17. jeanclode-0.1.0/src/activities/decorator.py +111 -0
  18. jeanclode-0.1.0/src/activities/git/__init__.py +29 -0
  19. jeanclode-0.1.0/src/activities/git/ops.py +195 -0
  20. jeanclode-0.1.0/src/activities/git/pr.py +178 -0
  21. jeanclode-0.1.0/src/activities/git/schemas.py +22 -0
  22. jeanclode-0.1.0/src/activities/git/targets.py +79 -0
  23. jeanclode-0.1.0/src/activities/issue/__init__.py +11 -0
  24. jeanclode-0.1.0/src/activities/issue/comment.py +73 -0
  25. jeanclode-0.1.0/src/activities/issue/fetch.py +188 -0
  26. jeanclode-0.1.0/src/activities/issue/schemas.py +17 -0
  27. jeanclode-0.1.0/src/activities/issue/utils.py +12 -0
  28. jeanclode-0.1.0/src/activities/notify.py +130 -0
  29. jeanclode-0.1.0/src/activities/respond/__init__.py +21 -0
  30. jeanclode-0.1.0/src/activities/respond/relabel.py +58 -0
  31. jeanclode-0.1.0/src/activities/respond/schemas.py +42 -0
  32. jeanclode-0.1.0/src/activities/respond/threads.py +149 -0
  33. jeanclode-0.1.0/src/activities/review/__init__.py +16 -0
  34. jeanclode-0.1.0/src/activities/review/dedup.py +18 -0
  35. jeanclode-0.1.0/src/activities/review/guardrail.py +44 -0
  36. jeanclode-0.1.0/src/activities/review/posting.py +407 -0
  37. jeanclode-0.1.0/src/activities/review/recovery.py +128 -0
  38. jeanclode-0.1.0/src/activities/review/routing.py +50 -0
  39. jeanclode-0.1.0/src/activities/review/schemas.py +111 -0
  40. jeanclode-0.1.0/src/activities/review/styling.py +25 -0
  41. jeanclode-0.1.0/src/activities/sentry/__init__.py +47 -0
  42. jeanclode-0.1.0/src/activities/sentry/fetch.py +208 -0
  43. jeanclode-0.1.0/src/activities/sentry/git.py +9 -0
  44. jeanclode-0.1.0/src/activities/sentry/pr.py +3 -0
  45. jeanclode-0.1.0/src/activities/sentry/route.py +139 -0
  46. jeanclode-0.1.0/src/activities/sentry/schemas.py +152 -0
  47. jeanclode-0.1.0/src/activities/sentry/secrets.py +57 -0
  48. jeanclode-0.1.0/src/activities/summary/__init__.py +17 -0
  49. jeanclode-0.1.0/src/activities/summary/format_summary.py +13 -0
  50. jeanclode-0.1.0/src/activities/summary/schemas.py +54 -0
  51. jeanclode-0.1.0/src/activities/summary/update_description.py +78 -0
  52. jeanclode-0.1.0/src/adaptors/__init__.py +5 -0
  53. jeanclode-0.1.0/src/adaptors/base.py +85 -0
  54. jeanclode-0.1.0/src/adaptors/diffn.py +68 -0
  55. jeanclode-0.1.0/src/adaptors/discussions.py +103 -0
  56. jeanclode-0.1.0/src/adaptors/display.py +50 -0
  57. jeanclode-0.1.0/src/adaptors/github/__init__.py +1 -0
  58. jeanclode-0.1.0/src/adaptors/github/adaptor.py +83 -0
  59. jeanclode-0.1.0/src/adaptors/github/auth.py +36 -0
  60. jeanclode-0.1.0/src/adaptors/github/client.py +92 -0
  61. jeanclode-0.1.0/src/adaptors/github/context.py +477 -0
  62. jeanclode-0.1.0/src/adaptors/github/display.py +338 -0
  63. jeanclode-0.1.0/src/adaptors/github/resolver.py +24 -0
  64. jeanclode-0.1.0/src/adaptors/github/sha.py +100 -0
  65. jeanclode-0.1.0/src/adaptors/gitlab/__init__.py +1 -0
  66. jeanclode-0.1.0/src/adaptors/gitlab/adaptor.py +79 -0
  67. jeanclode-0.1.0/src/adaptors/gitlab/auth.py +49 -0
  68. jeanclode-0.1.0/src/adaptors/gitlab/client.py +77 -0
  69. jeanclode-0.1.0/src/adaptors/gitlab/context.py +388 -0
  70. jeanclode-0.1.0/src/adaptors/gitlab/display.py +9 -0
  71. jeanclode-0.1.0/src/adaptors/gitlab/resolver.py +24 -0
  72. jeanclode-0.1.0/src/adaptors/gitlab/sha.py +73 -0
  73. jeanclode-0.1.0/src/adaptors/registry.py +31 -0
  74. jeanclode-0.1.0/src/adaptors/sentry/__init__.py +5 -0
  75. jeanclode-0.1.0/src/adaptors/sentry/adaptor.py +71 -0
  76. jeanclode-0.1.0/src/adaptors/sentry/auth.py +31 -0
  77. jeanclode-0.1.0/src/adaptors/sentry/client.py +78 -0
  78. jeanclode-0.1.0/src/adaptors/sentry/display.py +41 -0
  79. jeanclode-0.1.0/src/adaptors/sentry/resolver.py +71 -0
  80. jeanclode-0.1.0/src/agents/__init__.py +6 -0
  81. jeanclode-0.1.0/src/agents/_memory_backend.py +151 -0
  82. jeanclode-0.1.0/src/agents/base.py +430 -0
  83. jeanclode-0.1.0/src/agents/hooks.py +481 -0
  84. jeanclode-0.1.0/src/agents/issue/__init__.py +21 -0
  85. jeanclode-0.1.0/src/agents/issue/base.py +21 -0
  86. jeanclode-0.1.0/src/agents/issue/fixer.py +22 -0
  87. jeanclode-0.1.0/src/agents/issue/schemas.py +84 -0
  88. jeanclode-0.1.0/src/agents/issue/triage.py +20 -0
  89. jeanclode-0.1.0/src/agents/memory_tool.py +248 -0
  90. jeanclode-0.1.0/src/agents/respond/__init__.py +10 -0
  91. jeanclode-0.1.0/src/agents/respond/base.py +21 -0
  92. jeanclode-0.1.0/src/agents/respond/planner.py +31 -0
  93. jeanclode-0.1.0/src/agents/respond/schemas.py +47 -0
  94. jeanclode-0.1.0/src/agents/review/__init__.py +21 -0
  95. jeanclode-0.1.0/src/agents/review/analyzer.py +24 -0
  96. jeanclode-0.1.0/src/agents/review/base.py +12 -0
  97. jeanclode-0.1.0/src/agents/review/deduplicator.py +22 -0
  98. jeanclode-0.1.0/src/agents/review/fact_checker.py +26 -0
  99. jeanclode-0.1.0/src/agents/review/issue_explorer.py +22 -0
  100. jeanclode-0.1.0/src/agents/review/schemas.py +22 -0
  101. jeanclode-0.1.0/src/agents/review/styler.py +21 -0
  102. jeanclode-0.1.0/src/agents/review/synthesizer.py +23 -0
  103. jeanclode-0.1.0/src/agents/schemas.py +46 -0
  104. jeanclode-0.1.0/src/agents/sentry/__init__.py +17 -0
  105. jeanclode-0.1.0/src/agents/sentry/base.py +21 -0
  106. jeanclode-0.1.0/src/agents/sentry/fixer.py +43 -0
  107. jeanclode-0.1.0/src/agents/sentry/synthesis.py +24 -0
  108. jeanclode-0.1.0/src/agents/sentry/triage.py +29 -0
  109. jeanclode-0.1.0/src/agents/summary/__init__.py +9 -0
  110. jeanclode-0.1.0/src/agents/summary/base.py +21 -0
  111. jeanclode-0.1.0/src/agents/summary/parser.py +20 -0
  112. jeanclode-0.1.0/src/agents/summary/schemas.py +15 -0
  113. jeanclode-0.1.0/src/agents/summary/summarizer.py +21 -0
  114. jeanclode-0.1.0/src/agents/utils.py +84 -0
  115. jeanclode-0.1.0/src/cli.py +182 -0
  116. jeanclode-0.1.0/src/config.py +142 -0
  117. jeanclode-0.1.0/src/config_file.py +179 -0
  118. jeanclode-0.1.0/src/log.py +50 -0
  119. jeanclode-0.1.0/src/main.py +38 -0
  120. jeanclode-0.1.0/src/output.py +429 -0
  121. jeanclode-0.1.0/src/plugin_paths.py +25 -0
  122. jeanclode-0.1.0/src/runner/__init__.py +5 -0
  123. jeanclode-0.1.0/src/runner/display_subscriber.py +128 -0
  124. jeanclode-0.1.0/src/runner/preflight.py +238 -0
  125. jeanclode-0.1.0/src/runner/run.py +310 -0
  126. jeanclode-0.1.0/src/runtime/__init__.py +25 -0
  127. jeanclode-0.1.0/src/runtime/bots.py +29 -0
  128. jeanclode-0.1.0/src/runtime/bus.py +39 -0
  129. jeanclode-0.1.0/src/runtime/context.py +53 -0
  130. jeanclode-0.1.0/src/runtime/events.py +65 -0
  131. jeanclode-0.1.0/src/runtime/mcp_connectors.py +74 -0
  132. jeanclode-0.1.0/src/runtime/notify.py +94 -0
  133. jeanclode-0.1.0/src/skills/__init__.py +30 -0
  134. jeanclode-0.1.0/src/skills/discovery.py +99 -0
  135. jeanclode-0.1.0/src/skills/prompt.py +123 -0
  136. jeanclode-0.1.0/src/skills/schemas.py +23 -0
  137. jeanclode-0.1.0/src/skills/thirdparty.py +154 -0
  138. jeanclode-0.1.0/src/workflows/__init__.py +29 -0
  139. jeanclode-0.1.0/src/workflows/_smoke/__init__.py +0 -0
  140. jeanclode-0.1.0/src/workflows/_smoke/echo.py +55 -0
  141. jeanclode-0.1.0/src/workflows/base.py +97 -0
  142. jeanclode-0.1.0/src/workflows/code_review/__init__.py +1 -0
  143. jeanclode-0.1.0/src/workflows/code_review/runner.py +315 -0
  144. jeanclode-0.1.0/src/workflows/code_review/utils.py +205 -0
  145. jeanclode-0.1.0/src/workflows/issue_resolve/__init__.py +1 -0
  146. jeanclode-0.1.0/src/workflows/issue_resolve/runner.py +362 -0
  147. jeanclode-0.1.0/src/workflows/issue_resolve/utils.py +141 -0
  148. jeanclode-0.1.0/src/workflows/jeanclode_respond/__init__.py +5 -0
  149. jeanclode-0.1.0/src/workflows/jeanclode_respond/runner.py +212 -0
  150. jeanclode-0.1.0/src/workflows/jeanclode_respond/utils.py +150 -0
  151. jeanclode-0.1.0/src/workflows/pr_summary/__init__.py +5 -0
  152. jeanclode-0.1.0/src/workflows/pr_summary/runner.py +126 -0
  153. jeanclode-0.1.0/src/workflows/pr_summary/utils.py +138 -0
  154. jeanclode-0.1.0/src/workflows/schemas.py +13 -0
  155. jeanclode-0.1.0/src/workflows/sentry_fix/__init__.py +5 -0
  156. jeanclode-0.1.0/src/workflows/sentry_fix/runner.py +425 -0
  157. jeanclode-0.1.0/src/workflows/sentry_fix/utils.py +235 -0
  158. jeanclode-0.1.0/src/workflows/utils.py +9 -0
  159. jeanclode-0.1.0/src/workspace.py +241 -0
  160. jeanclode-0.1.0/tests/test_activity_decorator.py +85 -0
  161. jeanclode-0.1.0/tests/test_base_agent.py +368 -0
  162. jeanclode-0.1.0/tests/test_base_agent_continuity.py +97 -0
  163. jeanclode-0.1.0/tests/test_base_agent_mcp_connectors.py +190 -0
  164. jeanclode-0.1.0/tests/test_base_agent_memory.py +140 -0
  165. jeanclode-0.1.0/tests/test_base_agent_skills.py +153 -0
  166. jeanclode-0.1.0/tests/test_cli.py +248 -0
  167. jeanclode-0.1.0/tests/test_config.py +209 -0
  168. jeanclode-0.1.0/tests/test_config_file.py +111 -0
  169. jeanclode-0.1.0/tests/test_conftest_eval_isolation.py +35 -0
  170. jeanclode-0.1.0/tests/test_display_subscriber.py +101 -0
  171. jeanclode-0.1.0/tests/test_event_bus.py +47 -0
  172. jeanclode-0.1.0/tests/test_mcp_connectors.py +86 -0
  173. jeanclode-0.1.0/tests/test_notify.py +97 -0
  174. jeanclode-0.1.0/tests/test_output.py +259 -0
  175. jeanclode-0.1.0/tests/test_parallel_agents.py +69 -0
  176. jeanclode-0.1.0/tests/test_preflight.py +205 -0
  177. jeanclode-0.1.0/tests/test_run.py +104 -0
  178. jeanclode-0.1.0/tests/test_run_context.py +54 -0
  179. jeanclode-0.1.0/tests/test_skills_discovery.py +101 -0
  180. jeanclode-0.1.0/tests/test_skills_prompt.py +108 -0
  181. jeanclode-0.1.0/tests/test_skills_thirdparty.py +180 -0
  182. jeanclode-0.1.0/tests/test_smoke_workflow.py +110 -0
  183. jeanclode-0.1.0/tests/test_tool_summary.py +44 -0
  184. jeanclode-0.1.0/tests/test_workflow_registry.py +94 -0
  185. jeanclode-0.1.0/tests/test_workspace.py +119 -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,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,2 @@
1
+ [console_scripts]
2
+ jeanclode = src.main:cli_entry
@@ -0,0 +1,4 @@
1
+ claude-agent-sdk>=0.2.144
2
+ jinja2>=3.1.0
3
+ pydantic>=2.9.0
4
+ rich>=13.0.0
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """Jeanclode CLI — triage Sentry errors and invoke Claude Code to fix bugs."""
2
+
3
+ import importlib.metadata
4
+
5
+ try:
6
+ __version__: str = importlib.metadata.version("jeanclode")
7
+ except importlib.metadata.PackageNotFoundError:
8
+ __version__ = "0.0.0-dev"
@@ -0,0 +1,5 @@
1
+ """Activities — small deterministic units of work composed by workflows."""
2
+
3
+ from src.activities.decorator import activity
4
+
5
+ __all__ = ["activity"]
@@ -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