tcw-cli 2.8.2__tar.gz → 2.8.3__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.
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/PKG-INFO +1 -1
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/pyproject.toml +1 -1
- tcw_cli-2.8.3/tcw/__init__.py +1 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/jira.py +5 -1
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw_cli.egg-info/PKG-INFO +1 -1
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_client.py +63 -1
- tcw_cli-2.8.2/tcw/__init__.py +0 -1
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/LICENSE +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/README.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/setup.cfg +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/capabilities/__init__.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/capabilities/cli.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/cli.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/cli_suggest.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/harness.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/refs.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/serve/__init__.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/serve/dist/client/assets/index-C5_pngMt.css +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/serve/dist/client/assets/index-Dss8_ZG_.js +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/serve/dist/client/index.html +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/serve/dist/client/theme-init.js +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/serve/dist/server.cjs +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/serve/runtime.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/stdin.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/store/__init__.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/store/base.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/store/checkouts.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/store/config_edit.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/store/fs.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/store/project.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/store/yaml_source.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/taxonomy/__init__.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/taxonomy/cli.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/__init__.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/claim.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/create.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/intake.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/ownership.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/progress.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/tracker/sync.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/validate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/__init__.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/cli.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/generate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/hooks.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/audit-backlog.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/consolidate-plans.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/create-work.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/decompose.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/delegation.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/documentation-sync.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/post-mortem.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/search.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/triage-issues.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/procedures/unattended-work.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/projection.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/prompts/implement.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/prompts/inbox.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/prompts/plan.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/prompts/postmortem.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/prompts/request.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/prompts/spec.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/prompts/verify.md +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/recursion.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/resolve.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw/work/templates.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw_cli.egg-info/SOURCES.txt +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw_cli.egg-info/dependency_links.txt +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw_cli.egg-info/entry_points.txt +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw_cli.egg-info/requires.txt +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tcw_cli.egg-info/top_level.txt +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_already_integrated_check.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_blocker_cycles_through_unreadable_items.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_boardless_delegate_and_slices.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_body_prompt.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_capabilities.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_capabilities_federation.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_capabilities_reset.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_capabilities_rm.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_capabilities_sidecar.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_capability_gate_children.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_capability_ref_wording.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_check_versions.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_child_status.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_cli_help_coverage.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_command_suggestions.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_committed_paths.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_config_edit.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_config_edit_writers.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_configuration_text_home.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_creation_commits.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_cross_node_blocker_cycles.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_cross_node_blockers.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_cut_version.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_damaged_state_on_edit.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_detached_worktree_teardown.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_documentation_config.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_documentation_prompt.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_documentation_sync_wiring.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_documented_cli_surface.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_duplicate_slug_refusals.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_dynamic_skill_marker.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_edit_refusal.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_edit_type.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_environment_hardness.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_epic_completability_across_checkouts.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_epic_completable.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_eval_coverage.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_eval_files_changed.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_eval_fixture.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_eval_grading.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_eval_runner.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_external_work_store.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_falsification_rule.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_generate_hook.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_harness.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_inbox_title.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_init_configured_tree_path.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_initiative_node_qualified.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_interrupted_claim.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_item_tags_read.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_legacy_store_config.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_lifecycle_baseline.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_lifecycle_config_tags.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_lifecycle_hooks.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_lifecycle_inert.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_lifecycle_policy.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_lifecycle_validation.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_literal_store_names.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_local_qualified_blockers.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_multiproject.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_non_git_writes.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_override_in_linked_worktree.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_override_letter_case.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_override_references.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_planning_gates_after_start.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_plugin_manifests.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_procedure_config.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_procedure_verb.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_project_graph_paths.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_project_overrides.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_project_registry.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_projection.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_prompt_fallback.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_qualified_ref.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_recursion.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_refs.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_remote_session_setup.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_rename_work_item.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_repo_lifecycle.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_resolve.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_resolve_procedure.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_retention.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_routing_nodes.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_scaffold.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_serve.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_serve_descendants.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_serve_duplicate_slug.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_serve_projection.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_serve_resolve.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_serve_runtime.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_serve_write.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_session_bootstrap.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_shipped_procedures.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_shipped_prompts.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_show_json.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_sidecar_paths_in_validate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_skill_flow.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_skill_lifecycle_parity.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_skill_path_pointers.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_smoke.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_stage_validate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_stage_verb.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_stale_item_path.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_status_parity.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_stdin.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_stdin_cli.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_store_bounds.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_store_editor.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_store_lock.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_store_nodes.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_store_provisioning.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_store_publication.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_subprocess_stdin.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_taxonomy.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_taxonomy_rm_gaps.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tombstone.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_absent.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_binding.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_catch_up_moves.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_claim.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_claimability.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_cli.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_comment.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_config.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_create.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_gate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_hardening.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_help.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_hold.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_import.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_import_returns.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_inheritance.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_link.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_message_tidy.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_ownership.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_pre_backlog.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_replay.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_strict.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_strict_gate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_surface.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_sync.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_sync_config.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_tracker_validate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_transition_hints.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_unattended_work_skill.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_unplanned_start.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_unpushed_version_script.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_unreadable_capabilities_sidecar.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_unreadable_item_files.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_unreadable_state_gates.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_untag_invalid_tags.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_upstream_projects.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_validate.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_validate_moved_stores.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_validate_target.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_work.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_work_autocommit.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_work_review.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_work_start.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_work_tags.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_worktree_completion.py +0 -0
- {tcw_cli-2.8.2 → tcw_cli-2.8.3}/tests/test_worktree_sibling_nodes.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tcw-cli"
|
|
7
|
-
version = "2.8.
|
|
7
|
+
version = "2.8.3"
|
|
8
8
|
description = "Taxonomy · Capabilities · Work — a storage-abstracted framework for describing and evolving a software project."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.8.3"
|
|
@@ -149,7 +149,11 @@ class JiraClient:
|
|
|
149
149
|
request = urllib.request.Request(
|
|
150
150
|
f"{self.config.base_url}{path}", data=payload, method=method)
|
|
151
151
|
credentials = base64.b64encode(f"{email}:{token}".encode("utf-8")).decode("ascii")
|
|
152
|
-
|
|
152
|
+
# Unredirected: urllib copies every `add_header` header onto the request a
|
|
153
|
+
# redirect produces, whatever host it points at, which would hand the
|
|
154
|
+
# user's email and API token to that host. A redirected request now goes
|
|
155
|
+
# without them.
|
|
156
|
+
request.add_unredirected_header("Authorization", f"Basic {credentials}")
|
|
153
157
|
request.add_header("Accept", "application/json")
|
|
154
158
|
if payload is not None:
|
|
155
159
|
request.add_header("Content-Type", "application/json")
|
|
@@ -300,7 +300,9 @@ def test_the_authorization_header_is_built_from_the_named_variables(monkeypatch)
|
|
|
300
300
|
captured = {}
|
|
301
301
|
|
|
302
302
|
def fake_urlopen(request, timeout=None):
|
|
303
|
-
|
|
303
|
+
# `header_items()` rather than `headers`: the authorization header is
|
|
304
|
+
# an unredirected one, which `headers` does not include.
|
|
305
|
+
captured["headers"] = dict(request.header_items())
|
|
304
306
|
captured["timeout"] = timeout
|
|
305
307
|
captured["url"] = request.full_url
|
|
306
308
|
|
|
@@ -501,3 +503,63 @@ def test_a_server_that_drops_the_connection_is_unavailable_not_a_crash():
|
|
|
501
503
|
finally:
|
|
502
504
|
thread.join(timeout=5)
|
|
503
505
|
listener.close()
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def test_a_redirect_to_another_host_does_not_carry_the_credentials():
|
|
509
|
+
"""urllib copies every header added with `add_header` onto the request a
|
|
510
|
+
redirect produces, whatever host it points at. So the authorization header has
|
|
511
|
+
to be an unredirected one, or a redirect from the tracker (or anything between
|
|
512
|
+
it and us) to another host receives the user's email and API token. Two real
|
|
513
|
+
local servers: the first answers 302 to the second, under a different host
|
|
514
|
+
name and port, and the second records what it was sent."""
|
|
515
|
+
import http.server
|
|
516
|
+
import threading
|
|
517
|
+
|
|
518
|
+
seen = {}
|
|
519
|
+
|
|
520
|
+
def handler(name, answer):
|
|
521
|
+
class Handler(http.server.BaseHTTPRequestHandler):
|
|
522
|
+
def do_GET(self):
|
|
523
|
+
seen[name] = self.headers.get("Authorization")
|
|
524
|
+
answer(self)
|
|
525
|
+
|
|
526
|
+
def log_message(self, *args):
|
|
527
|
+
pass
|
|
528
|
+
return Handler
|
|
529
|
+
|
|
530
|
+
def ok(request_handler):
|
|
531
|
+
body = b"{}"
|
|
532
|
+
request_handler.send_response(200)
|
|
533
|
+
request_handler.send_header("Content-Type", "application/json")
|
|
534
|
+
request_handler.send_header("Content-Length", str(len(body)))
|
|
535
|
+
request_handler.end_headers()
|
|
536
|
+
request_handler.wfile.write(body)
|
|
537
|
+
|
|
538
|
+
other = http.server.HTTPServer(("127.0.0.1", 0), handler("other", ok))
|
|
539
|
+
other_port = other.server_address[1]
|
|
540
|
+
|
|
541
|
+
def redirect(request_handler):
|
|
542
|
+
request_handler.send_response(302)
|
|
543
|
+
request_handler.send_header(
|
|
544
|
+
"Location", f"http://localhost:{other_port}/rest/api/3/myself")
|
|
545
|
+
request_handler.send_header("Content-Length", "0")
|
|
546
|
+
request_handler.end_headers()
|
|
547
|
+
|
|
548
|
+
tracker = http.server.HTTPServer(("127.0.0.1", 0), handler("tracker", redirect))
|
|
549
|
+
servers = (tracker, other)
|
|
550
|
+
threads = [threading.Thread(target=s.serve_forever, daemon=True) for s in servers]
|
|
551
|
+
for thread in threads:
|
|
552
|
+
thread.start()
|
|
553
|
+
try:
|
|
554
|
+
config = dataclasses.replace(
|
|
555
|
+
CONFIG, base_url=f"http://127.0.0.1:{tracker.server_address[1]}",
|
|
556
|
+
timeout_seconds=5)
|
|
557
|
+
jira.JiraClient(config).myself()
|
|
558
|
+
finally:
|
|
559
|
+
for server in servers:
|
|
560
|
+
server.shutdown()
|
|
561
|
+
server.server_close()
|
|
562
|
+
|
|
563
|
+
assert seen["tracker"] is not None and seen["tracker"].startswith("Basic ")
|
|
564
|
+
assert "other" in seen, "the redirect was never followed"
|
|
565
|
+
assert seen["other"] is None
|
tcw_cli-2.8.2/tcw/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.8.2"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|