tg-agent 0.1.12__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 (234) hide show
  1. tg_agent-0.1.12/.codex-staging/feature-dev/README.md +15 -0
  2. tg_agent-0.1.12/.codex-staging/feature-dev/SKILL.md +120 -0
  3. tg_agent-0.1.12/.codex-staging/feature-dev/references/code-architect.md +17 -0
  4. tg_agent-0.1.12/.codex-staging/feature-dev/references/code-explorer.md +17 -0
  5. tg_agent-0.1.12/.codex-staging/feature-dev/references/code-reviewer.md +17 -0
  6. tg_agent-0.1.12/.devcontainer/devcontainer.json +6 -0
  7. tg_agent-0.1.12/.dockerignore +13 -0
  8. tg_agent-0.1.12/.env.example +23 -0
  9. tg_agent-0.1.12/.github/copilot-instructions.md +41 -0
  10. tg_agent-0.1.12/.github/release-metadata/README.md +22 -0
  11. tg_agent-0.1.12/.github/release-metadata/v0.1.10.json +14 -0
  12. tg_agent-0.1.12/.github/release-metadata/v0.1.12.json +18 -0
  13. tg_agent-0.1.12/.github/release.yml +12 -0
  14. tg_agent-0.1.12/.github/scripts/render_release.py +131 -0
  15. tg_agent-0.1.12/.github/workflows/ci.yml +31 -0
  16. tg_agent-0.1.12/.github/workflows/claude-code-review.yml +44 -0
  17. tg_agent-0.1.12/.github/workflows/claude.yml +50 -0
  18. tg_agent-0.1.12/.github/workflows/release.yml +48 -0
  19. tg_agent-0.1.12/.gitignore +54 -0
  20. tg_agent-0.1.12/CLAUDE.md +98 -0
  21. tg_agent-0.1.12/Dockerfile +18 -0
  22. tg_agent-0.1.12/LICENSE +21 -0
  23. tg_agent-0.1.12/PKG-INFO +252 -0
  24. tg_agent-0.1.12/README.md +204 -0
  25. tg_agent-0.1.12/README.ru.md +204 -0
  26. tg_agent-0.1.12/config.yaml +34 -0
  27. tg_agent-0.1.12/conftest.py +47 -0
  28. tg_agent-0.1.12/data/deepagents_model_compatibility_catalog.json +210 -0
  29. tg_agent-0.1.12/docker-compose.yml +15 -0
  30. tg_agent-0.1.12/docs/testing/real-telegram.md +148 -0
  31. tg_agent-0.1.12/pyproject.toml +102 -0
  32. tg_agent-0.1.12/reports/generate_reports.py +260 -0
  33. tg_agent-0.1.12/reports/moto_rental_chart.html +83 -0
  34. tg_agent-0.1.12/reports/moto_rental_messages.xlsx +0 -0
  35. tg_agent-0.1.12/scripts/release_pypi.sh +116 -0
  36. tg_agent-0.1.12/setup.cfg +4 -0
  37. tg_agent-0.1.12/src/__init__.py +0 -0
  38. tg_agent-0.1.12/src/agent/__init__.py +0 -0
  39. tg_agent-0.1.12/src/agent/context.py +61 -0
  40. tg_agent-0.1.12/src/agent/manager.py +1084 -0
  41. tg_agent-0.1.12/src/agent/models.py +10 -0
  42. tg_agent-0.1.12/src/agent/provider_registry.py +283 -0
  43. tg_agent-0.1.12/src/agent/tools.py +57 -0
  44. tg_agent-0.1.12/src/cli/__init__.py +3 -0
  45. tg_agent-0.1.12/src/cli/commands/__init__.py +8 -0
  46. tg_agent-0.1.12/src/cli/commands/account.py +44 -0
  47. tg_agent-0.1.12/src/cli/commands/agent.py +196 -0
  48. tg_agent-0.1.12/src/cli/commands/channel.py +289 -0
  49. tg_agent-0.1.12/src/cli/commands/collect.py +55 -0
  50. tg_agent-0.1.12/src/cli/commands/common.py +15 -0
  51. tg_agent-0.1.12/src/cli/commands/filter.py +151 -0
  52. tg_agent-0.1.12/src/cli/commands/my_telegram.py +43 -0
  53. tg_agent-0.1.12/src/cli/commands/notification.py +47 -0
  54. tg_agent-0.1.12/src/cli/commands/photo_loader.py +170 -0
  55. tg_agent-0.1.12/src/cli/commands/scheduler.py +60 -0
  56. tg_agent-0.1.12/src/cli/commands/search.py +53 -0
  57. tg_agent-0.1.12/src/cli/commands/search_query.py +121 -0
  58. tg_agent-0.1.12/src/cli/commands/serve.py +36 -0
  59. tg_agent-0.1.12/src/cli/commands/server_control.py +38 -0
  60. tg_agent-0.1.12/src/cli/commands/test.py +686 -0
  61. tg_agent-0.1.12/src/cli/main.py +74 -0
  62. tg_agent-0.1.12/src/cli/parser.py +264 -0
  63. tg_agent-0.1.12/src/cli/process_control.py +221 -0
  64. tg_agent-0.1.12/src/cli/runtime.py +53 -0
  65. tg_agent-0.1.12/src/collection_queue.py +184 -0
  66. tg_agent-0.1.12/src/config.py +170 -0
  67. tg_agent-0.1.12/src/database/__init__.py +3 -0
  68. tg_agent-0.1.12/src/database/bundles.py +708 -0
  69. tg_agent-0.1.12/src/database/connection.py +34 -0
  70. tg_agent-0.1.12/src/database/facade.py +561 -0
  71. tg_agent-0.1.12/src/database/migrations.py +425 -0
  72. tg_agent-0.1.12/src/database/repositories/accounts.py +141 -0
  73. tg_agent-0.1.12/src/database/repositories/channel_stats.py +77 -0
  74. tg_agent-0.1.12/src/database/repositories/channels.py +211 -0
  75. tg_agent-0.1.12/src/database/repositories/collection_tasks.py +517 -0
  76. tg_agent-0.1.12/src/database/repositories/dialog_cache.py +112 -0
  77. tg_agent-0.1.12/src/database/repositories/filters.py +191 -0
  78. tg_agent-0.1.12/src/database/repositories/messages.py +414 -0
  79. tg_agent-0.1.12/src/database/repositories/notification_bots.py +63 -0
  80. tg_agent-0.1.12/src/database/repositories/photo_loader.py +420 -0
  81. tg_agent-0.1.12/src/database/repositories/search_log.py +31 -0
  82. tg_agent-0.1.12/src/database/repositories/search_queries.py +171 -0
  83. tg_agent-0.1.12/src/database/repositories/settings.py +21 -0
  84. tg_agent-0.1.12/src/database/schema.py +216 -0
  85. tg_agent-0.1.12/src/filters/__init__.py +4 -0
  86. tg_agent-0.1.12/src/filters/analyzer.py +227 -0
  87. tg_agent-0.1.12/src/filters/criteria.py +31 -0
  88. tg_agent-0.1.12/src/filters/models.py +23 -0
  89. tg_agent-0.1.12/src/main.py +81 -0
  90. tg_agent-0.1.12/src/models.py +235 -0
  91. tg_agent-0.1.12/src/parsers.py +82 -0
  92. tg_agent-0.1.12/src/scheduler/__init__.py +0 -0
  93. tg_agent-0.1.12/src/scheduler/manager.py +199 -0
  94. tg_agent-0.1.12/src/search/__init__.py +0 -0
  95. tg_agent-0.1.12/src/search/ai_search.py +110 -0
  96. tg_agent-0.1.12/src/search/engine.py +60 -0
  97. tg_agent-0.1.12/src/search/local_search.py +34 -0
  98. tg_agent-0.1.12/src/search/persistence.py +34 -0
  99. tg_agent-0.1.12/src/search/telegram_search.py +359 -0
  100. tg_agent-0.1.12/src/search/transformers.py +118 -0
  101. tg_agent-0.1.12/src/security/__init__.py +3 -0
  102. tg_agent-0.1.12/src/security/session_cipher.py +80 -0
  103. tg_agent-0.1.12/src/services/__init__.py +1 -0
  104. tg_agent-0.1.12/src/services/account_service.py +44 -0
  105. tg_agent-0.1.12/src/services/agent_provider_service.py +1003 -0
  106. tg_agent-0.1.12/src/services/channel_service.py +129 -0
  107. tg_agent-0.1.12/src/services/collection_service.py +96 -0
  108. tg_agent-0.1.12/src/services/filter_deletion_service.py +86 -0
  109. tg_agent-0.1.12/src/services/notification_matcher.py +83 -0
  110. tg_agent-0.1.12/src/services/notification_service.py +94 -0
  111. tg_agent-0.1.12/src/services/notification_target_service.py +136 -0
  112. tg_agent-0.1.12/src/services/photo_auto_upload_service.py +135 -0
  113. tg_agent-0.1.12/src/services/photo_publish_service.py +72 -0
  114. tg_agent-0.1.12/src/services/photo_task_service.py +344 -0
  115. tg_agent-0.1.12/src/services/search_query_service.py +151 -0
  116. tg_agent-0.1.12/src/services/search_service.py +47 -0
  117. tg_agent-0.1.12/src/services/task_enqueuer.py +68 -0
  118. tg_agent-0.1.12/src/services/unified_dispatcher.py +348 -0
  119. tg_agent-0.1.12/src/settings_utils.py +19 -0
  120. tg_agent-0.1.12/src/telegram/__init__.py +0 -0
  121. tg_agent-0.1.12/src/telegram/account_lease_pool.py +127 -0
  122. tg_agent-0.1.12/src/telegram/auth.py +202 -0
  123. tg_agent-0.1.12/src/telegram/backends.py +256 -0
  124. tg_agent-0.1.12/src/telegram/botfather.py +80 -0
  125. tg_agent-0.1.12/src/telegram/client_pool.py +844 -0
  126. tg_agent-0.1.12/src/telegram/collector.py +896 -0
  127. tg_agent-0.1.12/src/telegram/notifier.py +89 -0
  128. tg_agent-0.1.12/src/telegram/session_materializer.py +70 -0
  129. tg_agent-0.1.12/src/telegram/utils.py +12 -0
  130. tg_agent-0.1.12/src/web/__init__.py +0 -0
  131. tg_agent-0.1.12/src/web/app.py +139 -0
  132. tg_agent-0.1.12/src/web/assembly.py +146 -0
  133. tg_agent-0.1.12/src/web/bootstrap.py +245 -0
  134. tg_agent-0.1.12/src/web/container.py +71 -0
  135. tg_agent-0.1.12/src/web/csrf.py +112 -0
  136. tg_agent-0.1.12/src/web/deps.py +315 -0
  137. tg_agent-0.1.12/src/web/log_handler.py +23 -0
  138. tg_agent-0.1.12/src/web/panel_auth.py +91 -0
  139. tg_agent-0.1.12/src/web/paths.py +7 -0
  140. tg_agent-0.1.12/src/web/routes/__init__.py +0 -0
  141. tg_agent-0.1.12/src/web/routes/agent.py +252 -0
  142. tg_agent-0.1.12/src/web/routes/auth.py +209 -0
  143. tg_agent-0.1.12/src/web/routes/channel_collection.py +207 -0
  144. tg_agent-0.1.12/src/web/routes/channels.py +79 -0
  145. tg_agent-0.1.12/src/web/routes/dashboard.py +28 -0
  146. tg_agent-0.1.12/src/web/routes/debug.py +26 -0
  147. tg_agent-0.1.12/src/web/routes/filter.py +204 -0
  148. tg_agent-0.1.12/src/web/routes/import_channels.py +130 -0
  149. tg_agent-0.1.12/src/web/routes/my_telegram.py +78 -0
  150. tg_agent-0.1.12/src/web/routes/photo_loader.py +513 -0
  151. tg_agent-0.1.12/src/web/routes/scheduler.py +155 -0
  152. tg_agent-0.1.12/src/web/routes/search.py +150 -0
  153. tg_agent-0.1.12/src/web/routes/search_queries.py +109 -0
  154. tg_agent-0.1.12/src/web/routes/settings.py +909 -0
  155. tg_agent-0.1.12/src/web/session.py +50 -0
  156. tg_agent-0.1.12/src/web/static/settings.js +424 -0
  157. tg_agent-0.1.12/src/web/static/style.css +243 -0
  158. tg_agent-0.1.12/src/web/template_globals.py +80 -0
  159. tg_agent-0.1.12/src/web/templates/_debug_logs.html +10 -0
  160. tg_agent-0.1.12/src/web/templates/agent.html +981 -0
  161. tg_agent-0.1.12/src/web/templates/base.html +211 -0
  162. tg_agent-0.1.12/src/web/templates/channels.html +295 -0
  163. tg_agent-0.1.12/src/web/templates/dashboard.html +53 -0
  164. tg_agent-0.1.12/src/web/templates/debug.html +16 -0
  165. tg_agent-0.1.12/src/web/templates/error.html +20 -0
  166. tg_agent-0.1.12/src/web/templates/filter_manage.html +155 -0
  167. tg_agent-0.1.12/src/web/templates/import_channels.html +68 -0
  168. tg_agent-0.1.12/src/web/templates/login.html +140 -0
  169. tg_agent-0.1.12/src/web/templates/my_telegram.html +339 -0
  170. tg_agent-0.1.12/src/web/templates/photo_loader.html +602 -0
  171. tg_agent-0.1.12/src/web/templates/scheduler.html +259 -0
  172. tg_agent-0.1.12/src/web/templates/search.html +181 -0
  173. tg_agent-0.1.12/src/web/templates/search_queries.html +243 -0
  174. tg_agent-0.1.12/src/web/templates/settings/_accounts.html +83 -0
  175. tg_agent-0.1.12/src/web/templates/settings/_credentials.html +41 -0
  176. tg_agent-0.1.12/src/web/templates/settings/_devmode.html +235 -0
  177. tg_agent-0.1.12/src/web/templates/settings/_filters.html +36 -0
  178. tg_agent-0.1.12/src/web/templates/settings/_notifications.html +50 -0
  179. tg_agent-0.1.12/src/web/templates/settings/_scheduler.html +11 -0
  180. tg_agent-0.1.12/src/web/templates/settings.html +112 -0
  181. tg_agent-0.1.12/src/web/templates/web_login.html +56 -0
  182. tg_agent-0.1.12/tasks/motobike_rental_search.md +100 -0
  183. tg_agent-0.1.12/tests/__init__.py +0 -0
  184. tg_agent-0.1.12/tests/conftest.py +336 -0
  185. tg_agent-0.1.12/tests/helpers.py +535 -0
  186. tg_agent-0.1.12/tests/test_account_service.py +112 -0
  187. tg_agent-0.1.12/tests/test_agent.py +574 -0
  188. tg_agent-0.1.12/tests/test_agent_errors.py +114 -0
  189. tg_agent-0.1.12/tests/test_agent_provider_service.py +817 -0
  190. tg_agent-0.1.12/tests/test_agent_tools.py +242 -0
  191. tg_agent-0.1.12/tests/test_ai_search.py +133 -0
  192. tg_agent-0.1.12/tests/test_auth.py +165 -0
  193. tg_agent-0.1.12/tests/test_bundles.py +352 -0
  194. tg_agent-0.1.12/tests/test_channel_stats.py +426 -0
  195. tg_agent-0.1.12/tests/test_cli.py +827 -0
  196. tg_agent-0.1.12/tests/test_cli_extended.py +560 -0
  197. tg_agent-0.1.12/tests/test_cli_extra.py +474 -0
  198. tg_agent-0.1.12/tests/test_client_pool.py +241 -0
  199. tg_agent-0.1.12/tests/test_client_pool_extended.py +282 -0
  200. tg_agent-0.1.12/tests/test_client_pool_runtime.py +193 -0
  201. tg_agent-0.1.12/tests/test_collector.py +1300 -0
  202. tg_agent-0.1.12/tests/test_collector_extended.py +189 -0
  203. tg_agent-0.1.12/tests/test_collector_runtime.py +126 -0
  204. tg_agent-0.1.12/tests/test_config.py +104 -0
  205. tg_agent-0.1.12/tests/test_database.py +1172 -0
  206. tg_agent-0.1.12/tests/test_filters.py +507 -0
  207. tg_agent-0.1.12/tests/test_import_web.py +177 -0
  208. tg_agent-0.1.12/tests/test_imports.py +43 -0
  209. tg_agent-0.1.12/tests/test_integration.py +856 -0
  210. tg_agent-0.1.12/tests/test_my_telegram.py +708 -0
  211. tg_agent-0.1.12/tests/test_notification.py +483 -0
  212. tg_agent-0.1.12/tests/test_notifier_extra.py +380 -0
  213. tg_agent-0.1.12/tests/test_packaging_release.py +18 -0
  214. tg_agent-0.1.12/tests/test_parsers.py +250 -0
  215. tg_agent-0.1.12/tests/test_photo_loader.py +1353 -0
  216. tg_agent-0.1.12/tests/test_photo_publish_runtime.py +78 -0
  217. tg_agent-0.1.12/tests/test_pool_harness_guardrails.py +34 -0
  218. tg_agent-0.1.12/tests/test_process_control.py +191 -0
  219. tg_agent-0.1.12/tests/test_real_telegram_policy.py +192 -0
  220. tg_agent-0.1.12/tests/test_scheduler.py +51 -0
  221. tg_agent-0.1.12/tests/test_scheduler_manager.py +194 -0
  222. tg_agent-0.1.12/tests/test_search.py +425 -0
  223. tg_agent-0.1.12/tests/test_search_queries.py +259 -0
  224. tg_agent-0.1.12/tests/test_session_cipher.py +84 -0
  225. tg_agent-0.1.12/tests/test_session_materializer.py +66 -0
  226. tg_agent-0.1.12/tests/test_transformers.py +114 -0
  227. tg_agent-0.1.12/tests/test_web.py +2995 -0
  228. tg_agent-0.1.12/tests/test_web_auth.py +544 -0
  229. tg_agent-0.1.12/tg_agent.egg-info/PKG-INFO +252 -0
  230. tg_agent-0.1.12/tg_agent.egg-info/SOURCES.txt +232 -0
  231. tg_agent-0.1.12/tg_agent.egg-info/dependency_links.txt +1 -0
  232. tg_agent-0.1.12/tg_agent.egg-info/entry_points.txt +2 -0
  233. tg_agent-0.1.12/tg_agent.egg-info/requires.txt +25 -0
  234. tg_agent-0.1.12/tg_agent.egg-info/top_level.txt +1 -0
@@ -0,0 +1,15 @@
1
+ # feature-dev
2
+
3
+ Codex skill adapted from the Claude `feature-dev` plugin.
4
+
5
+ It provides a structured workflow for feature delivery:
6
+
7
+ 1. Discovery
8
+ 2. Codebase exploration
9
+ 3. Clarifying questions
10
+ 4. Architecture design
11
+ 5. Implementation
12
+ 6. Quality review
13
+ 7. Summary
14
+
15
+ Primary entrypoint: [`SKILL.md`](./SKILL.md)
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: feature-dev
3
+ description: Structured feature development workflow with codebase exploration, architecture tradeoffs, implementation gating, and review
4
+ version: 1.0.0
5
+ metadata:
6
+ source: claude-plugins-official/feature-dev
7
+ ---
8
+
9
+ # Feature Dev Skill
10
+
11
+ Use this skill when the user wants to build a new feature and would benefit from a deliberate workflow instead of immediate implementation.
12
+
13
+ ## Goal
14
+
15
+ Drive feature work through seven phases:
16
+
17
+ 1. Discovery
18
+ 2. Codebase exploration
19
+ 3. Clarifying questions
20
+ 4. Architecture design
21
+ 5. Implementation
22
+ 6. Quality review
23
+ 7. Summary
24
+
25
+ ## Operating Rules
26
+
27
+ - Do not jump straight into code for ambiguous feature work.
28
+ - Read the codebase before proposing implementation details.
29
+ - Ask clarifying questions after exploration and before architecture selection.
30
+ - Present concrete tradeoffs, not abstract options.
31
+ - Do not start implementation until the user approves an approach.
32
+ - After implementation, run a review pass focused on bugs, regressions, and convention mismatches.
33
+
34
+ ## Workflow
35
+
36
+ ### 1. Discovery
37
+
38
+ - Restate the feature request in concrete terms.
39
+ - If the request is underspecified, ask what problem is being solved, required behavior, constraints, and non-goals.
40
+ - Create a task plan that tracks all seven phases.
41
+
42
+ ### 2. Codebase Exploration
43
+
44
+ Investigate the codebase from multiple angles. Parallelize local reads and searches when possible.
45
+
46
+ Cover at least these perspectives:
47
+ - Similar or adjacent features
48
+ - Relevant architecture layers and extension points
49
+ - Existing tests, conventions, and user flows
50
+
51
+ For each perspective, produce:
52
+ - Key files with path references
53
+ - Relevant control flow and abstractions
54
+ - Constraints that affect implementation
55
+
56
+ After exploration, read the most important files and summarize patterns that the new feature should follow.
57
+
58
+ ### 3. Clarifying Questions
59
+
60
+ Before designing the solution, identify ambiguities such as:
61
+ - Edge cases
62
+ - Error handling
63
+ - Scope boundaries
64
+ - Integration points
65
+ - Backward compatibility
66
+ - Performance or security requirements
67
+
68
+ Ask the user a concise grouped list of questions and wait for answers. If the user delegates the choice, give a recommendation and get explicit confirmation.
69
+
70
+ ### 4. Architecture Design
71
+
72
+ Develop 2-3 implementation approaches with distinct tradeoffs:
73
+ - Minimal changes
74
+ - Clean architecture
75
+ - Pragmatic balance
76
+
77
+ For each approach, include:
78
+ - Files to change or create
79
+ - Main abstractions
80
+ - Benefits
81
+ - Costs and risks
82
+
83
+ Recommend one approach and explain why it fits this codebase and request. Ask the user which approach to use.
84
+
85
+ ### 5. Implementation
86
+
87
+ Do not begin until the user explicitly approves an approach.
88
+
89
+ When implementing:
90
+ - Follow existing project patterns
91
+ - Keep changes scoped to the agreed design
92
+ - Update the task plan as work progresses
93
+ - Add tests when the codebase supports them
94
+
95
+ ### 6. Quality Review
96
+
97
+ Review the resulting changes from three lenses:
98
+ - Simplicity, DRY, and maintainability
99
+ - Bugs, correctness, and regressions
100
+ - Project conventions and abstraction fit
101
+
102
+ Report the highest-signal findings first. If issues exist, ask whether to fix now, defer, or proceed as-is.
103
+
104
+ ### 7. Summary
105
+
106
+ Close with:
107
+ - What was built
108
+ - Key decisions
109
+ - Main files changed
110
+ - Remaining risks or next steps
111
+
112
+ ## Specialist Lenses
113
+
114
+ Use these role prompts internally when useful:
115
+
116
+ - [code-explorer](references/code-explorer.md): trace feature flows and architecture
117
+ - [code-architect](references/code-architect.md): produce decisive implementation blueprints
118
+ - [code-reviewer](references/code-reviewer.md): review for real bugs and convention drift
119
+
120
+ You do not need separate agents to use this skill. Reuse the lenses as structured thinking modes during exploration, design, and review.
@@ -0,0 +1,17 @@
1
+ # Code Architect Lens
2
+
3
+ Use this lens to design a concrete implementation that fits the codebase.
4
+
5
+ ## Focus
6
+
7
+ - Reuse established patterns
8
+ - Pick a clear architecture rather than staying vague
9
+ - Map files to create or modify
10
+ - Call out interfaces, data flow, and sequencing
11
+
12
+ ## Output
13
+
14
+ - Recommended approach
15
+ - Tradeoffs
16
+ - File-by-file implementation map
17
+ - Build sequence and critical details
@@ -0,0 +1,17 @@
1
+ # Code Explorer Lens
2
+
3
+ Use this lens to understand how an existing feature works before changing it.
4
+
5
+ ## Focus
6
+
7
+ - Find entry points
8
+ - Trace execution flow end to end
9
+ - Identify important abstractions and dependencies
10
+ - Surface existing patterns worth reusing
11
+
12
+ ## Output
13
+
14
+ - Key files to read
15
+ - Step-by-step flow
16
+ - Data transformations
17
+ - Notable constraints, risks, or technical debt
@@ -0,0 +1,17 @@
1
+ # Code Reviewer Lens
2
+
3
+ Use this lens after implementation to review only meaningful issues.
4
+
5
+ ## Focus
6
+
7
+ - Functional correctness
8
+ - Regression risk
9
+ - Convention mismatches
10
+ - Simplicity and maintainability
11
+
12
+ ## Output
13
+
14
+ - High-signal findings only
15
+ - Concrete file references
16
+ - Specific fix guidance
17
+ - Residual risks if no issues are found
@@ -0,0 +1,6 @@
1
+ {
2
+ "image": "mcr.microsoft.com/devcontainers/universal:2",
3
+ "features": {
4
+ "ghcr.io/devcontainers/features/copilot-cli:1": {}
5
+ }
6
+ }
@@ -0,0 +1,13 @@
1
+ __pycache__
2
+ *.py[cod]
3
+ *.egg-info
4
+ .git
5
+ .env
6
+ .venv
7
+ venv
8
+ .pytest_cache
9
+ .ruff_cache
10
+ .mypy_cache
11
+ data/*.db
12
+ tests/
13
+ *.md
@@ -0,0 +1,23 @@
1
+ # Telegram API credentials (https://my.telegram.org/apps)
2
+ TG_API_ID=
3
+ TG_API_HASH=
4
+
5
+ # Web panel password (required)
6
+ WEB_PASS=
7
+
8
+ # Key for encrypting Telegram session strings in DB (enc:v2)
9
+ SESSION_ENCRYPTION_KEY=
10
+
11
+ # LLM for AI search (optional)
12
+ LLM_API_KEY=
13
+
14
+ # AI Agent — Claude API key and OAuth token
15
+ ANTHROPIC_API_KEY=
16
+ CLAUDE_CODE_OAUTH_TOKEN=
17
+ # AI Agent model override (optional, default: anthropic:claude-sonnet-4-6)
18
+ AGENT_MODEL=
19
+
20
+ # PyPI publishing (used by scripts/release_pypi.sh)
21
+ TWINE_USERNAME=__token__
22
+ TEST_PYPI_TOKEN=pypi-...
23
+ PYPI_TOKEN=pypi-...
@@ -0,0 +1,41 @@
1
+ # Copilot instructions
2
+
3
+ ## Build, test, and lint commands
4
+
5
+ - Install dev dependencies: `pip install -e ".[dev]"`
6
+ - Lint: `ruff check src/ tests/ conftest.py`
7
+ - Run parallel-safe tests: `pytest tests/ -v -m "not aiosqlite_serial" -n auto`
8
+ - Run `aiosqlite`-backed tests serially: `pytest tests/ -v -m aiosqlite_serial`
9
+ - Run a single test: `pytest tests/test_web.py::test_health_endpoint -v`
10
+ - Benchmark serial vs safe mixed-mode suite execution: `python -m src.main test benchmark`
11
+ - Run the web app locally: `python -m src.main serve [--web-pass PASS]`
12
+
13
+ ## High-level architecture
14
+
15
+ This project has three main layers: CLI/Web entry points, Telegram/search/scheduler services, and a single SQLite database.
16
+
17
+ - `src/main.py` is the main CLI entry point. CLI commands live under `src/cli/commands/`.
18
+ - `src/web/` is a FastAPI web UI over the same underlying logic. Keep CLI and web behavior aligned when changing features.
19
+ - The Telegram layer centers on `ClientPool` for multi-account rotation, `Collector` for message collection, and `Notifier` for Telegram alerts.
20
+ - Collection is orchestrated through `src/services/collection_service.py` and `src/collection_queue.py`: requests are queued, persisted as collection tasks, and processed by a single async worker.
21
+ - Search has multiple backends: local SQLite/FTS search, direct Telegram search, and AI-backed search/agent flows.
22
+ - Storage is a single SQLite database managed asynchronously with `aiosqlite`; schema creation and additive migrations happen in code.
23
+
24
+ ## Key conventions
25
+
26
+ - Preserve CLI/web parity: features exposed in the web UI should have corresponding CLI behavior and reuse shared logic.
27
+ - This codebase is async-first. Prefer `asyncio` patterns and existing async helpers over introducing sync shortcuts.
28
+ - Use Pydantic v2 APIs such as `model_validate`, not deprecated v1 patterns.
29
+ - Config values support `${ENV_VAR}` substitution, and keys whose value is only `${ENV_VAR}` are dropped entirely if the env var is empty or missing.
30
+ - Incremental collection matters: collection resumes from `last_collected_id` instead of reloading full history unless a full run is explicitly requested.
31
+ - `ClientPool` rotates around flood waits. Reuse its account-selection logic instead of bypassing it.
32
+ - Collection tasks are persisted and processed sequentially through `CollectionQueue`; keep task status transitions and cancellation behavior intact.
33
+ - Filtered channels are normally skipped during collection unless the flow explicitly uses `force=True`.
34
+ - The collector depends on Telegram entity cache warmup before some channel operations. Do not remove cache-priming behavior such as dialog loading without verifying PeerChannel resolution still works.
35
+ - Duplicate messages are tolerated through `INSERT OR IGNORE` with unique constraints; avoid replacing that behavior with manual dedup flows.
36
+ - Web auth is password-based and session tokens are custom HMAC-signed cookies backed by a secret stored in the database.
37
+ - When `SESSION_ENCRYPTION_KEY` is configured, session strings are stored encrypted (`enc:v2:*`); startup should fail fast if encrypted rows exist but the key is missing.
38
+ - Web routes use HTMX progressive enhancement patterns. When a route already branches on `HX-Request`, preserve fragment-vs-redirect behavior.
39
+ - Identifier import flows accept `t.me` links, `@usernames`, negative IDs, and text/file parsing through existing parser helpers; reuse those helpers instead of duplicating parsing logic.
40
+ - Real Telegram tests are opt-in only. Default tests should stay fake/harness-based. Any live pytest must use `real_telegram_sandbox` plus `@pytest.mark.real_tg_safe` or `@pytest.mark.real_tg_manual`.
41
+ - Parallel pytest runs use `pytest-xdist`, and the repository-level worker hook uses `joblib.cpu_count()` to pick `max(1, cpu_count - 1)`. Tests marked `aiosqlite_serial` must stay out of xdist and run in a separate serial pass; live Telegram runs are also forced back to a single worker.
@@ -0,0 +1,22 @@
1
+ # Release Metadata
2
+
3
+ Create one JSON file per release tag in this directory before pushing the tag.
4
+
5
+ Example:
6
+
7
+ ```json
8
+ {
9
+ "tag": "v0.1.11",
10
+ "title": "Short Release Title",
11
+ "highlights": [
12
+ {
13
+ "title": "Highlight One",
14
+ "description": "One-sentence summary for the release notes."
15
+ }
16
+ ]
17
+ }
18
+ ```
19
+
20
+ The release workflow reads `.github/release-metadata/<tag>.json`, generates the
21
+ `## What's Changed` section from GitHub, inserts `### Highlights` from this file,
22
+ and publishes the release title as `<tag> — <title>`.
@@ -0,0 +1,14 @@
1
+ {
2
+ "tag": "v0.1.10",
3
+ "title": "Deepagents Fallback",
4
+ "highlights": [
5
+ {
6
+ "title": "Explicit Photo Targets",
7
+ "description": "Require an explicit Telegram target for Photo Loader actions, restore the last selected target per account, and surface clear validation errors for missing or invalid destinations."
8
+ },
9
+ {
10
+ "title": "Agent Fallback Availability",
11
+ "description": "Keep the /agent flow available when DeepAgents runs through the legacy fallback path and optional provider libraries are not installed."
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "tag": "v0.1.12",
3
+ "title": "PyPI Packaging",
4
+ "highlights": [
5
+ {
6
+ "title": "PyPI-Ready Project Metadata",
7
+ "description": "Round out the package metadata with project URLs, classifiers, keywords, author information, and an explicit MIT license so the distribution is ready for a proper PyPI release."
8
+ },
9
+ {
10
+ "title": "Installable CLI and Web Assets",
11
+ "description": "Expose the `tg-agent` console entry point and include the bundled web templates and static assets in package data so installed builds behave like the source checkout."
12
+ },
13
+ {
14
+ "title": "Release Publishing Workflow",
15
+ "description": "Add the repository release script, token placeholders in `.env.example`, and the missing license file so the staged TestPyPI-to-PyPI publication flow is reproducible."
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,12 @@
1
+ changelog:
2
+ categories:
3
+ - title: "🚀 Features"
4
+ labels: ["feat", "feature", "enhancement"]
5
+ - title: "🐛 Fixes"
6
+ labels: ["fix", "bug", "bugfix"]
7
+ - title: "⚠️ Breaking Changes"
8
+ labels: ["breaking"]
9
+ - title: "🔧 Maintenance"
10
+ labels: ["refactor", "chore", "docs", "test", "ci"]
11
+ - title: "Other Changes"
12
+ labels: ["*"]
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+
10
+
11
+ def parse_args() -> argparse.Namespace:
12
+ parser = argparse.ArgumentParser(description="Render templated GitHub release notes.")
13
+ parser.add_argument("--repo", required=True, help="GitHub repository in OWNER/REPO format.")
14
+ parser.add_argument("--tag", required=True, help="Release tag, for example v0.1.10.")
15
+ parser.add_argument("--title-file", required=True, help="Path to write the rendered release title.")
16
+ parser.add_argument("--notes-file", required=True, help="Path to write the rendered release notes.")
17
+ parser.add_argument(
18
+ "--metadata-dir",
19
+ default=".github/release-metadata",
20
+ help="Directory containing per-tag release metadata JSON files.",
21
+ )
22
+ return parser.parse_args()
23
+
24
+
25
+ def load_metadata(tag: str, metadata_dir: Path) -> dict:
26
+ metadata_path = metadata_dir / f"{tag}.json"
27
+ if not metadata_path.exists():
28
+ raise SystemExit(
29
+ f"Missing release metadata: {metadata_path}. "
30
+ "Add a JSON file for this tag before pushing the release tag."
31
+ )
32
+
33
+ metadata = json.loads(metadata_path.read_text())
34
+ metadata_tag = metadata.get("tag")
35
+ if metadata_tag != tag:
36
+ raise SystemExit(
37
+ f"Release metadata tag mismatch in {metadata_path}: expected {tag}, got {metadata_tag!r}."
38
+ )
39
+
40
+ if not metadata.get("title"):
41
+ raise SystemExit(f"Release metadata {metadata_path} must define a non-empty 'title'.")
42
+
43
+ highlights = metadata.get("highlights")
44
+ if not isinstance(highlights, list) or not highlights:
45
+ raise SystemExit(f"Release metadata {metadata_path} must define a non-empty 'highlights' list.")
46
+
47
+ return metadata
48
+
49
+
50
+ def generate_notes(repo: str, tag: str) -> dict:
51
+ result = subprocess.run(
52
+ [
53
+ "gh",
54
+ "api",
55
+ f"repos/{repo}/releases/generate-notes",
56
+ "--method",
57
+ "POST",
58
+ "-f",
59
+ f"tag_name={tag}",
60
+ ],
61
+ check=True,
62
+ capture_output=True,
63
+ text=True,
64
+ )
65
+ return json.loads(result.stdout)
66
+
67
+
68
+ def strip_generator_comment(body: str) -> str:
69
+ stripped = body.lstrip()
70
+ if stripped.startswith("<!--"):
71
+ end = stripped.find("-->")
72
+ if end != -1:
73
+ stripped = stripped[end + 3 :]
74
+ return stripped.lstrip()
75
+
76
+
77
+ def normalize_single_category(body: str) -> str:
78
+ marker = "\n**Full Changelog**:"
79
+ head, sep, tail = body.partition(marker)
80
+ headings = head.count("\n### ")
81
+ prefix = "## What's Changed\n### "
82
+ if headings == 1 and head.startswith(prefix):
83
+ _, remainder = head.split("\n", 1)
84
+ head = "## What's Changed\n" + remainder.split("\n", 1)[1]
85
+ if not sep:
86
+ return head
87
+ return head + sep + tail
88
+
89
+
90
+ def render_highlights(highlights: list[dict]) -> str:
91
+ lines = ["### Highlights", ""]
92
+ for item in highlights:
93
+ title = str(item.get("title", "")).strip()
94
+ description = str(item.get("description", "")).strip()
95
+ if not title or not description:
96
+ raise SystemExit("Each highlight item must include non-empty 'title' and 'description'.")
97
+ lines.append(f"- **{title}** — {description}")
98
+ return "\n".join(lines)
99
+
100
+
101
+ def inject_highlights(body: str, highlights_block: str) -> str:
102
+ marker = "\n**Full Changelog**:"
103
+ if marker in body:
104
+ head, tail = body.split(marker, 1)
105
+ head = head.rstrip()
106
+ return f"{head}\n\n{highlights_block}\n\n**Full Changelog**:{tail}"
107
+ return f"{body.rstrip()}\n\n{highlights_block}\n"
108
+
109
+
110
+ def write_text(path: str, content: str) -> None:
111
+ output_path = Path(path)
112
+ output_path.parent.mkdir(parents=True, exist_ok=True)
113
+ output_path.write_text(content.rstrip() + "\n")
114
+
115
+
116
+ def main() -> int:
117
+ args = parse_args()
118
+ metadata = load_metadata(args.tag, Path(args.metadata_dir))
119
+ generated = generate_notes(args.repo, args.tag)
120
+ generated_body = normalize_single_category(strip_generator_comment(str(generated.get("body", ""))))
121
+ highlights_block = render_highlights(metadata["highlights"])
122
+ rendered_body = inject_highlights(generated_body, highlights_block)
123
+ rendered_title = f"{args.tag} — {metadata['title']}"
124
+
125
+ write_text(args.title_file, rendered_title)
126
+ write_text(args.notes_file, rendered_body)
127
+ return 0
128
+
129
+
130
+ if __name__ == "__main__":
131
+ sys.exit(main())
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - fix/**
8
+ - codex/**
9
+ pull_request:
10
+
11
+ jobs:
12
+ lint-and-test:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.11"
23
+
24
+ - name: Install dependencies
25
+ run: pip install -e ".[dev]"
26
+
27
+ - name: Ruff
28
+ run: ruff check src tests conftest.py
29
+
30
+ - name: Pytest
31
+ run: pytest tests -q -m "not aiosqlite_serial" -n auto && pytest tests -q -m aiosqlite_serial
@@ -0,0 +1,44 @@
1
+ name: Claude Code Review
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, ready_for_review, reopened]
6
+ # Optional: Only run on specific file changes
7
+ # paths:
8
+ # - "src/**/*.ts"
9
+ # - "src/**/*.tsx"
10
+ # - "src/**/*.js"
11
+ # - "src/**/*.jsx"
12
+
13
+ jobs:
14
+ claude-review:
15
+ # Optional: Filter by PR author
16
+ # if: |
17
+ # github.event.pull_request.user.login == 'external-contributor' ||
18
+ # github.event.pull_request.user.login == 'new-developer' ||
19
+ # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20
+
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: read
24
+ pull-requests: read
25
+ issues: read
26
+ id-token: write
27
+
28
+ steps:
29
+ - name: Checkout repository
30
+ uses: actions/checkout@v4
31
+ with:
32
+ fetch-depth: 1
33
+
34
+ - name: Run Claude Code Review
35
+ id: claude-review
36
+ uses: anthropics/claude-code-action@v1
37
+ with:
38
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
+ plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
40
+ plugins: 'code-review@claude-code-plugins'
41
+ prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
42
+ # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
43
+ # or https://code.claude.com/docs/en/cli-reference for available options
44
+
@@ -0,0 +1,50 @@
1
+ name: Claude Code
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_review_comment:
7
+ types: [created]
8
+ issues:
9
+ types: [opened, assigned]
10
+ pull_request_review:
11
+ types: [submitted]
12
+
13
+ jobs:
14
+ claude:
15
+ if: |
16
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19
+ (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: read
23
+ pull-requests: read
24
+ issues: read
25
+ id-token: write
26
+ actions: read # Required for Claude to read CI results on PRs
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 1
32
+
33
+ - name: Run Claude Code
34
+ id: claude
35
+ uses: anthropics/claude-code-action@v1
36
+ with:
37
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
+
39
+ # This is an optional setting that allows Claude to read CI results on PRs
40
+ additional_permissions: |
41
+ actions: read
42
+
43
+ # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
44
+ # prompt: 'Update the pull request description to include a summary of changes.'
45
+
46
+ # Optional: Add claude_args to customize behavior and configuration
47
+ # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
48
+ # or https://code.claude.com/docs/en/cli-reference for available options
49
+ # claude_args: '--allowed-tools Bash(gh pr:*)'
50
+
@@ -0,0 +1,48 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ publish-release:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - name: Create GitHub release if missing
22
+ env:
23
+ GH_REPO: ${{ github.repository }}
24
+ GH_TOKEN: ${{ github.token }}
25
+ GITHUB_TOKEN: ${{ github.token }}
26
+ TAG: ${{ github.ref_name }}
27
+ run: |
28
+ set -euo pipefail
29
+
30
+ if gh release view "$TAG" --repo "$GH_REPO" >/dev/null 2>&1; then
31
+ echo "Release $TAG already exists; nothing to do."
32
+ exit 0
33
+ fi
34
+
35
+ TITLE_FILE="$RUNNER_TEMP/release-title.txt"
36
+ NOTES_FILE="$RUNNER_TEMP/release-notes.md"
37
+
38
+ python3 .github/scripts/render_release.py \
39
+ --repo "$GH_REPO" \
40
+ --tag "$TAG" \
41
+ --title-file "$TITLE_FILE" \
42
+ --notes-file "$NOTES_FILE"
43
+
44
+ gh release create "$TAG" \
45
+ --repo "$GH_REPO" \
46
+ --title "$(cat "$TITLE_FILE")" \
47
+ --notes-file "$NOTES_FILE" \
48
+ --verify-tag