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.
- tg_agent-0.1.12/.codex-staging/feature-dev/README.md +15 -0
- tg_agent-0.1.12/.codex-staging/feature-dev/SKILL.md +120 -0
- tg_agent-0.1.12/.codex-staging/feature-dev/references/code-architect.md +17 -0
- tg_agent-0.1.12/.codex-staging/feature-dev/references/code-explorer.md +17 -0
- tg_agent-0.1.12/.codex-staging/feature-dev/references/code-reviewer.md +17 -0
- tg_agent-0.1.12/.devcontainer/devcontainer.json +6 -0
- tg_agent-0.1.12/.dockerignore +13 -0
- tg_agent-0.1.12/.env.example +23 -0
- tg_agent-0.1.12/.github/copilot-instructions.md +41 -0
- tg_agent-0.1.12/.github/release-metadata/README.md +22 -0
- tg_agent-0.1.12/.github/release-metadata/v0.1.10.json +14 -0
- tg_agent-0.1.12/.github/release-metadata/v0.1.12.json +18 -0
- tg_agent-0.1.12/.github/release.yml +12 -0
- tg_agent-0.1.12/.github/scripts/render_release.py +131 -0
- tg_agent-0.1.12/.github/workflows/ci.yml +31 -0
- tg_agent-0.1.12/.github/workflows/claude-code-review.yml +44 -0
- tg_agent-0.1.12/.github/workflows/claude.yml +50 -0
- tg_agent-0.1.12/.github/workflows/release.yml +48 -0
- tg_agent-0.1.12/.gitignore +54 -0
- tg_agent-0.1.12/CLAUDE.md +98 -0
- tg_agent-0.1.12/Dockerfile +18 -0
- tg_agent-0.1.12/LICENSE +21 -0
- tg_agent-0.1.12/PKG-INFO +252 -0
- tg_agent-0.1.12/README.md +204 -0
- tg_agent-0.1.12/README.ru.md +204 -0
- tg_agent-0.1.12/config.yaml +34 -0
- tg_agent-0.1.12/conftest.py +47 -0
- tg_agent-0.1.12/data/deepagents_model_compatibility_catalog.json +210 -0
- tg_agent-0.1.12/docker-compose.yml +15 -0
- tg_agent-0.1.12/docs/testing/real-telegram.md +148 -0
- tg_agent-0.1.12/pyproject.toml +102 -0
- tg_agent-0.1.12/reports/generate_reports.py +260 -0
- tg_agent-0.1.12/reports/moto_rental_chart.html +83 -0
- tg_agent-0.1.12/reports/moto_rental_messages.xlsx +0 -0
- tg_agent-0.1.12/scripts/release_pypi.sh +116 -0
- tg_agent-0.1.12/setup.cfg +4 -0
- tg_agent-0.1.12/src/__init__.py +0 -0
- tg_agent-0.1.12/src/agent/__init__.py +0 -0
- tg_agent-0.1.12/src/agent/context.py +61 -0
- tg_agent-0.1.12/src/agent/manager.py +1084 -0
- tg_agent-0.1.12/src/agent/models.py +10 -0
- tg_agent-0.1.12/src/agent/provider_registry.py +283 -0
- tg_agent-0.1.12/src/agent/tools.py +57 -0
- tg_agent-0.1.12/src/cli/__init__.py +3 -0
- tg_agent-0.1.12/src/cli/commands/__init__.py +8 -0
- tg_agent-0.1.12/src/cli/commands/account.py +44 -0
- tg_agent-0.1.12/src/cli/commands/agent.py +196 -0
- tg_agent-0.1.12/src/cli/commands/channel.py +289 -0
- tg_agent-0.1.12/src/cli/commands/collect.py +55 -0
- tg_agent-0.1.12/src/cli/commands/common.py +15 -0
- tg_agent-0.1.12/src/cli/commands/filter.py +151 -0
- tg_agent-0.1.12/src/cli/commands/my_telegram.py +43 -0
- tg_agent-0.1.12/src/cli/commands/notification.py +47 -0
- tg_agent-0.1.12/src/cli/commands/photo_loader.py +170 -0
- tg_agent-0.1.12/src/cli/commands/scheduler.py +60 -0
- tg_agent-0.1.12/src/cli/commands/search.py +53 -0
- tg_agent-0.1.12/src/cli/commands/search_query.py +121 -0
- tg_agent-0.1.12/src/cli/commands/serve.py +36 -0
- tg_agent-0.1.12/src/cli/commands/server_control.py +38 -0
- tg_agent-0.1.12/src/cli/commands/test.py +686 -0
- tg_agent-0.1.12/src/cli/main.py +74 -0
- tg_agent-0.1.12/src/cli/parser.py +264 -0
- tg_agent-0.1.12/src/cli/process_control.py +221 -0
- tg_agent-0.1.12/src/cli/runtime.py +53 -0
- tg_agent-0.1.12/src/collection_queue.py +184 -0
- tg_agent-0.1.12/src/config.py +170 -0
- tg_agent-0.1.12/src/database/__init__.py +3 -0
- tg_agent-0.1.12/src/database/bundles.py +708 -0
- tg_agent-0.1.12/src/database/connection.py +34 -0
- tg_agent-0.1.12/src/database/facade.py +561 -0
- tg_agent-0.1.12/src/database/migrations.py +425 -0
- tg_agent-0.1.12/src/database/repositories/accounts.py +141 -0
- tg_agent-0.1.12/src/database/repositories/channel_stats.py +77 -0
- tg_agent-0.1.12/src/database/repositories/channels.py +211 -0
- tg_agent-0.1.12/src/database/repositories/collection_tasks.py +517 -0
- tg_agent-0.1.12/src/database/repositories/dialog_cache.py +112 -0
- tg_agent-0.1.12/src/database/repositories/filters.py +191 -0
- tg_agent-0.1.12/src/database/repositories/messages.py +414 -0
- tg_agent-0.1.12/src/database/repositories/notification_bots.py +63 -0
- tg_agent-0.1.12/src/database/repositories/photo_loader.py +420 -0
- tg_agent-0.1.12/src/database/repositories/search_log.py +31 -0
- tg_agent-0.1.12/src/database/repositories/search_queries.py +171 -0
- tg_agent-0.1.12/src/database/repositories/settings.py +21 -0
- tg_agent-0.1.12/src/database/schema.py +216 -0
- tg_agent-0.1.12/src/filters/__init__.py +4 -0
- tg_agent-0.1.12/src/filters/analyzer.py +227 -0
- tg_agent-0.1.12/src/filters/criteria.py +31 -0
- tg_agent-0.1.12/src/filters/models.py +23 -0
- tg_agent-0.1.12/src/main.py +81 -0
- tg_agent-0.1.12/src/models.py +235 -0
- tg_agent-0.1.12/src/parsers.py +82 -0
- tg_agent-0.1.12/src/scheduler/__init__.py +0 -0
- tg_agent-0.1.12/src/scheduler/manager.py +199 -0
- tg_agent-0.1.12/src/search/__init__.py +0 -0
- tg_agent-0.1.12/src/search/ai_search.py +110 -0
- tg_agent-0.1.12/src/search/engine.py +60 -0
- tg_agent-0.1.12/src/search/local_search.py +34 -0
- tg_agent-0.1.12/src/search/persistence.py +34 -0
- tg_agent-0.1.12/src/search/telegram_search.py +359 -0
- tg_agent-0.1.12/src/search/transformers.py +118 -0
- tg_agent-0.1.12/src/security/__init__.py +3 -0
- tg_agent-0.1.12/src/security/session_cipher.py +80 -0
- tg_agent-0.1.12/src/services/__init__.py +1 -0
- tg_agent-0.1.12/src/services/account_service.py +44 -0
- tg_agent-0.1.12/src/services/agent_provider_service.py +1003 -0
- tg_agent-0.1.12/src/services/channel_service.py +129 -0
- tg_agent-0.1.12/src/services/collection_service.py +96 -0
- tg_agent-0.1.12/src/services/filter_deletion_service.py +86 -0
- tg_agent-0.1.12/src/services/notification_matcher.py +83 -0
- tg_agent-0.1.12/src/services/notification_service.py +94 -0
- tg_agent-0.1.12/src/services/notification_target_service.py +136 -0
- tg_agent-0.1.12/src/services/photo_auto_upload_service.py +135 -0
- tg_agent-0.1.12/src/services/photo_publish_service.py +72 -0
- tg_agent-0.1.12/src/services/photo_task_service.py +344 -0
- tg_agent-0.1.12/src/services/search_query_service.py +151 -0
- tg_agent-0.1.12/src/services/search_service.py +47 -0
- tg_agent-0.1.12/src/services/task_enqueuer.py +68 -0
- tg_agent-0.1.12/src/services/unified_dispatcher.py +348 -0
- tg_agent-0.1.12/src/settings_utils.py +19 -0
- tg_agent-0.1.12/src/telegram/__init__.py +0 -0
- tg_agent-0.1.12/src/telegram/account_lease_pool.py +127 -0
- tg_agent-0.1.12/src/telegram/auth.py +202 -0
- tg_agent-0.1.12/src/telegram/backends.py +256 -0
- tg_agent-0.1.12/src/telegram/botfather.py +80 -0
- tg_agent-0.1.12/src/telegram/client_pool.py +844 -0
- tg_agent-0.1.12/src/telegram/collector.py +896 -0
- tg_agent-0.1.12/src/telegram/notifier.py +89 -0
- tg_agent-0.1.12/src/telegram/session_materializer.py +70 -0
- tg_agent-0.1.12/src/telegram/utils.py +12 -0
- tg_agent-0.1.12/src/web/__init__.py +0 -0
- tg_agent-0.1.12/src/web/app.py +139 -0
- tg_agent-0.1.12/src/web/assembly.py +146 -0
- tg_agent-0.1.12/src/web/bootstrap.py +245 -0
- tg_agent-0.1.12/src/web/container.py +71 -0
- tg_agent-0.1.12/src/web/csrf.py +112 -0
- tg_agent-0.1.12/src/web/deps.py +315 -0
- tg_agent-0.1.12/src/web/log_handler.py +23 -0
- tg_agent-0.1.12/src/web/panel_auth.py +91 -0
- tg_agent-0.1.12/src/web/paths.py +7 -0
- tg_agent-0.1.12/src/web/routes/__init__.py +0 -0
- tg_agent-0.1.12/src/web/routes/agent.py +252 -0
- tg_agent-0.1.12/src/web/routes/auth.py +209 -0
- tg_agent-0.1.12/src/web/routes/channel_collection.py +207 -0
- tg_agent-0.1.12/src/web/routes/channels.py +79 -0
- tg_agent-0.1.12/src/web/routes/dashboard.py +28 -0
- tg_agent-0.1.12/src/web/routes/debug.py +26 -0
- tg_agent-0.1.12/src/web/routes/filter.py +204 -0
- tg_agent-0.1.12/src/web/routes/import_channels.py +130 -0
- tg_agent-0.1.12/src/web/routes/my_telegram.py +78 -0
- tg_agent-0.1.12/src/web/routes/photo_loader.py +513 -0
- tg_agent-0.1.12/src/web/routes/scheduler.py +155 -0
- tg_agent-0.1.12/src/web/routes/search.py +150 -0
- tg_agent-0.1.12/src/web/routes/search_queries.py +109 -0
- tg_agent-0.1.12/src/web/routes/settings.py +909 -0
- tg_agent-0.1.12/src/web/session.py +50 -0
- tg_agent-0.1.12/src/web/static/settings.js +424 -0
- tg_agent-0.1.12/src/web/static/style.css +243 -0
- tg_agent-0.1.12/src/web/template_globals.py +80 -0
- tg_agent-0.1.12/src/web/templates/_debug_logs.html +10 -0
- tg_agent-0.1.12/src/web/templates/agent.html +981 -0
- tg_agent-0.1.12/src/web/templates/base.html +211 -0
- tg_agent-0.1.12/src/web/templates/channels.html +295 -0
- tg_agent-0.1.12/src/web/templates/dashboard.html +53 -0
- tg_agent-0.1.12/src/web/templates/debug.html +16 -0
- tg_agent-0.1.12/src/web/templates/error.html +20 -0
- tg_agent-0.1.12/src/web/templates/filter_manage.html +155 -0
- tg_agent-0.1.12/src/web/templates/import_channels.html +68 -0
- tg_agent-0.1.12/src/web/templates/login.html +140 -0
- tg_agent-0.1.12/src/web/templates/my_telegram.html +339 -0
- tg_agent-0.1.12/src/web/templates/photo_loader.html +602 -0
- tg_agent-0.1.12/src/web/templates/scheduler.html +259 -0
- tg_agent-0.1.12/src/web/templates/search.html +181 -0
- tg_agent-0.1.12/src/web/templates/search_queries.html +243 -0
- tg_agent-0.1.12/src/web/templates/settings/_accounts.html +83 -0
- tg_agent-0.1.12/src/web/templates/settings/_credentials.html +41 -0
- tg_agent-0.1.12/src/web/templates/settings/_devmode.html +235 -0
- tg_agent-0.1.12/src/web/templates/settings/_filters.html +36 -0
- tg_agent-0.1.12/src/web/templates/settings/_notifications.html +50 -0
- tg_agent-0.1.12/src/web/templates/settings/_scheduler.html +11 -0
- tg_agent-0.1.12/src/web/templates/settings.html +112 -0
- tg_agent-0.1.12/src/web/templates/web_login.html +56 -0
- tg_agent-0.1.12/tasks/motobike_rental_search.md +100 -0
- tg_agent-0.1.12/tests/__init__.py +0 -0
- tg_agent-0.1.12/tests/conftest.py +336 -0
- tg_agent-0.1.12/tests/helpers.py +535 -0
- tg_agent-0.1.12/tests/test_account_service.py +112 -0
- tg_agent-0.1.12/tests/test_agent.py +574 -0
- tg_agent-0.1.12/tests/test_agent_errors.py +114 -0
- tg_agent-0.1.12/tests/test_agent_provider_service.py +817 -0
- tg_agent-0.1.12/tests/test_agent_tools.py +242 -0
- tg_agent-0.1.12/tests/test_ai_search.py +133 -0
- tg_agent-0.1.12/tests/test_auth.py +165 -0
- tg_agent-0.1.12/tests/test_bundles.py +352 -0
- tg_agent-0.1.12/tests/test_channel_stats.py +426 -0
- tg_agent-0.1.12/tests/test_cli.py +827 -0
- tg_agent-0.1.12/tests/test_cli_extended.py +560 -0
- tg_agent-0.1.12/tests/test_cli_extra.py +474 -0
- tg_agent-0.1.12/tests/test_client_pool.py +241 -0
- tg_agent-0.1.12/tests/test_client_pool_extended.py +282 -0
- tg_agent-0.1.12/tests/test_client_pool_runtime.py +193 -0
- tg_agent-0.1.12/tests/test_collector.py +1300 -0
- tg_agent-0.1.12/tests/test_collector_extended.py +189 -0
- tg_agent-0.1.12/tests/test_collector_runtime.py +126 -0
- tg_agent-0.1.12/tests/test_config.py +104 -0
- tg_agent-0.1.12/tests/test_database.py +1172 -0
- tg_agent-0.1.12/tests/test_filters.py +507 -0
- tg_agent-0.1.12/tests/test_import_web.py +177 -0
- tg_agent-0.1.12/tests/test_imports.py +43 -0
- tg_agent-0.1.12/tests/test_integration.py +856 -0
- tg_agent-0.1.12/tests/test_my_telegram.py +708 -0
- tg_agent-0.1.12/tests/test_notification.py +483 -0
- tg_agent-0.1.12/tests/test_notifier_extra.py +380 -0
- tg_agent-0.1.12/tests/test_packaging_release.py +18 -0
- tg_agent-0.1.12/tests/test_parsers.py +250 -0
- tg_agent-0.1.12/tests/test_photo_loader.py +1353 -0
- tg_agent-0.1.12/tests/test_photo_publish_runtime.py +78 -0
- tg_agent-0.1.12/tests/test_pool_harness_guardrails.py +34 -0
- tg_agent-0.1.12/tests/test_process_control.py +191 -0
- tg_agent-0.1.12/tests/test_real_telegram_policy.py +192 -0
- tg_agent-0.1.12/tests/test_scheduler.py +51 -0
- tg_agent-0.1.12/tests/test_scheduler_manager.py +194 -0
- tg_agent-0.1.12/tests/test_search.py +425 -0
- tg_agent-0.1.12/tests/test_search_queries.py +259 -0
- tg_agent-0.1.12/tests/test_session_cipher.py +84 -0
- tg_agent-0.1.12/tests/test_session_materializer.py +66 -0
- tg_agent-0.1.12/tests/test_transformers.py +114 -0
- tg_agent-0.1.12/tests/test_web.py +2995 -0
- tg_agent-0.1.12/tests/test_web_auth.py +544 -0
- tg_agent-0.1.12/tg_agent.egg-info/PKG-INFO +252 -0
- tg_agent-0.1.12/tg_agent.egg-info/SOURCES.txt +232 -0
- tg_agent-0.1.12/tg_agent.egg-info/dependency_links.txt +1 -0
- tg_agent-0.1.12/tg_agent.egg-info/entry_points.txt +2 -0
- tg_agent-0.1.12/tg_agent.egg-info/requires.txt +25 -0
- 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,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
|