wade-cli 0.53.0__tar.gz → 0.53.1__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.
- {wade_cli-0.53.0 → wade_cli-0.53.1}/CHANGELOG.md +6 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/PKG-INFO +10 -1
- {wade_cli-0.53.0 → wade_cli-0.53.1}/README.md +9 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/architecture.md +11 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/pyproject.toml +1 -1
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/__init__.py +1 -1
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/core.py +51 -12
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/plan_service.py +4 -1
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/terminal.py +97 -1
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_launch_context.py +148 -7
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_plan_service.py +1 -1
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_terminal_launch.py +40 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.gitattributes +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/auto-version.yml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/ci.yml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/pr-title-lint.yml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/publish.yml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.github/workflows/release.yml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.gitignore +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.pre-commit-config.yaml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/.wade.yml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/AGENTS.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/CONTRIBUTING.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/KNOWLEDGE.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/KNOWLEDGE.ratings.jsonl +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/assets/wade.png +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/docker-compose.e2e.yml +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/documentation-policies.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/extending.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/skills-system.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/testing.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/docs/dev/workflows-and-skills.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/install.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/auto_version.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/changelog.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/check-all.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/check.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/fmt.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/hooks/pre-push +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/install-hooks.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/release.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/setup-worktree.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-concurrency.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-e2e-docker.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-e2e.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-live-ai-taskr.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-live-ai.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test-live-gh.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/scripts/test.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/__main__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/admin.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/autocomplete.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/deps_session.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/hook.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/implementation_session.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/knowledge.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/main.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/plan_session.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/review.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/review_pr_comments_session.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/session.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/session_shared.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/skills.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/task.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/cli/worktree.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/loader.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/migrations.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/config/schema.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/branch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/hooks.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/pr.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/repo.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/stash.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/sync.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/git/worktree.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/hooks/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/hooks/cli.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/hooks/policies.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/logging/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/logging/context.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/logging/setup.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/batch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/config.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/delegation.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/deps.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/events.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/hooks.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/permission.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/readiness.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/review.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/session.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/session_manifest.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/skill.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/task.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/workflow.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/models/worktree.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/_pr_delegate.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/base.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/clickup.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/github.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/markdown.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/providers/registry.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/ai_resolution.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/batch_review_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/bot_trigger.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/check_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/delegation_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/deps_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/documentation_receipt_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/_shared.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/batch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/bootstrap.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/cleanup.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/done.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/draft_pr.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/lifecycle.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/sync.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/implementation_service/usage_tracking.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/auth.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/commands.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/config_io.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/manifest.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/migrations.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/prompts_ai.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/prompts_setup.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/init_service/shell.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/knowledge_recovery.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/knowledge_search.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/knowledge_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/prompt_delivery.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_delegation_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_record_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/review_settle.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/session_composition_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/skill_diagnostics_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/skill_invocation_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/smart_start.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/services/task_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/catalog.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/discovery.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/doc_targets.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/installer.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/materializer.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/pointer.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/resolver.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/skills/validation.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/ui/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/ui/console.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/ui/prompts.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/body_markers.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/clipboard.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/conventional.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/filelock.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/gitref.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/http.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/install.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/knowledge_file.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/markdown.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/markers.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/paths.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/plan_validation.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/process.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/safe_state.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/slug.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/stale_base.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/templates.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/token_usage_markdown.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/src/wade/utils/update_check.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/agents-pointer.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/hooks/commit-msg +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/hooks/pre-commit +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/hooks/pre-push +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/deps-analysis.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/implement-context.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/plan-session.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-batch.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-code.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-plan.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/review-pr-comments.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/session-start-implement.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/session-start-plan.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/prompts/session-start-review.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/setup-worktree.sh.example +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/batch-review/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/code-review/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/dependency-analysis/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/implementation/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/knowledge/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/plan-review/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/planning/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/review-comments/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/SKILL.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/examples.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/plan-format.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/skills/task/reference/session-summary-format.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/statusline-command.sh +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/completion.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/documentation-step.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/interaction-policy.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/knowledge-step.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/_partials/review-budget.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/implementation.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/plan.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/documentation.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/new-plan.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/plan-output-contract.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/pr-summary-format.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/recovery.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/session-summary-format.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/reference/tracking-issues.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/templates/workflows/review-pr-comments.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_batch_multi_pr.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_knowledge_merge.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_lock_retry.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/concurrency/test_stash_race.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/conftest.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/_support.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/conftest.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/mock_gh_script.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_admin_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_check_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_implement_work_done_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_knowledge_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_mock_gh_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_plan_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_review_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_smart_start_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_task_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/e2e/test_work_sync_list_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/live/taskr_header_hi_body.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/live/taskr_header_howdy_body.md +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/claude_session.txt +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/codex_session.txt +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/copilot_session.txt +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/fixtures/transcripts/generic_session.txt +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_autostash.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_check.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_dynamic_skill_sessions.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_git.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_implementation_lifecycle.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_init.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_knowledge.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_markdown_provider.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_skill_context_budget.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/integration/test_skill_install.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/test_wade_live_ai.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/test_wade_live_ai_taskr.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/live/test_wade_live_gh.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/test_cli_basics.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/conftest.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_done_doc_pass_advisory.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_done_review_hint.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_knowledge_cli.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_review_cli.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_session_subapps.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_shell_init.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_skills_session_cli.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_task_cli.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_work_ai_flags.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_cli/test_work_cli.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_bot_review_config.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_dynamic_skill_config.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_loader.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_merge_strategy_migration.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_migrations.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_config/test_migrations_atomic.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_console.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_effort.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_all_patches_present.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_branch_list.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_branch_slugify.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_detached_worktree.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_is_merged_into.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_main_checkout_root.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_merge_count.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_base.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_lookup.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_retry.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_pr_title.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_repo.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_reset_branch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_scaffold_commit.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_stash_drop.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_sync_conflicted_files.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_tip_commit_is_empty.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_git/test_worktree_remove.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_hook_cli.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_policies.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_post_tool_use.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_hooks/test_session_start.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_batch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_config_models.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_delegation.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_deps_models.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_deps_partition.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_review.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_review_expectation.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_task_models.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_models/test_workflow_models.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_permission_mode.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_clickup.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_github.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_github_pr.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_providers/test_markdown.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_ai_resolution.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_batch_build_cmd.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_batch_pr_index.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_batch_review_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_allowlist.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_guard_matcher.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_hook_timeout.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_post_tool_use.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_pre_push.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bootstrap_session_start.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_bot_trigger.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_catchup.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_catchup_reconcile.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_check.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_cleanup_loss_guard.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_codex_worktree_launch_context.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_delegation_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_deps_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_documentation_receipt.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_gates.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_idempotent.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_plan_flag.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_done_pr_title_sync.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_draft_pr_base_fetch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_draft_pr_retarget.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_done_sync.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_service_resume.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_init.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_init_shell_integration.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_knowledge_recovery.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_knowledge_search.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_knowledge_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_managed_git_hooks.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_no_direct_git.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_poll_for_reviews.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_post_implementation_lifecycle.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_pr_lookup_guards.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_pr_summary_path.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_pre_push_backstop.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_preserve_session_data.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_prompt_delivery.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_push_recovery.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_resolve_task_branch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_resume_autonomy.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_delegation_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_receipts.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_records.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_review_status_block.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_skill_diagnostics_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_smart_start.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_smart_start_resume.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_stale_base_surface.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_sync_base_branch.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_task_service.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_tracked_managed_files.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_worktree_gitignore.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_crossby_contract.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_doc_targets.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_dynamic_skills.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_generic_builtin_boundaries.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_knowledge_attributes.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_managed_gitignore.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_pointer.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_skills/test_review_budget_partial.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/__init__.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_body_markers.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_conventional.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_filelock.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_gitref.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_http.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_install.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_markdown.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_markers.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_plan_validation.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_process.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_prompts.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_slug.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_stale_base.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_terminal.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_utils/test_update_check.py +0 -0
- {wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_yolo.py +0 -0
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Conventional Commits](https://conventionalcommits.org/).
|
|
6
6
|
|
|
7
|
+
## [v0.53.1] — 2026-08-31
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- relaunch Codex with implementation network policy after plan handoff (#473) (202018b)
|
|
12
|
+
|
|
7
13
|
## [v0.53.0] — 2026-08-31
|
|
8
14
|
|
|
9
15
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: wade-cli
|
|
3
|
-
Version: 0.53.
|
|
3
|
+
Version: 0.53.1
|
|
4
4
|
Summary: WADE — Workflow for AI-Driven Engineering
|
|
5
5
|
Project-URL: Homepage, https://github.com/ivanviragine/wade
|
|
6
6
|
Project-URL: Repository, https://github.com/ivanviragine/wade
|
|
@@ -730,6 +730,15 @@ flag enables it there. Precedence for the commands that honor it is
|
|
|
730
730
|
enable network for a WADE-managed sandbox. Enabling network never disables the
|
|
731
731
|
sandbox and never changes approval-policy semantics.
|
|
732
732
|
|
|
733
|
+
An accepted single-issue plan handoff starts implementation from a planning
|
|
734
|
+
session that is always network-off. When both the planning tool and resolved
|
|
735
|
+
implementation tool are Codex and the implementation policy resolves to enabled,
|
|
736
|
+
WADE starts a fresh detached Codex context with network explicitly enabled;
|
|
737
|
+
reusing the planner process would retain its immutable network-off sandbox. If
|
|
738
|
+
WADE cannot open that detached context, the handoff fails rather than reporting
|
|
739
|
+
an implementation session ready. Open a new host terminal and run the displayed
|
|
740
|
+
`wade implement <issue> --network` command to continue with the required policy.
|
|
741
|
+
|
|
733
742
|
### Session readiness and least-privilege remediation
|
|
734
743
|
|
|
735
744
|
Every `wade` command an agent runs is a child of the AI tool. It inherits that
|
|
@@ -694,6 +694,15 @@ flag enables it there. Precedence for the commands that honor it is
|
|
|
694
694
|
enable network for a WADE-managed sandbox. Enabling network never disables the
|
|
695
695
|
sandbox and never changes approval-policy semantics.
|
|
696
696
|
|
|
697
|
+
An accepted single-issue plan handoff starts implementation from a planning
|
|
698
|
+
session that is always network-off. When both the planning tool and resolved
|
|
699
|
+
implementation tool are Codex and the implementation policy resolves to enabled,
|
|
700
|
+
WADE starts a fresh detached Codex context with network explicitly enabled;
|
|
701
|
+
reusing the planner process would retain its immutable network-off sandbox. If
|
|
702
|
+
WADE cannot open that detached context, the handoff fails rather than reporting
|
|
703
|
+
an implementation session ready. Open a new host terminal and run the displayed
|
|
704
|
+
`wade implement <issue> --network` command to continue with the required policy.
|
|
705
|
+
|
|
697
706
|
### Session readiness and least-privilege remediation
|
|
698
707
|
|
|
699
708
|
Every `wade` command an agent runs is a child of the AI tool. It inherits that
|
|
@@ -499,6 +499,17 @@ picks (implement / batch / review). `None` (unset) still re-resolves per the
|
|
|
499
499
|
routed command's own config, matching how a non-explicit `permission_mode` is
|
|
500
500
|
threaded.
|
|
501
501
|
|
|
502
|
+
Planning itself is always network-off, including when it later offers an
|
|
503
|
+
accepted single-issue implementation handoff. The internal `plan_handoff`
|
|
504
|
+
intent keeps implementation-network resolution at `implementation_service.start`.
|
|
505
|
+
If that handoff originates in Codex, resolves the implementation tool to Codex,
|
|
506
|
+
and resolves network access to enabled, implementation must create a fresh
|
|
507
|
+
detached Codex launch with an explicit `network_access=True` pin. It cannot reuse
|
|
508
|
+
the planner's immutable network-off sandbox. If the detached launch cannot be
|
|
509
|
+
created, the handoff fails closed and prints `wade implement <issue> --network`
|
|
510
|
+
for the user to run from a new host terminal; it must not fall back to an inline
|
|
511
|
+
or nested agent. All other nested-agent starts retain the ordinary launch guard.
|
|
512
|
+
|
|
502
513
|
### Phase-aware session readiness (#462)
|
|
503
514
|
|
|
504
515
|
`check_service.check_session_readiness()` applies the declarative
|
|
@@ -434,6 +434,7 @@ def start(
|
|
|
434
434
|
work_skills: list[str] | None = None,
|
|
435
435
|
review_skills: list[str] | None = None,
|
|
436
436
|
refresh_skills: bool = False,
|
|
437
|
+
plan_handoff: bool = False,
|
|
437
438
|
) -> ImplementResult:
|
|
438
439
|
"""Start an implementation session on an issue.
|
|
439
440
|
|
|
@@ -453,6 +454,10 @@ def start(
|
|
|
453
454
|
project_root: Repository root (defaults to CWD).
|
|
454
455
|
detach: If True, launch AI in a new terminal tab.
|
|
455
456
|
cd_only: If True, create worktree and print path only (no AI launch).
|
|
457
|
+
plan_handoff: Internal marker for an accepted ``wade plan`` handoff.
|
|
458
|
+
A network-enabled Codex implementation must launch in a fresh
|
|
459
|
+
detached context rather than inherit the planner's network-off
|
|
460
|
+
sandbox.
|
|
456
461
|
|
|
457
462
|
Returns:
|
|
458
463
|
ImplementResult with success/merged status.
|
|
@@ -931,9 +936,19 @@ def start(
|
|
|
931
936
|
return ImplementResult(success=True)
|
|
932
937
|
|
|
933
938
|
# AI-initiated start guard: if we're inside an AI CLI session,
|
|
934
|
-
# don't launch another AI tool — just print the worktree path.
|
|
939
|
+
# don't launch another AI tool — just print the worktree path. The
|
|
940
|
+
# one exception is a plan handoff from Codex to a network-enabled Codex
|
|
941
|
+
# implementation: network is a launch-time sandbox property, and the
|
|
942
|
+
# planner always ran with it disabled, so that handoff needs a fresh
|
|
943
|
+
# detached process instead of reusing this agent context.
|
|
935
944
|
detected_env = _detect_ai_cli_env()
|
|
936
|
-
|
|
945
|
+
requires_fresh_codex_handoff = (
|
|
946
|
+
plan_handoff
|
|
947
|
+
and detected_env == "CODEX_CLI"
|
|
948
|
+
and resolved_tool == AIToolID.CODEX.value
|
|
949
|
+
and resolved_network_access
|
|
950
|
+
)
|
|
951
|
+
if detected_env and not requires_fresh_codex_handoff:
|
|
937
952
|
logger.info(
|
|
938
953
|
"implementation.ai_launch_skipped",
|
|
939
954
|
reason="inside_ai_cli",
|
|
@@ -960,8 +975,10 @@ def start(
|
|
|
960
975
|
except OSError:
|
|
961
976
|
logger.warning("implementation.transcript_dir_failed")
|
|
962
977
|
|
|
963
|
-
# Detach mode: launch AI tool in a new terminal, don't block
|
|
964
|
-
|
|
978
|
+
# Detach mode: launch AI tool in a new terminal, don't block. A
|
|
979
|
+
# network-enabled Codex plan handoff is forced through this path so it
|
|
980
|
+
# cannot inherit the planner's immutable network-off sandbox.
|
|
981
|
+
if (detach or requires_fresh_codex_handoff) and resolved_tool:
|
|
965
982
|
cmd: list[str] | None = None
|
|
966
983
|
try:
|
|
967
984
|
detach_adapter = AbstractAITool.get(AIToolID(resolved_tool))
|
|
@@ -995,18 +1012,40 @@ def start(
|
|
|
995
1012
|
effort=resolved_effort,
|
|
996
1013
|
allowed_commands=config.permissions.allowed_commands,
|
|
997
1014
|
working_dir=worktree_path,
|
|
998
|
-
|
|
1015
|
+
# ``requires_fresh_codex_handoff`` already established
|
|
1016
|
+
# the resolved value is true; pin it explicitly for the
|
|
1017
|
+
# new Codex process rather than relying on ambient config.
|
|
1018
|
+
network_access=(
|
|
1019
|
+
True if requires_fresh_codex_handoff else resolved_network_access
|
|
1020
|
+
),
|
|
999
1021
|
**permission_mode_launch_kwargs(resolved_permission_mode),
|
|
1000
1022
|
)
|
|
1001
1023
|
except (ValueError, KeyError):
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1024
|
+
# A fresh Codex handoff must retain its explicit sandbox,
|
|
1025
|
+
# network, permission, and prompt arguments. Do not replace a
|
|
1026
|
+
# failed build with bare ``codex``, which would bypass them.
|
|
1027
|
+
if not requires_fresh_codex_handoff:
|
|
1028
|
+
cmd = [resolved_tool]
|
|
1029
|
+
|
|
1030
|
+
if cmd is not None:
|
|
1031
|
+
console.step(f"Launching {resolved_tool} in new terminal...")
|
|
1032
|
+
if launch_in_new_terminal(
|
|
1033
|
+
cmd,
|
|
1034
|
+
cwd=str(worktree_path),
|
|
1035
|
+
title=work_title,
|
|
1036
|
+
wait_for_ready=requires_fresh_codex_handoff,
|
|
1037
|
+
):
|
|
1038
|
+
console.success(f"Detached AI session for #{task.id}")
|
|
1039
|
+
stop_title_keeper()
|
|
1040
|
+
return ImplementResult(success=True)
|
|
1041
|
+
if requires_fresh_codex_handoff:
|
|
1042
|
+
console.error(
|
|
1043
|
+
"Could not launch a fresh Codex implementation session with network access."
|
|
1044
|
+
)
|
|
1045
|
+
console.hint("Open a new host terminal, then run:")
|
|
1046
|
+
console.detail(f"wade implement {task.id} --network")
|
|
1008
1047
|
stop_title_keeper()
|
|
1009
|
-
return ImplementResult(success=
|
|
1048
|
+
return ImplementResult(success=False)
|
|
1010
1049
|
console.warn("Could not launch in new terminal — falling back to inline")
|
|
1011
1050
|
detach = False
|
|
1012
1051
|
# Fall through to inline launch below
|
|
@@ -1539,7 +1539,10 @@ def _offer_to_implement(
|
|
|
1539
1539
|
return False
|
|
1540
1540
|
|
|
1541
1541
|
try:
|
|
1542
|
-
|
|
1542
|
+
# Planning always launches with network disabled. Tell implementation
|
|
1543
|
+
# startup this is an accepted handoff so it can establish a fresh Codex
|
|
1544
|
+
# context when its independently resolved policy needs network access.
|
|
1545
|
+
result = start_implementation_session(target=issue_number, plan_handoff=True)
|
|
1543
1546
|
return result.success
|
|
1544
1547
|
except Exception:
|
|
1545
1548
|
logger.exception("plan.work_session_start_failed", issue=issue_number)
|
|
@@ -11,6 +11,7 @@ import sys
|
|
|
11
11
|
import threading
|
|
12
12
|
import time
|
|
13
13
|
from collections.abc import Sequence
|
|
14
|
+
from pathlib import Path
|
|
14
15
|
|
|
15
16
|
import structlog
|
|
16
17
|
|
|
@@ -20,6 +21,9 @@ _title_keeper_thread: threading.Thread | None = None
|
|
|
20
21
|
_title_keeper_running = False
|
|
21
22
|
|
|
22
23
|
_TERMINAL_TITLE_MAX_LEN = 50
|
|
24
|
+
_LAUNCH_READY_TIMEOUT_SECONDS = 5.0
|
|
25
|
+
_LAUNCH_READY_POLL_SECONDS = 0.05
|
|
26
|
+
_LAUNCH_STABILITY_SECONDS = 0.5
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
def _truncate_terminal_title(text: str) -> str:
|
|
@@ -126,6 +130,74 @@ def _create_temp_script(command: list[str], cwd: str | None = None) -> str:
|
|
|
126
130
|
return tmp_path
|
|
127
131
|
|
|
128
132
|
|
|
133
|
+
def _create_readiness_file() -> str:
|
|
134
|
+
"""Create an empty, private file used for a detached-launch acknowledgement."""
|
|
135
|
+
import tempfile
|
|
136
|
+
|
|
137
|
+
fd, path = tempfile.mkstemp(prefix="wade-launch-", suffix=".ready")
|
|
138
|
+
os.close(fd)
|
|
139
|
+
return path
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _create_readiness_script(
|
|
143
|
+
command: list[str],
|
|
144
|
+
cwd: str | None,
|
|
145
|
+
readiness_file: str,
|
|
146
|
+
) -> str:
|
|
147
|
+
"""Create a launcher which acknowledges only after its child stays alive.
|
|
148
|
+
|
|
149
|
+
Terminal brokers can confirm that they accepted a tab/window request but
|
|
150
|
+
cannot observe a command executed inside it. This wrapper reports
|
|
151
|
+
``ready`` only after the command has remained alive briefly, which catches
|
|
152
|
+
an unavailable executable and launch-time failures before the caller
|
|
153
|
+
reports a successful handoff.
|
|
154
|
+
"""
|
|
155
|
+
import tempfile
|
|
156
|
+
|
|
157
|
+
cmd_str = " ".join(shlex.quote(str(c)) for c in command)
|
|
158
|
+
quoted_cwd = shlex.quote(cwd or ".")
|
|
159
|
+
quoted_readiness_file = shlex.quote(readiness_file)
|
|
160
|
+
with tempfile.NamedTemporaryFile(prefix="wade-", suffix=".sh", delete=False, mode="w") as f:
|
|
161
|
+
tmp_path = f.name
|
|
162
|
+
f.write(
|
|
163
|
+
"#!/usr/bin/env bash\n"
|
|
164
|
+
f"cd {quoted_cwd} || {{ printf 'failed\\n' > {quoted_readiness_file}; exit 1; }}\n"
|
|
165
|
+
f"{cmd_str} &\n"
|
|
166
|
+
"child_pid=$!\n"
|
|
167
|
+
f"sleep {_LAUNCH_STABILITY_SECONDS}\n"
|
|
168
|
+
'if kill -0 "$child_pid" 2>/dev/null; then\n'
|
|
169
|
+
f" printf 'ready\\n' > {quoted_readiness_file}\n"
|
|
170
|
+
"else\n"
|
|
171
|
+
' wait "$child_pid"\n'
|
|
172
|
+
f" printf 'failed\\n' > {quoted_readiness_file}\n"
|
|
173
|
+
" exit 1\n"
|
|
174
|
+
"fi\n"
|
|
175
|
+
'wait "$child_pid"\n'
|
|
176
|
+
)
|
|
177
|
+
os.chmod(tmp_path, 0o700)
|
|
178
|
+
return tmp_path
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _wait_for_launch_readiness(
|
|
182
|
+
readiness_file: str,
|
|
183
|
+
timeout: float = _LAUNCH_READY_TIMEOUT_SECONDS,
|
|
184
|
+
) -> bool:
|
|
185
|
+
"""Wait for the detached launch wrapper to acknowledge a live child."""
|
|
186
|
+
deadline = time.monotonic() + timeout
|
|
187
|
+
path = Path(readiness_file)
|
|
188
|
+
while time.monotonic() < deadline:
|
|
189
|
+
try:
|
|
190
|
+
status = path.read_text()
|
|
191
|
+
except OSError:
|
|
192
|
+
return False
|
|
193
|
+
if status == "ready\n":
|
|
194
|
+
return True
|
|
195
|
+
if status == "failed\n":
|
|
196
|
+
return False
|
|
197
|
+
time.sleep(_LAUNCH_READY_POLL_SECONDS)
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
|
|
129
201
|
def detect_terminal() -> str | None:
|
|
130
202
|
"""Detect the current terminal emulator.
|
|
131
203
|
|
|
@@ -154,12 +226,36 @@ def launch_in_new_terminal(
|
|
|
154
226
|
command: list[str],
|
|
155
227
|
cwd: str | None = None,
|
|
156
228
|
title: str | None = None,
|
|
229
|
+
*,
|
|
230
|
+
wait_for_ready: bool = False,
|
|
157
231
|
) -> bool:
|
|
158
232
|
"""Launch a command in a new terminal window/tab.
|
|
159
233
|
|
|
160
234
|
Tries Ghostty, iTerm2, Terminal.app, tmux in order.
|
|
161
|
-
Returns True if launched successfully, False otherwise.
|
|
235
|
+
Returns True if launched successfully, False otherwise. When
|
|
236
|
+
``wait_for_ready`` is set, require a brief child-process acknowledgement
|
|
237
|
+
rather than treating terminal-broker acceptance as success.
|
|
162
238
|
"""
|
|
239
|
+
if not wait_for_ready:
|
|
240
|
+
return _launch_in_new_terminal(command, cwd=cwd, title=title)
|
|
241
|
+
|
|
242
|
+
readiness_file = _create_readiness_file()
|
|
243
|
+
launcher_script = _create_readiness_script(command, cwd, readiness_file)
|
|
244
|
+
try:
|
|
245
|
+
if not _launch_in_new_terminal([launcher_script], title=title):
|
|
246
|
+
return False
|
|
247
|
+
return _wait_for_launch_readiness(readiness_file)
|
|
248
|
+
finally:
|
|
249
|
+
_safe_unlink(launcher_script)
|
|
250
|
+
_safe_unlink(readiness_file)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _launch_in_new_terminal(
|
|
254
|
+
command: list[str],
|
|
255
|
+
cwd: str | None = None,
|
|
256
|
+
title: str | None = None,
|
|
257
|
+
) -> bool:
|
|
258
|
+
"""Launch a command in a new terminal window/tab without confirmation."""
|
|
163
259
|
terminal = detect_terminal()
|
|
164
260
|
|
|
165
261
|
if terminal == "ghostty":
|
{wade_cli-0.53.0 → wade_cli-0.53.1}/tests/unit/test_services/test_implementation_launch_context.py
RENAMED
|
@@ -22,7 +22,7 @@ from unittest.mock import MagicMock, patch
|
|
|
22
22
|
import pytest
|
|
23
23
|
|
|
24
24
|
from wade.git.pr import PRLookup, PRRef
|
|
25
|
-
from wade.models.config import AIConfig, ProjectConfig, ProjectSettings
|
|
25
|
+
from wade.models.config import AICommandConfig, AIConfig, ProjectConfig, ProjectSettings
|
|
26
26
|
from wade.models.permission import PermissionMode
|
|
27
27
|
from wade.models.task import Task, TaskState
|
|
28
28
|
from wade.models.worktree import Worktree
|
|
@@ -32,7 +32,15 @@ _CORE = "wade.services.implementation_service.core"
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@contextlib.contextmanager
|
|
35
|
-
def _driven_start(
|
|
35
|
+
def _driven_start(
|
|
36
|
+
worktree_path: Path,
|
|
37
|
+
*,
|
|
38
|
+
network_access_config: bool,
|
|
39
|
+
implement_network_access_config: bool | None = None,
|
|
40
|
+
detected_env: str | None = None,
|
|
41
|
+
resolved_tool: str = "codex",
|
|
42
|
+
terminal_launch_succeeds: bool = True,
|
|
43
|
+
) -> Iterator[MagicMock]:
|
|
36
44
|
"""Patch impl start()'s prerequisites and yield the spy AI adapter.
|
|
37
45
|
|
|
38
46
|
Every heavy step between ``start()`` entry and the AI launch is stubbed so the
|
|
@@ -40,7 +48,10 @@ def _driven_start(worktree_path: Path, *, network_access_config: bool) -> Iterat
|
|
|
40
48
|
worktree at *worktree_path*, and ``network_access`` resolved from config.
|
|
41
49
|
"""
|
|
42
50
|
config = ProjectConfig(
|
|
43
|
-
ai=AIConfig(
|
|
51
|
+
ai=AIConfig(
|
|
52
|
+
network_access=network_access_config,
|
|
53
|
+
implement=AICommandConfig(network_access=implement_network_access_config),
|
|
54
|
+
),
|
|
44
55
|
project=ProjectSettings(main_branch="main", branch_prefix="feat"),
|
|
45
56
|
)
|
|
46
57
|
task = Task(id="42", title="Fix the widget", body="broken", state=TaskState.OPEN)
|
|
@@ -80,23 +91,26 @@ def _driven_start(worktree_path: Path, *, network_access_config: bool) -> Iterat
|
|
|
80
91
|
p(patch(f"{_CORE}.bootstrap_worktree"))
|
|
81
92
|
p(patch(f"{_CORE}._catchup_and_surface_staleness", return_value=None))
|
|
82
93
|
p(patch(f"{_CORE}.build_implementation_prompt", return_value="PROMPT"))
|
|
83
|
-
p(patch(f"{_CORE}.resolve_ai_tool", return_value=
|
|
94
|
+
p(patch(f"{_CORE}.resolve_ai_tool", return_value=resolved_tool))
|
|
84
95
|
p(patch(f"{_CORE}.resolve_model", return_value=None))
|
|
85
96
|
p(patch(f"{_CORE}.resolve_effort", return_value=None))
|
|
86
97
|
p(
|
|
87
98
|
patch(
|
|
88
99
|
f"{_CORE}.confirm_ai_selection",
|
|
89
|
-
return_value=(
|
|
100
|
+
return_value=(resolved_tool, None, None, PermissionMode.DEFAULT),
|
|
90
101
|
)
|
|
91
102
|
)
|
|
92
|
-
p(patch(f"{_CORE}._detect_ai_cli_env", return_value=
|
|
103
|
+
p(patch(f"{_CORE}._detect_ai_cli_env", return_value=detected_env))
|
|
93
104
|
p(patch(f"{_CORE}.set_terminal_title"))
|
|
94
105
|
p(patch(f"{_CORE}.start_title_keeper"))
|
|
95
106
|
p(patch(f"{_CORE}.stop_title_keeper"))
|
|
96
107
|
p(patch(f"{_CORE}.deliver_prompt_if_needed"))
|
|
97
|
-
p(
|
|
108
|
+
terminal_launch = p(
|
|
109
|
+
patch(f"{_CORE}.launch_in_new_terminal", return_value=terminal_launch_succeeds)
|
|
110
|
+
)
|
|
98
111
|
p(patch("crossby.ai_tools.AbstractAITool.get", return_value=spy))
|
|
99
112
|
with stack:
|
|
113
|
+
spy.terminal_launch = terminal_launch
|
|
100
114
|
yield spy
|
|
101
115
|
|
|
102
116
|
|
|
@@ -164,3 +178,130 @@ class TestImplementationLaunchContext:
|
|
|
164
178
|
kwargs = spy.build_resume_command.call_args.kwargs
|
|
165
179
|
assert kwargs["working_dir"] == worktree
|
|
166
180
|
assert kwargs["network_access"] is True
|
|
181
|
+
|
|
182
|
+
@pytest.mark.parametrize(
|
|
183
|
+
("global_network_access", "implement_network_access"),
|
|
184
|
+
[(True, None), (False, True)],
|
|
185
|
+
ids=["global-policy", "implement-policy"],
|
|
186
|
+
)
|
|
187
|
+
def test_network_enabled_codex_plan_handoff_launches_fresh_detached_context(
|
|
188
|
+
self,
|
|
189
|
+
worktree: Path,
|
|
190
|
+
global_network_access: bool,
|
|
191
|
+
implement_network_access: bool | None,
|
|
192
|
+
) -> None:
|
|
193
|
+
"""A Codex plan handoff cannot reuse the planner's network-off sandbox."""
|
|
194
|
+
with _driven_start(
|
|
195
|
+
worktree,
|
|
196
|
+
network_access_config=global_network_access,
|
|
197
|
+
implement_network_access_config=implement_network_access,
|
|
198
|
+
detected_env="CODEX_CLI",
|
|
199
|
+
) as spy:
|
|
200
|
+
spy.build_launch_command.return_value = ["codex"]
|
|
201
|
+
|
|
202
|
+
result = start(target="42", plan_handoff=True)
|
|
203
|
+
|
|
204
|
+
assert result.success is True
|
|
205
|
+
assert spy.build_launch_command.call_args.kwargs["working_dir"] == worktree
|
|
206
|
+
assert spy.build_launch_command.call_args.kwargs["network_access"] is True
|
|
207
|
+
assert spy.terminal_launch.call_args.kwargs["wait_for_ready"] is True
|
|
208
|
+
spy.launch.assert_not_called()
|
|
209
|
+
|
|
210
|
+
def test_network_disabled_codex_plan_handoff_keeps_nested_launch_guard(
|
|
211
|
+
self, worktree: Path
|
|
212
|
+
) -> None:
|
|
213
|
+
"""A false policy must not gain network access merely to leave planning."""
|
|
214
|
+
with _driven_start(
|
|
215
|
+
worktree,
|
|
216
|
+
network_access_config=False,
|
|
217
|
+
detected_env="CODEX_CLI",
|
|
218
|
+
) as spy:
|
|
219
|
+
result = start(target="42", plan_handoff=True)
|
|
220
|
+
|
|
221
|
+
assert result.success is True
|
|
222
|
+
spy.build_launch_command.assert_not_called()
|
|
223
|
+
spy.launch.assert_not_called()
|
|
224
|
+
|
|
225
|
+
def test_failed_fresh_codex_plan_handoff_fails_closed_with_restart_command(
|
|
226
|
+
self, worktree: Path
|
|
227
|
+
) -> None:
|
|
228
|
+
"""The handoff must never fall through to an inline planner context."""
|
|
229
|
+
with (
|
|
230
|
+
_driven_start(
|
|
231
|
+
worktree,
|
|
232
|
+
network_access_config=True,
|
|
233
|
+
detected_env="CODEX_CLI",
|
|
234
|
+
terminal_launch_succeeds=False,
|
|
235
|
+
) as spy,
|
|
236
|
+
patch(f"{_CORE}.console") as mock_console,
|
|
237
|
+
):
|
|
238
|
+
spy.build_launch_command.return_value = ["codex"]
|
|
239
|
+
|
|
240
|
+
result = start(target="42", plan_handoff=True)
|
|
241
|
+
|
|
242
|
+
assert result.success is False
|
|
243
|
+
spy.launch.assert_not_called()
|
|
244
|
+
mock_console.detail.assert_any_call("wade implement 42 --network")
|
|
245
|
+
|
|
246
|
+
@pytest.mark.parametrize("command_error", [ValueError, KeyError])
|
|
247
|
+
def test_fresh_codex_plan_handoff_fails_closed_when_command_build_fails(
|
|
248
|
+
self, worktree: Path, command_error: type[Exception]
|
|
249
|
+
) -> None:
|
|
250
|
+
"""A builder failure must not degrade to an unconfigured ``codex`` command."""
|
|
251
|
+
with (
|
|
252
|
+
_driven_start(
|
|
253
|
+
worktree,
|
|
254
|
+
network_access_config=True,
|
|
255
|
+
detected_env="CODEX_CLI",
|
|
256
|
+
) as spy,
|
|
257
|
+
patch(f"{_CORE}.console") as mock_console,
|
|
258
|
+
patch(f"{_CORE}.launch_in_new_terminal") as mock_launch,
|
|
259
|
+
):
|
|
260
|
+
spy.build_launch_command.side_effect = command_error("build failed")
|
|
261
|
+
|
|
262
|
+
result = start(target="42", plan_handoff=True)
|
|
263
|
+
|
|
264
|
+
assert result.success is False
|
|
265
|
+
mock_launch.assert_not_called()
|
|
266
|
+
spy.launch.assert_not_called()
|
|
267
|
+
mock_console.detail.assert_any_call("wade implement 42 --network")
|
|
268
|
+
|
|
269
|
+
def test_non_handoff_codex_session_keeps_nested_launch_guard(self, worktree: Path) -> None:
|
|
270
|
+
"""Ordinary ``wade implement`` calls still do not recursively launch Codex."""
|
|
271
|
+
with _driven_start(
|
|
272
|
+
worktree,
|
|
273
|
+
network_access_config=True,
|
|
274
|
+
detected_env="CODEX_CLI",
|
|
275
|
+
) as spy:
|
|
276
|
+
result = start(target="42")
|
|
277
|
+
|
|
278
|
+
assert result.success is True
|
|
279
|
+
spy.build_launch_command.assert_not_called()
|
|
280
|
+
spy.launch.assert_not_called()
|
|
281
|
+
|
|
282
|
+
def test_non_codex_plan_handoff_keeps_nested_launch_guard(self, worktree: Path) -> None:
|
|
283
|
+
"""A plan handoff to another implementation tool cannot escape the guard."""
|
|
284
|
+
with _driven_start(
|
|
285
|
+
worktree,
|
|
286
|
+
network_access_config=True,
|
|
287
|
+
detected_env="CODEX_CLI",
|
|
288
|
+
resolved_tool="claude",
|
|
289
|
+
) as spy:
|
|
290
|
+
result = start(target="42", plan_handoff=True)
|
|
291
|
+
|
|
292
|
+
assert result.success is True
|
|
293
|
+
spy.build_launch_command.assert_not_called()
|
|
294
|
+
spy.launch.assert_not_called()
|
|
295
|
+
|
|
296
|
+
def test_other_ai_session_marker_keeps_nested_launch_guard(self, worktree: Path) -> None:
|
|
297
|
+
"""Only a Codex-originated handoff gets the fresh-context exception."""
|
|
298
|
+
with _driven_start(
|
|
299
|
+
worktree,
|
|
300
|
+
network_access_config=True,
|
|
301
|
+
detected_env="CLAUDE_CODE",
|
|
302
|
+
) as spy:
|
|
303
|
+
result = start(target="42", plan_handoff=True)
|
|
304
|
+
|
|
305
|
+
assert result.success is True
|
|
306
|
+
spy.build_launch_command.assert_not_called()
|
|
307
|
+
spy.launch.assert_not_called()
|
|
@@ -1170,7 +1170,7 @@ class TestOfferToImplement:
|
|
|
1170
1170
|
result = _offer_to_implement("42")
|
|
1171
1171
|
|
|
1172
1172
|
assert result is True
|
|
1173
|
-
mock_start.assert_called_once_with(target="42")
|
|
1173
|
+
mock_start.assert_called_once_with(target="42", plan_handoff=True)
|
|
1174
1174
|
|
|
1175
1175
|
def test_user_declines_returns_none(self) -> None:
|
|
1176
1176
|
"""Declining the prompt returns None without flushing or starting."""
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
from unittest.mock import Mock
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
@@ -272,6 +273,45 @@ def test_create_temp_script_content(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
272
273
|
assert "exec" in script
|
|
273
274
|
|
|
274
275
|
|
|
276
|
+
def test_readiness_script_reports_failed_when_child_exits_immediately(tmp_path: Path) -> None:
|
|
277
|
+
"""A broker success is rejected when its child cannot stay alive."""
|
|
278
|
+
from wade.utils.terminal import _create_readiness_script
|
|
279
|
+
|
|
280
|
+
readiness_file = tmp_path / "launch.ready"
|
|
281
|
+
readiness_file.touch()
|
|
282
|
+
launcher_script = _create_readiness_script(
|
|
283
|
+
["bash", "-c", "exit 1"],
|
|
284
|
+
str(tmp_path),
|
|
285
|
+
str(readiness_file),
|
|
286
|
+
)
|
|
287
|
+
try:
|
|
288
|
+
completed = subprocess.run([launcher_script], capture_output=True, check=False)
|
|
289
|
+
finally:
|
|
290
|
+
Path(launcher_script).unlink(missing_ok=True)
|
|
291
|
+
|
|
292
|
+
assert completed.returncode == 1
|
|
293
|
+
assert readiness_file.read_text() == "failed\n"
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_readiness_script_reports_ready_for_a_live_child(tmp_path: Path) -> None:
|
|
297
|
+
"""A child that survives the stability window confirms the handoff."""
|
|
298
|
+
from wade.utils.terminal import _create_readiness_script, _wait_for_launch_readiness
|
|
299
|
+
|
|
300
|
+
readiness_file = tmp_path / "launch.ready"
|
|
301
|
+
readiness_file.touch()
|
|
302
|
+
launcher_script = _create_readiness_script(
|
|
303
|
+
["bash", "-c", "sleep 1"],
|
|
304
|
+
str(tmp_path),
|
|
305
|
+
str(readiness_file),
|
|
306
|
+
)
|
|
307
|
+
process = subprocess.Popen([launcher_script])
|
|
308
|
+
try:
|
|
309
|
+
assert _wait_for_launch_readiness(str(readiness_file), timeout=2.0) is True
|
|
310
|
+
assert process.wait(timeout=2.0) == 0
|
|
311
|
+
finally:
|
|
312
|
+
Path(launcher_script).unlink(missing_ok=True)
|
|
313
|
+
|
|
314
|
+
|
|
275
315
|
# ---------------------------------------------------------------------------
|
|
276
316
|
# Batch launcher tests
|
|
277
317
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|