teddy-cli 0.1.4__tar.gz → 0.1.5__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.
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/PKG-INFO +2 -1
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/pyproject.toml +2 -1
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/__main__.py +79 -13
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/cli_helpers.py +15 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/session_cli_handlers.py +59 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/parser_metadata.py +76 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/planning_service.py +1 -0
- teddy_cli-0.1.5/src/teddy_executor/core/services/update_checker.py +212 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/config.yaml +1 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/architect.xml +24 -6
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/assistant.xml +3 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/debugger.xml +6 -3
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/developer.xml +10 -4
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/pathfinder.xml +6 -3
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/prototyper.xml +4 -1
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/LICENSE +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/cli_formatter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/console_plan_reviewer.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_app.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_editor.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_execution.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_helpers.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_logic.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_previews.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_widgets.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/console_interactor.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/console_interactor_ask_loop.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/console_interactor_helpers.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/console_tooling.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/filesystem_helpers.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/litellm_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/local_file_system_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/local_repo_tree_generator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/openrouter_hydrator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/shell_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/shell_command_builder.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/system_environment_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/system_environment_inspector.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/system_time_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/web_scraper_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/web_searcher_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/outbound/yaml_config_adapter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/container.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/action_ports.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/change_set.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/exceptions.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/execution_report.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/orchestrator_ports.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/plan.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/planning_ports.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/project_context.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/report_assembly_data.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/session.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/shell_output.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/domain/models/web_search_results.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/edit_simulator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/get_context_use_case.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/init.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/plan_parser.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/plan_reviewer.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/plan_validator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/planning_use_case.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/inbound/run_plan_use_case.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/config_service.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/environment_inspector.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/execution_report_assembler.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/file_system_manager.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/llm_client.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/markdown_report_formatter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/prompt_manager.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/repo_tree_generator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/session_loop_guard.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/session_manager.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/session_repository.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/shell_executor.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/system_environment.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/time_service.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/user_interactor.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/web_scraper.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/ports/outbound/web_searcher.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/action_changeset_builder.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/action_diff_manager.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/action_dispatcher.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/action_executor.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/action_factory.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/action_parser_complex.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/action_parser_strategies.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/context_service.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/edit_simulator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/execution_orchestrator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/execution_report_assembler.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/init_service.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/markdown_plan_parser.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/markdown_report_formatter.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/parser_infrastructure.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/parser_reporting.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/plan_validator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/prompt_manager.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_lifecycle_manager.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_loop_guard.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_orchestrator.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_planner.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_pruning_service.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_replanner.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_repository.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/session_service.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/templates/execution_report.md.j2 +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/edit.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/edit_matcher.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/edit_matcher_heuristics.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/execute.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/filesystem.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/helpers.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/services/validation_rules/message.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/utils/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/utils/diff.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/utils/io.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/utils/markdown.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/utils/serialization.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/core/utils/string.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/prompts.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/registries/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/registries/infrastructure.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/registries/reviewer.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/registries/validators.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/.gitignore +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/__init__.py +0 -0
- {teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/init.context +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: teddy-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A local-first, file-based AI coding workflow that applies the UNIX philosophy to AI collaboration.
|
|
5
5
|
License: AGPL-3.0-only
|
|
6
6
|
License-File: LICENSE
|
|
@@ -30,6 +30,7 @@ Requires-Dist: python-dotenv (>=1.0.1)
|
|
|
30
30
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
31
31
|
Requires-Dist: requests (>=2.33.0,<3.0.0)
|
|
32
32
|
Requires-Dist: textual (>=8.1.0,<9.0.0)
|
|
33
|
+
Requires-Dist: tld (>=0.10,<1.0)
|
|
33
34
|
Requires-Dist: trafilatura (>=2.0.0,<3.0.0)
|
|
34
35
|
Requires-Dist: typer[all]
|
|
35
36
|
Requires-Dist: urllib3 (>=2.7.0,<3.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "teddy-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.5"
|
|
4
4
|
description = "A local-first, file-based AI coding workflow that applies the UNIX philosophy to AI collaboration."
|
|
5
5
|
authors = ["Raphael Atteritano"]
|
|
6
6
|
license = "AGPL-3.0-only"
|
|
@@ -29,6 +29,7 @@ bandit = ">=1.7.9,<1.8.0"
|
|
|
29
29
|
pip-audit = ">=2.7.3,<2.8.0"
|
|
30
30
|
textual = "^8.1.0"
|
|
31
31
|
urllib3 = "^2.7.0"
|
|
32
|
+
tld = ">=0.10,<1.0"
|
|
32
33
|
|
|
33
34
|
[tool.poetry.group.dev.dependencies]
|
|
34
35
|
pytest = "^9.0.3"
|
|
@@ -57,8 +57,12 @@ logging.basicConfig(
|
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def _ensure_project_initialized(container) -> None:
|
|
61
|
-
"""Lazily performs project anchoring and initialization.
|
|
60
|
+
def _ensure_project_initialized(container, root_dir: str | None = None) -> None:
|
|
61
|
+
"""Lazily performs project anchoring and initialization.
|
|
62
|
+
|
|
63
|
+
If root_dir is provided, uses that path as the root. Otherwise, finds
|
|
64
|
+
the nearest parent containing .teddy/ via find_project_root().
|
|
65
|
+
"""
|
|
62
66
|
from teddy_executor.adapters.inbound.cli_helpers import find_project_root
|
|
63
67
|
from teddy_executor.core.ports.outbound.file_system_manager import (
|
|
64
68
|
IFileSystemManager,
|
|
@@ -77,7 +81,10 @@ def _ensure_project_initialized(container) -> None:
|
|
|
77
81
|
from teddy_executor.adapters.outbound.yaml_config_adapter import YamlConfigAdapter
|
|
78
82
|
from punq import Scope
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
if root_dir is None:
|
|
85
|
+
root = str(find_project_root())
|
|
86
|
+
else:
|
|
87
|
+
root = root_dir
|
|
81
88
|
c = container
|
|
82
89
|
|
|
83
90
|
regs = getattr(c.registrations, "_Registry__registrations", {})
|
|
@@ -170,17 +177,11 @@ def init():
|
|
|
170
177
|
"""
|
|
171
178
|
Initializes the .teddy directory and pre-warms heavy imports for faster startup.
|
|
172
179
|
"""
|
|
180
|
+
from teddy_executor.adapters.inbound.cli_helpers import prewarm_imports
|
|
181
|
+
|
|
173
182
|
container = get_container()
|
|
174
|
-
_ensure_project_initialized(container)
|
|
175
|
-
|
|
176
|
-
try:
|
|
177
|
-
import litellm # noqa: F401
|
|
178
|
-
import trafilatura # noqa: F401
|
|
179
|
-
import pyperclip # noqa: F401
|
|
180
|
-
from bs4 import BeautifulSoup # noqa: F401
|
|
181
|
-
from ddgs import DDGS # noqa: F401
|
|
182
|
-
except ImportError:
|
|
183
|
-
pass # Some optional dependencies may not be installed
|
|
183
|
+
_ensure_project_initialized(container, root_dir=str(Path.cwd()))
|
|
184
|
+
prewarm_imports()
|
|
184
185
|
# Auto-login would trigger here once `teddy login` is implemented (no-op for now)
|
|
185
186
|
typer.echo("TeDDy initialized in .teddy folder.")
|
|
186
187
|
|
|
@@ -194,6 +195,71 @@ def version() -> None:
|
|
|
194
195
|
typer.echo(f"TeDDy v{installed}")
|
|
195
196
|
|
|
196
197
|
|
|
198
|
+
@app.command()
|
|
199
|
+
def update(
|
|
200
|
+
experimental: bool = typer.Option(
|
|
201
|
+
False,
|
|
202
|
+
"--experimental",
|
|
203
|
+
help="Check and upgrade from TestPyPI instead of PyPI.",
|
|
204
|
+
),
|
|
205
|
+
):
|
|
206
|
+
"""Checks PyPI for the latest version of TeDDy and displays upgrade
|
|
207
|
+
instructions. Does not upgrade automatically."""
|
|
208
|
+
from teddy_executor.core.services.update_checker import (
|
|
209
|
+
get_current_version,
|
|
210
|
+
fetch_latest_version,
|
|
211
|
+
compare_versions,
|
|
212
|
+
is_prerelease,
|
|
213
|
+
PYPI_URL,
|
|
214
|
+
TEST_PYPI_URL,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
index_url = TEST_PYPI_URL if experimental else PYPI_URL
|
|
218
|
+
# When experimental, include prerelease versions (TestPyPI only has dev releases)
|
|
219
|
+
latest = fetch_latest_version(index_url, stable_only=not experimental)
|
|
220
|
+
|
|
221
|
+
if latest is None:
|
|
222
|
+
typer.echo("Could not check for updates: network error.")
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
current = get_current_version()
|
|
226
|
+
|
|
227
|
+
needs_update = compare_versions(current, latest)
|
|
228
|
+
is_channel_switch = False
|
|
229
|
+
if not needs_update:
|
|
230
|
+
# If current is a pre-release and the latest is stable, allow downgrade
|
|
231
|
+
# to the stable channel
|
|
232
|
+
if is_prerelease(current) and not is_prerelease(latest):
|
|
233
|
+
is_channel_switch = True
|
|
234
|
+
|
|
235
|
+
if not needs_update and not is_channel_switch:
|
|
236
|
+
typer.echo(f"You are already running the latest version ({current}).")
|
|
237
|
+
return
|
|
238
|
+
|
|
239
|
+
if is_channel_switch:
|
|
240
|
+
typer.echo(f"You are running the latest experimental version ({current}).")
|
|
241
|
+
typer.echo(
|
|
242
|
+
"To switch to the stable release, run: pip install --upgrade teddy-cli"
|
|
243
|
+
)
|
|
244
|
+
typer.echo(
|
|
245
|
+
"To apply prompt updates: delete .teddy/prompts/ and run 'teddy init'"
|
|
246
|
+
)
|
|
247
|
+
elif experimental:
|
|
248
|
+
typer.echo(f"A new experimental version {latest} is available.")
|
|
249
|
+
typer.echo(
|
|
250
|
+
"To upgrade, run: pip install --upgrade teddy-cli --index-url https://test.pypi.org/simple/"
|
|
251
|
+
)
|
|
252
|
+
typer.echo(
|
|
253
|
+
"To apply prompt updates: delete .teddy/prompts/ and run 'teddy init'"
|
|
254
|
+
)
|
|
255
|
+
else:
|
|
256
|
+
typer.echo(f"A new version {latest} is available.")
|
|
257
|
+
typer.echo("To upgrade, run: pip install --upgrade teddy-cli")
|
|
258
|
+
typer.echo(
|
|
259
|
+
"To apply prompt updates: delete .teddy/prompts/ and run 'teddy init'"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
197
263
|
@app.command()
|
|
198
264
|
def context(
|
|
199
265
|
no_copy: bool = typer.Option(
|
|
@@ -247,3 +247,18 @@ def apply_ui_mode_override(container: Container, ui_mode_bool: bool) -> None:
|
|
|
247
247
|
|
|
248
248
|
mode = "tui" if ui_mode_bool else "console"
|
|
249
249
|
register_reviewer(container, ui_mode=mode)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def prewarm_imports() -> None:
|
|
253
|
+
"""
|
|
254
|
+
Pre-warm heavy imports to reduce first-run latency.
|
|
255
|
+
Extracted from __main__.py init command for reuse by update command.
|
|
256
|
+
"""
|
|
257
|
+
try:
|
|
258
|
+
import litellm # noqa: F401
|
|
259
|
+
import trafilatura # noqa: F401
|
|
260
|
+
import pyperclip # noqa: F401
|
|
261
|
+
from bs4 import BeautifulSoup # noqa: F401
|
|
262
|
+
from ddgs import DDGS # noqa: F401
|
|
263
|
+
except ImportError:
|
|
264
|
+
pass
|
{teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/adapters/inbound/session_cli_handlers.py
RENAMED
|
@@ -17,6 +17,13 @@ from teddy_executor.core.utils.string import slugify
|
|
|
17
17
|
from teddy_executor.adapters.inbound.cli_formatter import format_project_context
|
|
18
18
|
from teddy_executor.adapters.inbound.cli_helpers import (
|
|
19
19
|
echo_and_copy,
|
|
20
|
+
find_project_root,
|
|
21
|
+
)
|
|
22
|
+
from teddy_executor.core.services.update_checker import (
|
|
23
|
+
background_check,
|
|
24
|
+
compare_versions,
|
|
25
|
+
get_current_version,
|
|
26
|
+
read_update_cache,
|
|
20
27
|
)
|
|
21
28
|
|
|
22
29
|
|
|
@@ -78,6 +85,34 @@ def _orchestrate_session_loop(
|
|
|
78
85
|
break
|
|
79
86
|
|
|
80
87
|
|
|
88
|
+
def _display_update_notification(cache_path: Path) -> None:
|
|
89
|
+
"""Check the update cache and display a non-blocking notification
|
|
90
|
+
if a newer version is available. Called on session startup, after
|
|
91
|
+
the background check thread has been started."""
|
|
92
|
+
try:
|
|
93
|
+
cache = read_update_cache(cache_path)
|
|
94
|
+
if cache is None:
|
|
95
|
+
return
|
|
96
|
+
latest = cache.get("latest_version", "")
|
|
97
|
+
if not latest:
|
|
98
|
+
return
|
|
99
|
+
current = get_current_version()
|
|
100
|
+
if compare_versions(current, latest):
|
|
101
|
+
typer.echo(
|
|
102
|
+
typer.style(
|
|
103
|
+
f"ℹ A new version {latest} is available. "
|
|
104
|
+
"To upgrade, run: pip install --upgrade teddy-cli\n"
|
|
105
|
+
" To apply prompt updates: delete .teddy/prompts/ and run 'teddy init'",
|
|
106
|
+
fg=typer.colors.YELLOW,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
except Exception:
|
|
110
|
+
import logging
|
|
111
|
+
|
|
112
|
+
logger = logging.getLogger(__name__)
|
|
113
|
+
logger.debug("Failed to display update notification", exc_info=True)
|
|
114
|
+
|
|
115
|
+
|
|
81
116
|
def handle_new_session( # noqa: PLR0913
|
|
82
117
|
container: Container,
|
|
83
118
|
name: Optional[str],
|
|
@@ -91,6 +126,18 @@ def handle_new_session( # noqa: PLR0913
|
|
|
91
126
|
api_key: Optional[str] = None,
|
|
92
127
|
):
|
|
93
128
|
"""Logic for the 'start' command."""
|
|
129
|
+
import threading
|
|
130
|
+
|
|
131
|
+
# Start non-blocking background version check
|
|
132
|
+
cache_path = find_project_root() / ".teddy" / ".update_cache.json"
|
|
133
|
+
thread = threading.Thread(
|
|
134
|
+
target=background_check,
|
|
135
|
+
args=(cache_path,),
|
|
136
|
+
daemon=True,
|
|
137
|
+
)
|
|
138
|
+
thread.start()
|
|
139
|
+
_display_update_notification(cache_path)
|
|
140
|
+
|
|
94
141
|
try:
|
|
95
142
|
# 0. Ensure project is initialized
|
|
96
143
|
container.resolve(IInitUseCase).ensure_initialized()
|
|
@@ -326,6 +373,18 @@ def handle_resume_session( # noqa: PLR0913
|
|
|
326
373
|
api_key: Optional[str] = None,
|
|
327
374
|
):
|
|
328
375
|
"""Logic for the 'resume' command."""
|
|
376
|
+
import threading
|
|
377
|
+
|
|
378
|
+
# Start non-blocking background version check
|
|
379
|
+
cache_path = find_project_root() / ".teddy" / ".update_cache.json"
|
|
380
|
+
thread = threading.Thread(
|
|
381
|
+
target=background_check,
|
|
382
|
+
args=(cache_path,),
|
|
383
|
+
daemon=True,
|
|
384
|
+
)
|
|
385
|
+
thread.start()
|
|
386
|
+
_display_update_notification(cache_path)
|
|
387
|
+
|
|
329
388
|
try:
|
|
330
389
|
# 1. Pre-flight checks
|
|
331
390
|
typer.echo("Checking configurations...", err=True)
|
|
@@ -17,6 +17,76 @@ from teddy_executor.core.services.parser_infrastructure import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def _extract_text_with_emphasis(node: Any) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Extract text from an AST node, emitting delimiter markers for Strong/Emphasis tokens.
|
|
23
|
+
|
|
24
|
+
Used only for extracting the value portion of metadata key-value pairs.
|
|
25
|
+
Unlike `get_child_text`, it emits `__text__` for Strong tokens and `_text_`
|
|
26
|
+
for Emphasis tokens, preserving literal double underscores like those in
|
|
27
|
+
`__main__.py` that mistletoe would otherwise parse as formatting.
|
|
28
|
+
"""
|
|
29
|
+
from mistletoe.span_token import Strong, Emphasis, RawText
|
|
30
|
+
|
|
31
|
+
if isinstance(node, RawText):
|
|
32
|
+
return getattr(node, "content", "")
|
|
33
|
+
|
|
34
|
+
if isinstance(node, Strong):
|
|
35
|
+
inner = ""
|
|
36
|
+
if hasattr(node, "children") and node.children:
|
|
37
|
+
inner = "".join(_extract_text_with_emphasis(c) for c in node.children)
|
|
38
|
+
return f"__{inner}__"
|
|
39
|
+
|
|
40
|
+
if isinstance(node, Emphasis):
|
|
41
|
+
inner = ""
|
|
42
|
+
if hasattr(node, "children") and node.children:
|
|
43
|
+
inner = "".join(_extract_text_with_emphasis(c) for c in node.children)
|
|
44
|
+
return f"_{inner}_"
|
|
45
|
+
|
|
46
|
+
if hasattr(node, "children") and node.children is not None:
|
|
47
|
+
return "".join(_extract_text_with_emphasis(c) for c in node.children)
|
|
48
|
+
return getattr(node, "content", "")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _extract_value_after_key(item: "MdListItem", key_text: str) -> Optional[str]:
|
|
52
|
+
"""
|
|
53
|
+
Extract the value portion of a key-value pair from an AST ListItem node,
|
|
54
|
+
preserving emphasis delimiters in the value (e.g., for paths like __main__.py).
|
|
55
|
+
|
|
56
|
+
Navigates the item's children to find the node containing the key (a Strong
|
|
57
|
+
token like **Resource:**), then extracts emphasis-aware text from all subsequent
|
|
58
|
+
sibling nodes. When a Strong/Emphasis token's text matches the key, we STOP
|
|
59
|
+
recursion and return the remaining siblings at that level.
|
|
60
|
+
"""
|
|
61
|
+
from mistletoe.span_token import Strong, Emphasis
|
|
62
|
+
|
|
63
|
+
key_with_colon = f"{key_text}:"
|
|
64
|
+
|
|
65
|
+
def find_remaining(parent) -> Optional[list[Any]]:
|
|
66
|
+
if not hasattr(parent, "children") or not parent.children:
|
|
67
|
+
return None
|
|
68
|
+
children = list(parent.children)
|
|
69
|
+
for i, child in enumerate(children):
|
|
70
|
+
child_text = get_child_text(child)
|
|
71
|
+
if child_text and key_with_colon in child_text:
|
|
72
|
+
if isinstance(child, (Strong, Emphasis)):
|
|
73
|
+
return children[i + 1 :]
|
|
74
|
+
remaining = find_remaining(child)
|
|
75
|
+
if remaining is not None:
|
|
76
|
+
return remaining
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
remaining_children = find_remaining(item)
|
|
80
|
+
if not remaining_children:
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
value_parts = []
|
|
84
|
+
for child in remaining_children:
|
|
85
|
+
value_parts.append(_extract_text_with_emphasis(child))
|
|
86
|
+
result = "".join(value_parts).strip()
|
|
87
|
+
return result if result else None
|
|
88
|
+
|
|
89
|
+
|
|
20
90
|
def _process_link_key(
|
|
21
91
|
item: "MdListItem", text: str, key_map: dict[str, str]
|
|
22
92
|
) -> Optional[tuple[str, str]]:
|
|
@@ -29,6 +99,12 @@ def _process_link_key(
|
|
|
29
99
|
if link_node:
|
|
30
100
|
target = normalize_link_target(link_node.target)
|
|
31
101
|
return param_key, normalize_path(target)
|
|
102
|
+
# No link found: extract value from plain text using AST-based
|
|
103
|
+
# emphasis-aware extraction to preserve double underscores.
|
|
104
|
+
value = _extract_value_after_key(item, key_text)
|
|
105
|
+
if value is not None:
|
|
106
|
+
return param_key, normalize_path(value)
|
|
107
|
+
# Last resort fallback: use original (possibly corrupted) text
|
|
32
108
|
parts = text.split(f"{key_text}:", 1)
|
|
33
109
|
if len(parts) == EXPECTED_KV_PARTS and parts[1].strip():
|
|
34
110
|
return param_key, normalize_path(parts[1].strip())
|
|
@@ -75,6 +75,7 @@ class PlanningService(IPlanningUseCase):
|
|
|
75
75
|
agent_name=agent_name,
|
|
76
76
|
current_turn=Path(turn_dir).name,
|
|
77
77
|
system_prompt_tokens=system_token_count,
|
|
78
|
+
cache_dir=str(Path(turn_dir).parent),
|
|
78
79
|
)
|
|
79
80
|
|
|
80
81
|
# Context is purely project state (including initial_request.md via session.context).
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""Update Checker: Lightweight version check and upgrade mechanism.
|
|
2
|
+
|
|
3
|
+
Provides functions for detecting the current installed version, fetching the
|
|
4
|
+
latest version from PyPI/TestPyPI, comparing versions, caching results, and
|
|
5
|
+
performing upgrades. All public functions use stdlib only (plus the
|
|
6
|
+
`packaging` library which is a transitive dependency via pip-audit).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING, Optional
|
|
13
|
+
|
|
14
|
+
from packaging.version import Version
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
import ssl
|
|
18
|
+
|
|
19
|
+
# --- Constants ---
|
|
20
|
+
|
|
21
|
+
PYPI_URL = "https://pypi.org/pypi/teddy-cli/json"
|
|
22
|
+
TEST_PYPI_URL = "https://test.pypi.org/pypi/teddy-cli/json"
|
|
23
|
+
CACHE_FILENAME = ".update_cache.json"
|
|
24
|
+
CACHE_TTL_HOURS = 24
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# --- SSL Context Setup ---
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _create_ssl_context() -> ssl.SSLContext:
|
|
31
|
+
"""
|
|
32
|
+
Create an SSL context with proper CA bundle.
|
|
33
|
+
|
|
34
|
+
Priority order:
|
|
35
|
+
1. certifi if available (provides latest Mozilla CA bundle)
|
|
36
|
+
2. Default SSL context (system CA bundle)
|
|
37
|
+
|
|
38
|
+
Returns an ssl.SSLContext object suitable for urllib.
|
|
39
|
+
"""
|
|
40
|
+
import ssl # noqa: F811 (imported at module level under TYPE_CHECKING)
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
import certifi
|
|
44
|
+
|
|
45
|
+
cafile = certifi.where()
|
|
46
|
+
if Path(cafile).is_file():
|
|
47
|
+
return ssl.create_default_context(cafile=cafile)
|
|
48
|
+
except ImportError:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
# Fallback: use system default (may fail on some Python 3.14 macOS builds)
|
|
52
|
+
return ssl.create_default_context()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# --- Public API ---
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_current_version() -> str:
|
|
59
|
+
"""
|
|
60
|
+
Read installed version from importlib.metadata.
|
|
61
|
+
Falls back to '0.0.0' for dev installations or missing package.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
from importlib.metadata import version as _get_version
|
|
65
|
+
|
|
66
|
+
return _get_version("teddy-cli")
|
|
67
|
+
except Exception:
|
|
68
|
+
return "0.0.0"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def fetch_latest_version(
|
|
72
|
+
index_url: str = PYPI_URL,
|
|
73
|
+
stable_only: bool = True,
|
|
74
|
+
) -> Optional[str]:
|
|
75
|
+
"""
|
|
76
|
+
Fetch the highest version from PyPI/TestPyPI JSON API.
|
|
77
|
+
|
|
78
|
+
Scans all releases in data['releases'] (instead of only data['info']['version']),
|
|
79
|
+
and optionally filters to stable versions only.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
index_url: The PyPI JSON API URL.
|
|
83
|
+
stable_only: If True (default), only consider stable (non-prerelease) versions.
|
|
84
|
+
If False, consider all versions including dev/pre-releases.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
The highest matching version string, or None on failure.
|
|
88
|
+
"""
|
|
89
|
+
import json
|
|
90
|
+
import urllib.error
|
|
91
|
+
import urllib.request
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
req = urllib.request.Request(
|
|
95
|
+
index_url,
|
|
96
|
+
headers={
|
|
97
|
+
"User-Agent": "TeDDy-Update-Checker/1.0",
|
|
98
|
+
"Accept": "application/json",
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
context = _create_ssl_context()
|
|
102
|
+
with urllib.request.urlopen(req, timeout=10, context=context) as resp: # nosec
|
|
103
|
+
data = json.loads(resp.read().decode("utf-8"))
|
|
104
|
+
releases = data.get("releases", {})
|
|
105
|
+
if not releases:
|
|
106
|
+
# Fallback to info.version if releases dict is empty
|
|
107
|
+
return data.get("info", {}).get("version")
|
|
108
|
+
valid_versions = []
|
|
109
|
+
for v_str in releases.keys():
|
|
110
|
+
try:
|
|
111
|
+
ver = Version(v_str)
|
|
112
|
+
if stable_only and ver.is_prerelease:
|
|
113
|
+
continue
|
|
114
|
+
valid_versions.append(ver)
|
|
115
|
+
except Exception:
|
|
116
|
+
pass
|
|
117
|
+
if not valid_versions:
|
|
118
|
+
return None
|
|
119
|
+
highest = max(valid_versions)
|
|
120
|
+
return str(highest)
|
|
121
|
+
except (urllib.error.URLError, OSError, json.JSONDecodeError, KeyError) as e:
|
|
122
|
+
import logging
|
|
123
|
+
|
|
124
|
+
logger = logging.getLogger(__name__)
|
|
125
|
+
logger.debug("fetch_latest_version failed: %s", e)
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def is_prerelease(version_str: str) -> bool:
|
|
130
|
+
"""
|
|
131
|
+
Returns True if the given version string is a pre-release (dev, alpha, beta, rc, etc.)
|
|
132
|
+
according to PEP 440. Returns False on any parse failure.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
return Version(version_str).is_prerelease
|
|
136
|
+
except Exception:
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def compare_versions(current: str, latest: str) -> bool:
|
|
141
|
+
"""
|
|
142
|
+
Returns True if latest > current using PEP 440 version comparison.
|
|
143
|
+
Returns False on any parse failure.
|
|
144
|
+
"""
|
|
145
|
+
try:
|
|
146
|
+
return Version(latest) > Version(current)
|
|
147
|
+
except Exception:
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def read_update_cache(cache_path: Path) -> Optional[dict]:
|
|
152
|
+
"""
|
|
153
|
+
Read the cache file. Returns None if:
|
|
154
|
+
- File is missing
|
|
155
|
+
- File is corrupt (invalid JSON)
|
|
156
|
+
- File has invalid structure (missing keys)
|
|
157
|
+
- TTL exceeded (24h from checked_at)
|
|
158
|
+
"""
|
|
159
|
+
import json
|
|
160
|
+
from datetime import datetime, timezone, timedelta
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
if not cache_path.is_file():
|
|
164
|
+
return None
|
|
165
|
+
data = json.loads(cache_path.read_text(encoding="utf-8"))
|
|
166
|
+
if not isinstance(data, dict):
|
|
167
|
+
return None
|
|
168
|
+
if "latest_version" not in data or "checked_at" not in data:
|
|
169
|
+
return None
|
|
170
|
+
checked_at = datetime.fromisoformat(data["checked_at"])
|
|
171
|
+
if datetime.now(timezone.utc) - checked_at > timedelta(hours=CACHE_TTL_HOURS):
|
|
172
|
+
return None
|
|
173
|
+
return data
|
|
174
|
+
except (OSError, json.JSONDecodeError, ValueError, TypeError):
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def write_update_cache(cache_path: Path, latest_version: str) -> None:
|
|
179
|
+
"""
|
|
180
|
+
Atomically write the cache file (write to temp file, rename).
|
|
181
|
+
Ensures the main thread never reads a partially written file.
|
|
182
|
+
"""
|
|
183
|
+
import json
|
|
184
|
+
from datetime import datetime, timezone
|
|
185
|
+
|
|
186
|
+
cache_data = {
|
|
187
|
+
"latest_version": latest_version,
|
|
188
|
+
"checked_at": datetime.now(timezone.utc).isoformat(),
|
|
189
|
+
}
|
|
190
|
+
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
191
|
+
tmp_path = cache_path.with_suffix(".tmp")
|
|
192
|
+
try:
|
|
193
|
+
tmp_path.write_text(json.dumps(cache_data, indent=2), encoding="utf-8")
|
|
194
|
+
tmp_path.rename(cache_path)
|
|
195
|
+
except OSError as e:
|
|
196
|
+
import logging
|
|
197
|
+
|
|
198
|
+
logger = logging.getLogger(__name__)
|
|
199
|
+
logger.debug("Failed to write update cache: %s", e)
|
|
200
|
+
if tmp_path.exists():
|
|
201
|
+
tmp_path.unlink()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def background_check(cache_path: Path, index_url: str = PYPI_URL) -> None:
|
|
205
|
+
"""
|
|
206
|
+
Non-blocking background version check.
|
|
207
|
+
Intended to run in a daemon thread. Fetches latest version from PyPI
|
|
208
|
+
and writes it to cache. All errors are silently caught.
|
|
209
|
+
"""
|
|
210
|
+
latest = fetch_latest_version(index_url)
|
|
211
|
+
if latest is not None:
|
|
212
|
+
write_update_cache(cache_path, latest)
|
{teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/architect.xml
RENAMED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<step n="2" name="CI Quality Gate">Verify CI pipeline status for the targeted components. Ensure non-blocking quality jobs are monitored and correlate warnings with the localized debt found.</step>
|
|
30
30
|
<step n="3" name="Specs Alignment">Identify divergences where implementation reality differs from the `Specification Document`. Resolve these via As-Built Alignment by `EDIT`-ing the Spec and Design Docs to match reality.</step>
|
|
31
31
|
<step n="4" name="Milestone Management">If no active milestone exists, `READ` the Roadmap in `docs/project/PROJECT.md` to `CREATE` the next `Milestone`. You MUST translate ALL its Roadmap requirements into the Milestone document. Change status to `In Progress` and transition to `Completed` once fully implemented.</step>
|
|
32
|
-
<step n="5" name="Draft Component Designs">`CREATE` or `EDIT` draft design docs for affected components. Focus strictly on strategic intent, purpose, and key contracts; defer detailed logic. Transition any updated existing docs to `Refactoring` status.</step>
|
|
32
|
+
<step n="5" name="Draft Component Designs">`CREATE` or `EDIT` draft design docs for affected components. Focus strictly on strategic intent, purpose, and key contracts; defer detailed logic. Transition any updated existing docs to `Refactoring` status. If any existing component design docs do not adhere to the Component Design blueprint, update them to align.</step>
|
|
33
33
|
<step n="6" name="Architecture Maintenance">Update `docs/architecture/ARCHITECTURE.md`'s `Component & Boundary Map` and `Key Architectural Decisions` if needed to align.</step>
|
|
34
34
|
<step n="7" name="Delta Analysis">Use `EXECUTE git grep` and `READ` on all targeted components, contracts, interfaces, and DTOs referenced in the design. Verify implementation status of every piece the design calls for. Catalog the delta: what already exists (skip — do NOT include as a deliverable), what is partially implemented (note remaining work), and what is entirely missing (add as pending). Only truly unimplemented or incomplete work SHALL appear in the slice deliverables. Do NOT create entries for work that demonstrably exists.</step>
|
|
35
35
|
<step n="8" name="Create Slice">`CREATE` exactly ONE Vertical Slice following these rules:
|
|
@@ -91,17 +91,32 @@
|
|
|
91
91
|
</section>
|
|
92
92
|
<section n="3" name="Failure Modes">
|
|
93
93
|
<item name="Heading">## Failure Modes</item>
|
|
94
|
-
<item name="Content">Explicitly list known failure modes for this component. Ports (Interfaces) MUST NOT return generic "error-swallowing" values (like `None` or `False`) to hide internal crashes; instead, they should allow exceptions to propagate to the boundary where they can be handled transparently.</item>
|
|
94
|
+
<item name="Content">Explicitly list known failure modes for this component. Each entry MUST reference the specific precondition or postcondition it violates. Ports (Interfaces) MUST NOT return generic "error-swallowing" values (like `None` or `False`) to hide internal crashes; instead, they should allow exceptions to propagate to the boundary where they can be handled transparently.</item>
|
|
95
|
+
<item name="Content">Format: "- **Failure Title**: Violates the precondition/postcondition 'XYZ'. The component MUST raise a specific exception rather than returning a partial/invalid result."</item>
|
|
95
96
|
</section>
|
|
96
|
-
<section n="4" name="
|
|
97
|
+
<section n="4" name="Class Invariants">
|
|
98
|
+
<item name="Heading">## Class Invariants</item>
|
|
99
|
+
<item name="Content">Document conditions that must always hold true for instances of this component (e.g., "turn_counter >= 0", "plan is never None after initialization"). Invariants are checked at public method entry and exit. If no invariants apply, state: "None — stateless component."</item>
|
|
100
|
+
</section>
|
|
101
|
+
<section n="5" name="Ports">
|
|
97
102
|
<item name="Heading">## Ports</item>
|
|
98
103
|
<item name="Content">Explicit inbound/outbound relationships.</item>
|
|
104
|
+
<item name="Content">If this component depends on other Ports' contracts, list them under **Contract Dependencies:** with links to the relevant doc sections. Example: "- Relies on `IFileSystemManager.read()` postcondition 'Returns file content as string'."</item>
|
|
99
105
|
</section>
|
|
100
|
-
<section n="
|
|
106
|
+
<section n="6" name="Logic">
|
|
101
107
|
<item name="Heading">## Implementation Details / Logic</item>
|
|
102
108
|
</section>
|
|
103
|
-
<section n="
|
|
109
|
+
<section n="7" name="Contracts">
|
|
104
110
|
<item name="Heading">## Data Contracts / Methods</item>
|
|
111
|
+
<item name="Content">Document every public method with the following standard subsections:</item>
|
|
112
|
+
<item name="Content">```text
|
|
113
|
+
### `method_name`
|
|
114
|
+
- **Preconditions:** (required) What must be true before calling. Use "must" language: "key must be a non-empty string."
|
|
115
|
+
- **Postconditions:** (required) What is guaranteed after successful return. Use "returns" or "ensures" language: "Returns a valid ConfigValue. Ensures the cache is updated."
|
|
116
|
+
- **Exceptions:** (required) What errors can be raised and under what conditions.
|
|
117
|
+
- **Invariants:** (optional) Class-level invariants that must hold before and after this method executes.
|
|
118
|
+
```</item>
|
|
119
|
+
<item name="Content">**Contract Enforcement:** Implementations MUST validate preconditions with explicit guard clauses (assertions or early raises). Postcondition failures MUST be surfaced as specific exceptions, never swallowed. Invariants should be checked at public method entry and exit where practical. Core domain code should enforce contracts at boundary crossings (ports), not deep inside implementation details.</item>
|
|
105
120
|
</section>
|
|
106
121
|
<directory_conventions>
|
|
107
122
|
<rule>Doc path: docs/architecture/BOUNDARY/LAYER/TYPE/name.md -> Source path: src/{pkg}/BOUNDARY/LAYER/TYPE/name.py -> Test path: tests/suites/TEST_TYPE/BOUNDARY/LAYER/TYPE/test_name.py</rule>
|
|
@@ -140,7 +155,7 @@
|
|
|
140
155
|
</section>
|
|
141
156
|
<section n="6" name="Deliverables">
|
|
142
157
|
<item name="Heading">## Deliverables</item>
|
|
143
|
-
<item name="Content">Checklist of atomic units of work ordered following the
|
|
158
|
+
<item name="Content">Checklist of atomic units of work ordered following the Tracer Bullet Dependency Sequence: 1. Contract (Interfaces; DTOs; Signatures; Expansion), 2. Harness (Infrastructure; Env), 3. Seam (Abstraction; Toggles; DI), 4. Wiring (The Tracer Bullet: Connecting components to the Composition Root/entrypoints returning trivial/hardcoded data to prove the end-to-end path), 5. Logic (Replacing the Tracer Bullet with complex Rules/Algorithms via TDD), 6. Migration (Updating consumers), 7. Refactor (Internal restructuring; non-breaking), 8. Cleanup (Pruning; Contraction). Every deliverable MUST be an atomic "Green-to-Green" transition; behavioral tests MUST be bundled with Wiring; Unit tests with Logic. Format: `- [ ] **Type** - Description`.</item>
|
|
144
159
|
</section>
|
|
145
160
|
<section n="7" name="Implementation Notes">
|
|
146
161
|
<item name="Heading">## Implementation Notes</item>
|
|
@@ -243,6 +258,9 @@ WIP & REMINDERS:
|
|
|
243
258
|
2. Commit Message: Use Conventional Commits '<type>(<scope>): <description>' wrapped in single quotes. Use imperative mood. Append ! for breaking changes.
|
|
244
259
|
3. Execution: You MUST test, stage, commit locally, pull safely with rebase, and push by executing exactly this codeblock:
|
|
245
260
|
```shell
|
|
261
|
+
# 0. Ensure commit hooks are installed
|
|
262
|
+
pre-commit install -t pre-commit -t post-commit
|
|
263
|
+
|
|
246
264
|
# 1. Stage changes and run pre-commit
|
|
247
265
|
git add .
|
|
248
266
|
pre-commit run || true
|
{teddy_cli-0.1.4 → teddy_cli-0.1.5}/src/teddy_executor/resources/config/prompts/assistant.xml
RENAMED
|
@@ -90,6 +90,9 @@ WIP & REMINDERS:
|
|
|
90
90
|
2. Commit Message: Use Conventional Commits '<type>(<scope>): <description>' wrapped in single quotes. Use imperative mood. Append ! for breaking changes.
|
|
91
91
|
3. Execution: You MUST test, stage, commit locally, pull safely with rebase, and push by executing exactly this codeblock:
|
|
92
92
|
```shell
|
|
93
|
+
# 0. Ensure commit hooks are installed
|
|
94
|
+
pre-commit install -t pre-commit -t post-commit
|
|
95
|
+
|
|
93
96
|
# 1. Stage changes and run pre-commit
|
|
94
97
|
git add .
|
|
95
98
|
pre-commit run || true
|