mcp-ticketer 0.1.15__tar.gz → 0.1.16__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.
Potentially problematic release.
This version of mcp-ticketer might be problematic. Click here for more details.
- mcp_ticketer-0.1.16/CONFIG_RESOLUTION_FIX.md +223 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/PKG-INFO +1 -1
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/pyproject.toml +1 -1
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/__version__.py +1 -1
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/main.py +36 -4
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/utils.py +27 -3
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer.egg-info/PKG-INFO +1 -1
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer.egg-info/SOURCES.txt +3 -0
- mcp_ticketer-0.1.16/test_config_resolution.py +187 -0
- mcp_ticketer-0.1.16/test_serve_config.py +153 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/.dependency_cache +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/.mpm_deployment_state +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/agent-manager.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/agentic-coder-optimizer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/api_qa.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/clerk-ops.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/code_analyzer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/content-agent.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/dart_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/data_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/documentation.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/gcp_ops_agent.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/golang_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/imagemagick.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/java_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/local_ops_agent.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/memory_manager.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/nextjs_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/ops.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/php-engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/product_owner.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/project_organizer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/prompt-engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/python_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/qa.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/react_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/refactoring_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/research.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/ruby-engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/rust_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/security.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/ticketing.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/typescript_engineer.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/vercel_ops_agent.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/version_control.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/web_qa.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude/agents/web_ui.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/config/project.json +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/mcp_auto_config_preference.json +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/README.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/agentic_coder_optimizer_memories.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/documentation_memories.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/engineer_memories.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/ops_memories.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/project_knowledge.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/qa_memories.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/research_memories.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/version-control_memories.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude-mpm/memories/workflows.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.claude.json +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.coveragerc +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.env.example +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.github/workflows/docs.yml +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.github/workflows/publish.yml +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.github/workflows/test.yml +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.mcp/config.json +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.pre-commit-config.yaml +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.python-version +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/.readthedocs.yaml +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/CHANGELOG.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/CLAUDE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/CLAUDE_DESKTOP_SETUP.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/CODE_STRUCTURE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/CONTRIBUTING.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/ENV_DISCOVERY_COMPLETE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/HIERARCHY_IMPLEMENTATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/IMPLEMENTATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/JIRA_SETUP.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/LICENSE +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/LINEAR_SETUP.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/MANIFEST.in +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/Makefile +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/OPTIMIZATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/PROJECT_INITIALIZATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/QUEUE_SYSTEM.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/QUICK_START.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/README.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/RELEASE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/RELEASING.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/TEST_COVERAGE_REPORT.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/TEST_REPORT.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/TEST_RESULTS_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/claude-desktop-config.json +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/debug_search.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/debug_test.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/ADAPTERS.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/API_REFERENCE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/CONFIGURATION.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/CONFIG_RESOLUTION_FLOW.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/DEVELOPER_GUIDE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/ENV_DISCOVERY.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/MCP_INTEGRATION.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/MIGRATION_GUIDE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/PROJECT_CONFIG.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/PR_INTEGRATION.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/QUICK_START_ENV.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/USER_GUIDE.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_archive/README.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/ADAPTERS.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/API_REFERENCE.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/CONFIGURATION.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/DEVELOPER_GUIDE.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/MCP_INTEGRATION.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/MIGRATION_GUIDE.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/USER_GUIDE.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/adapters/github.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/adapters.rst.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/api.rst.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/cli.rst.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/development.rst.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/examples.rst.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/index.rst.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/installation.rst.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/_build/_sources/prd/mcp-ticketer-prd.md.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/adapters/github.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/adapters.rst +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/api.rst +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/cli.rst +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/conf.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/development.rst +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/examples.rst +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/index.rst +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/installation.rst +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/prd/mcp-ticketer-prd.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/docs/requirements.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/install.sh +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/mcp-ticketer +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/mcp-ticketer-server +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/mcp_server.sh +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/pytest.ini +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/requirements-dev.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/requirements.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/scripts/README.md +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/scripts/manage_version.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/setup.cfg +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/setup.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/adapters/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/adapters/aitrackdown.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/adapters/github.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/adapters/hybrid.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/adapters/jira.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/adapters/linear.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cache/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cache/memory.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/configure.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/discover.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/mcp_configure.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/migrate_config.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/cli/queue_commands.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/adapter.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/config.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/env_discovery.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/http_client.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/mappers.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/models.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/project_config.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/core/registry.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/mcp/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/mcp/server.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/py.typed +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/queue/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/queue/__main__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/queue/manager.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/queue/queue.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/queue/run_worker.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer/queue/worker.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer.egg-info/dependency_links.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer.egg-info/entry_points.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer.egg-info/not-zip-safe +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer.egg-info/requires.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/src/mcp_ticketer.egg-info/top_level.txt +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test-tickets/tickets/task-20250924002724.json +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_all_adapters.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_api_usage.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_basic.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_comprehensive.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_error_handling.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_github.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_github_token.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_jira.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_linear.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_linear_native.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_linear_teams.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_mcp_server_qa.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_optimizations.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_performance.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_pr_functionality.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_queue_system.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/test_set_command.sh +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tests/adapters/__init__.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tests/adapters/test_aitrackdown.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tests/conftest.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tests/core/test_env_discovery.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tests/test_base_adapter.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tests/test_models.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tests/test_queue.py +0 -0
- {mcp_ticketer-0.1.15 → mcp_ticketer-0.1.16}/tox.ini +0 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Configuration Resolution Fix for `serve` Command
|
|
2
|
+
|
|
3
|
+
## Problem Summary
|
|
4
|
+
|
|
5
|
+
The `serve` command was not respecting project-specific configuration. It always read from the global config (`~/.mcp-ticketer/config.json`) instead of checking for project-specific config (`.mcp-ticketer/config.json` in the current working directory) first.
|
|
6
|
+
|
|
7
|
+
### Impact
|
|
8
|
+
|
|
9
|
+
When the MCP server starts via Claude Code/Desktop:
|
|
10
|
+
- The server's working directory is set based on the `cwd` field in `.mcp/config.json`
|
|
11
|
+
- Despite running in the project directory, the server would use global config
|
|
12
|
+
- This meant project-specific adapter configurations were ignored
|
|
13
|
+
|
|
14
|
+
## Solution
|
|
15
|
+
|
|
16
|
+
Updated the `load_config()` function to follow the correct configuration resolution order:
|
|
17
|
+
|
|
18
|
+
1. **Project-specific config** (`.mcp-ticketer/config.json` in current working directory)
|
|
19
|
+
2. **Global config** (`~/.mcp-ticketer/config.json`)
|
|
20
|
+
3. **Default fallback** (aitrackdown adapter with `.aitrackdown` base path)
|
|
21
|
+
|
|
22
|
+
## Files Modified
|
|
23
|
+
|
|
24
|
+
### 1. `src/mcp_ticketer/cli/main.py`
|
|
25
|
+
|
|
26
|
+
**Before:**
|
|
27
|
+
```python
|
|
28
|
+
def load_config() -> dict:
|
|
29
|
+
"""Load configuration from file."""
|
|
30
|
+
if CONFIG_FILE.exists():
|
|
31
|
+
with open(CONFIG_FILE, "r") as f:
|
|
32
|
+
return json.load(f)
|
|
33
|
+
return {"adapter": "aitrackdown", "config": {"base_path": ".aitrackdown"}}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**After:**
|
|
37
|
+
```python
|
|
38
|
+
def load_config() -> dict:
|
|
39
|
+
"""Load configuration from file.
|
|
40
|
+
|
|
41
|
+
Resolution order:
|
|
42
|
+
1. Project-specific config (.mcp-ticketer/config.json in cwd)
|
|
43
|
+
2. Global config (~/.mcp-ticketer/config.json)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Configuration dictionary
|
|
47
|
+
"""
|
|
48
|
+
# Check project-specific config first
|
|
49
|
+
project_config = Path.cwd() / ".mcp-ticketer" / "config.json"
|
|
50
|
+
if project_config.exists():
|
|
51
|
+
try:
|
|
52
|
+
with open(project_config, "r") as f:
|
|
53
|
+
return json.load(f)
|
|
54
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
55
|
+
console.print(f"[yellow]Warning: Could not load project config: {e}[/yellow]")
|
|
56
|
+
# Fall through to global config
|
|
57
|
+
|
|
58
|
+
# Fall back to global config
|
|
59
|
+
if CONFIG_FILE.exists():
|
|
60
|
+
try:
|
|
61
|
+
with open(CONFIG_FILE, "r") as f:
|
|
62
|
+
return json.load(f)
|
|
63
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
64
|
+
console.print(f"[yellow]Warning: Could not load global config: {e}[/yellow]")
|
|
65
|
+
|
|
66
|
+
# Default fallback
|
|
67
|
+
return {"adapter": "aitrackdown", "config": {"base_path": ".aitrackdown"}}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. `src/mcp_ticketer/cli/utils.py`
|
|
71
|
+
|
|
72
|
+
Updated `CommonPatterns.load_config()` with identical changes to maintain consistency across the codebase.
|
|
73
|
+
|
|
74
|
+
### 3. `src/mcp_ticketer/cli/main.py` - `serve` command documentation
|
|
75
|
+
|
|
76
|
+
Added comprehensive documentation to the `serve` command explaining the configuration resolution process:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
"""Start MCP server for JSON-RPC communication over stdio.
|
|
80
|
+
|
|
81
|
+
This command is used by Claude Code/Desktop when connecting to the MCP server.
|
|
82
|
+
You typically don't need to run this manually - use 'mcp-ticketer mcp' to configure.
|
|
83
|
+
|
|
84
|
+
Configuration Resolution:
|
|
85
|
+
- When MCP server starts, it uses the current working directory (cwd)
|
|
86
|
+
- The cwd is set by Claude Code/Desktop from the 'cwd' field in .mcp/config.json
|
|
87
|
+
- Configuration is loaded with this priority:
|
|
88
|
+
1. Project-specific: .mcp-ticketer/config.json in cwd
|
|
89
|
+
2. Global: ~/.mcp-ticketer/config.json
|
|
90
|
+
3. Default: aitrackdown adapter with .aitrackdown base path
|
|
91
|
+
"""
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Backward Compatibility
|
|
95
|
+
|
|
96
|
+
✅ **Fully backward compatible**
|
|
97
|
+
|
|
98
|
+
- Existing global configs continue to work
|
|
99
|
+
- Existing commands that use `load_config()` work unchanged
|
|
100
|
+
- New behavior only activates when project-specific config exists
|
|
101
|
+
- Graceful error handling with fallback to next priority level
|
|
102
|
+
|
|
103
|
+
## Testing
|
|
104
|
+
|
|
105
|
+
Created comprehensive test suite to verify the fix:
|
|
106
|
+
|
|
107
|
+
### Test 1: Project-specific config takes precedence
|
|
108
|
+
- Creates both project and global configs
|
|
109
|
+
- Verifies project config is loaded when both exist
|
|
110
|
+
|
|
111
|
+
### Test 2: Global config fallback
|
|
112
|
+
- Creates only global config
|
|
113
|
+
- Verifies global config is used when project config doesn't exist
|
|
114
|
+
|
|
115
|
+
### Test 3: Default fallback
|
|
116
|
+
- Removes all configs
|
|
117
|
+
- Verifies default config is used
|
|
118
|
+
|
|
119
|
+
### Test 4: MCP server cwd scenario
|
|
120
|
+
- Simulates real-world MCP server startup
|
|
121
|
+
- Verifies correct config is loaded based on working directory
|
|
122
|
+
|
|
123
|
+
**All tests pass:** ✓
|
|
124
|
+
|
|
125
|
+
## Usage Examples
|
|
126
|
+
|
|
127
|
+
### Scenario 1: Project with specific config
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# In project directory
|
|
131
|
+
$ ls -la .mcp-ticketer/
|
|
132
|
+
total 8
|
|
133
|
+
drwxr-xr-x 3 user staff 96 Oct 22 12:00 .
|
|
134
|
+
drwxr-xr-x 8 user staff 256 Oct 22 12:00 ..
|
|
135
|
+
-rw-r--r-- 1 user staff 150 Oct 22 12:00 config.json
|
|
136
|
+
|
|
137
|
+
# When MCP server starts in this directory, it will use .mcp-ticketer/config.json
|
|
138
|
+
$ mcp-ticketer serve
|
|
139
|
+
Starting MCP server with linear adapter
|
|
140
|
+
# Uses project-specific Linear configuration
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Scenario 2: No project config, using global
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# In project without .mcp-ticketer/config.json
|
|
147
|
+
$ ls -la .mcp-ticketer/
|
|
148
|
+
ls: .mcp-ticketer/: No such file or directory
|
|
149
|
+
|
|
150
|
+
# Falls back to ~/.mcp-ticketer/config.json
|
|
151
|
+
$ mcp-ticketer serve
|
|
152
|
+
Starting MCP server with github adapter
|
|
153
|
+
# Uses global GitHub configuration
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Scenario 3: MCP server via Claude Code
|
|
157
|
+
|
|
158
|
+
When Claude Code/Desktop starts the MCP server:
|
|
159
|
+
|
|
160
|
+
1. Reads `.mcp/config.json` in your project
|
|
161
|
+
2. Finds `"cwd": "/path/to/your/project"`
|
|
162
|
+
3. Starts MCP server with that working directory
|
|
163
|
+
4. Server's `load_config()` checks `/path/to/your/project/.mcp-ticketer/config.json` first
|
|
164
|
+
5. Uses project-specific config if it exists, otherwise falls back to global
|
|
165
|
+
|
|
166
|
+
## Implementation Details
|
|
167
|
+
|
|
168
|
+
### Error Handling
|
|
169
|
+
|
|
170
|
+
The implementation includes robust error handling:
|
|
171
|
+
|
|
172
|
+
- **JSON decode errors**: Catches malformed JSON files and falls back to next priority level
|
|
173
|
+
- **IO errors**: Handles file permission issues gracefully
|
|
174
|
+
- **Warning messages**: Prints user-friendly warnings when config loading fails
|
|
175
|
+
- **Graceful degradation**: Always provides a working default config
|
|
176
|
+
|
|
177
|
+
### Path Resolution
|
|
178
|
+
|
|
179
|
+
- Uses `Path.cwd()` to get current working directory (respects MCP server's cwd)
|
|
180
|
+
- Uses `Path.home()` for global config location
|
|
181
|
+
- All path operations use `pathlib.Path` for cross-platform compatibility
|
|
182
|
+
|
|
183
|
+
### Performance
|
|
184
|
+
|
|
185
|
+
- No performance impact: Same number of file system calls as before
|
|
186
|
+
- Early return when project config exists (most common case)
|
|
187
|
+
- No caching issues: Always reads fresh config on server start
|
|
188
|
+
|
|
189
|
+
## Verification
|
|
190
|
+
|
|
191
|
+
To verify the fix is working:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Run the test suite
|
|
195
|
+
./venv/bin/python test_config_resolution.py
|
|
196
|
+
./venv/bin/python test_serve_config.py
|
|
197
|
+
|
|
198
|
+
# Manual verification
|
|
199
|
+
# 1. Create project config
|
|
200
|
+
mkdir -p .mcp-ticketer
|
|
201
|
+
echo '{"default_adapter": "aitrackdown", "adapters": {"aitrackdown": {"base_path": ".aitrackdown-test"}}}' > .mcp-ticketer/config.json
|
|
202
|
+
|
|
203
|
+
# 2. Start serve command
|
|
204
|
+
mcp-ticketer serve
|
|
205
|
+
# Should use .aitrackdown-test base path, not global config
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Related Issues
|
|
209
|
+
|
|
210
|
+
This fix resolves:
|
|
211
|
+
- MCP server ignoring project-specific configuration
|
|
212
|
+
- Configuration precedence not documented
|
|
213
|
+
- Unexpected behavior when running in project directories
|
|
214
|
+
- Need for better configuration isolation between projects
|
|
215
|
+
|
|
216
|
+
## Future Improvements
|
|
217
|
+
|
|
218
|
+
Potential enhancements (not part of this fix):
|
|
219
|
+
|
|
220
|
+
- Add `--config` flag to explicitly specify config file path
|
|
221
|
+
- Add verbose logging to show which config file was loaded
|
|
222
|
+
- Create config file watcher for hot-reloading during development
|
|
223
|
+
- Add config validation and helpful error messages for common mistakes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-ticketer
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: Universal ticket management interface for AI agents with MCP support
|
|
5
5
|
Author-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
6
6
|
Maintainer-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
@@ -71,10 +71,34 @@ class AdapterType(str, Enum):
|
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
def load_config() -> dict:
|
|
74
|
-
"""Load configuration from file.
|
|
74
|
+
"""Load configuration from file.
|
|
75
|
+
|
|
76
|
+
Resolution order:
|
|
77
|
+
1. Project-specific config (.mcp-ticketer/config.json in cwd)
|
|
78
|
+
2. Global config (~/.mcp-ticketer/config.json)
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Configuration dictionary
|
|
82
|
+
"""
|
|
83
|
+
# Check project-specific config first
|
|
84
|
+
project_config = Path.cwd() / ".mcp-ticketer" / "config.json"
|
|
85
|
+
if project_config.exists():
|
|
86
|
+
try:
|
|
87
|
+
with open(project_config, "r") as f:
|
|
88
|
+
return json.load(f)
|
|
89
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
90
|
+
console.print(f"[yellow]Warning: Could not load project config: {e}[/yellow]")
|
|
91
|
+
# Fall through to global config
|
|
92
|
+
|
|
93
|
+
# Fall back to global config
|
|
75
94
|
if CONFIG_FILE.exists():
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
try:
|
|
96
|
+
with open(CONFIG_FILE, "r") as f:
|
|
97
|
+
return json.load(f)
|
|
98
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
99
|
+
console.print(f"[yellow]Warning: Could not load global config: {e}[/yellow]")
|
|
100
|
+
|
|
101
|
+
# Default fallback
|
|
78
102
|
return {"adapter": "aitrackdown", "config": {"base_path": ".aitrackdown"}}
|
|
79
103
|
|
|
80
104
|
|
|
@@ -1152,10 +1176,18 @@ def serve(
|
|
|
1152
1176
|
|
|
1153
1177
|
This command is used by Claude Code/Desktop when connecting to the MCP server.
|
|
1154
1178
|
You typically don't need to run this manually - use 'mcp-ticketer mcp' to configure.
|
|
1179
|
+
|
|
1180
|
+
Configuration Resolution:
|
|
1181
|
+
- When MCP server starts, it uses the current working directory (cwd)
|
|
1182
|
+
- The cwd is set by Claude Code/Desktop from the 'cwd' field in .mcp/config.json
|
|
1183
|
+
- Configuration is loaded with this priority:
|
|
1184
|
+
1. Project-specific: .mcp-ticketer/config.json in cwd
|
|
1185
|
+
2. Global: ~/.mcp-ticketer/config.json
|
|
1186
|
+
3. Default: aitrackdown adapter with .aitrackdown base path
|
|
1155
1187
|
"""
|
|
1156
1188
|
from ..mcp.server import MCPTicketServer
|
|
1157
1189
|
|
|
1158
|
-
# Load configuration
|
|
1190
|
+
# Load configuration (respects project-specific config in cwd)
|
|
1159
1191
|
config = load_config()
|
|
1160
1192
|
|
|
1161
1193
|
# Determine adapter type
|
|
@@ -31,10 +31,34 @@ class CommonPatterns:
|
|
|
31
31
|
|
|
32
32
|
@staticmethod
|
|
33
33
|
def load_config() -> dict:
|
|
34
|
-
"""Load configuration from file.
|
|
34
|
+
"""Load configuration from file.
|
|
35
|
+
|
|
36
|
+
Resolution order:
|
|
37
|
+
1. Project-specific config (.mcp-ticketer/config.json in cwd)
|
|
38
|
+
2. Global config (~/.mcp-ticketer/config.json)
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Configuration dictionary
|
|
42
|
+
"""
|
|
43
|
+
# Check project-specific config first
|
|
44
|
+
project_config = Path.cwd() / ".mcp-ticketer" / "config.json"
|
|
45
|
+
if project_config.exists():
|
|
46
|
+
try:
|
|
47
|
+
with open(project_config, "r") as f:
|
|
48
|
+
return json.load(f)
|
|
49
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
50
|
+
console.print(f"[yellow]Warning: Could not load project config: {e}[/yellow]")
|
|
51
|
+
# Fall through to global config
|
|
52
|
+
|
|
53
|
+
# Fall back to global config
|
|
35
54
|
if CommonPatterns.CONFIG_FILE.exists():
|
|
36
|
-
|
|
37
|
-
|
|
55
|
+
try:
|
|
56
|
+
with open(CommonPatterns.CONFIG_FILE, "r") as f:
|
|
57
|
+
return json.load(f)
|
|
58
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
59
|
+
console.print(f"[yellow]Warning: Could not load global config: {e}[/yellow]")
|
|
60
|
+
|
|
61
|
+
# Default fallback
|
|
38
62
|
return {"adapter": "aitrackdown", "config": {"base_path": ".aitrackdown"}}
|
|
39
63
|
|
|
40
64
|
@staticmethod
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-ticketer
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: Universal ticket management interface for AI agents with MCP support
|
|
5
5
|
Author-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
6
6
|
Maintainer-email: MCP Ticketer Team <support@mcp-ticketer.io>
|
|
@@ -8,6 +8,7 @@ CHANGELOG.md
|
|
|
8
8
|
CLAUDE.md
|
|
9
9
|
CLAUDE_DESKTOP_SETUP.md
|
|
10
10
|
CODE_STRUCTURE.md
|
|
11
|
+
CONFIG_RESOLUTION_FIX.md
|
|
11
12
|
CONTRIBUTING.md
|
|
12
13
|
ENV_DISCOVERY_COMPLETE.md
|
|
13
14
|
HIERARCHY_IMPLEMENTATION_SUMMARY.md
|
|
@@ -43,6 +44,7 @@ test_all_adapters.py
|
|
|
43
44
|
test_api_usage.py
|
|
44
45
|
test_basic.py
|
|
45
46
|
test_comprehensive.py
|
|
47
|
+
test_config_resolution.py
|
|
46
48
|
test_error_handling.py
|
|
47
49
|
test_github.py
|
|
48
50
|
test_github_token.py
|
|
@@ -55,6 +57,7 @@ test_optimizations.py
|
|
|
55
57
|
test_performance.py
|
|
56
58
|
test_pr_functionality.py
|
|
57
59
|
test_queue_system.py
|
|
60
|
+
test_serve_config.py
|
|
58
61
|
test_set_command.sh
|
|
59
62
|
tox.ini
|
|
60
63
|
.claude-mpm/mcp_auto_config_preference.json
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Test configuration resolution order for project-specific vs global config."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from unittest import mock
|
|
8
|
+
|
|
9
|
+
# Import the function we're testing
|
|
10
|
+
from src.mcp_ticketer.cli.main import load_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_project_specific_config_takes_precedence():
|
|
14
|
+
"""Test that project-specific config is loaded when it exists."""
|
|
15
|
+
# Create a temporary directory structure
|
|
16
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
17
|
+
tmpdir_path = Path(tmpdir)
|
|
18
|
+
|
|
19
|
+
# Create project-specific config
|
|
20
|
+
project_config_dir = tmpdir_path / ".mcp-ticketer"
|
|
21
|
+
project_config_dir.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
project_config_file = project_config_dir / "config.json"
|
|
23
|
+
|
|
24
|
+
project_config_data = {
|
|
25
|
+
"default_adapter": "linear",
|
|
26
|
+
"adapters": {
|
|
27
|
+
"linear": {
|
|
28
|
+
"team_key": "PROJECT-SPECIFIC"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
with open(project_config_file, "w") as f:
|
|
34
|
+
json.dump(project_config_data, f)
|
|
35
|
+
|
|
36
|
+
# Create global config
|
|
37
|
+
global_config_dir = Path.home() / ".mcp-ticketer"
|
|
38
|
+
global_config_dir.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
global_config_file = global_config_dir / "config.json"
|
|
40
|
+
|
|
41
|
+
global_config_data = {
|
|
42
|
+
"default_adapter": "github",
|
|
43
|
+
"adapters": {
|
|
44
|
+
"github": {
|
|
45
|
+
"owner": "GLOBAL-CONFIG"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Backup existing global config if it exists
|
|
51
|
+
backup_config = None
|
|
52
|
+
if global_config_file.exists():
|
|
53
|
+
with open(global_config_file, "r") as f:
|
|
54
|
+
backup_config = f.read()
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
# Write temporary global config
|
|
58
|
+
with open(global_config_file, "w") as f:
|
|
59
|
+
json.dump(global_config_data, f)
|
|
60
|
+
|
|
61
|
+
# Mock Path.cwd() to return our temp directory
|
|
62
|
+
with mock.patch('pathlib.Path.cwd', return_value=tmpdir_path):
|
|
63
|
+
# Load config - should prefer project-specific
|
|
64
|
+
config = load_config()
|
|
65
|
+
|
|
66
|
+
# Verify project-specific config was loaded
|
|
67
|
+
assert config["default_adapter"] == "linear", \
|
|
68
|
+
f"Expected 'linear', got '{config['default_adapter']}'"
|
|
69
|
+
assert "linear" in config["adapters"], \
|
|
70
|
+
"Expected 'linear' adapter in config"
|
|
71
|
+
assert config["adapters"]["linear"]["team_key"] == "PROJECT-SPECIFIC", \
|
|
72
|
+
"Expected project-specific config values"
|
|
73
|
+
|
|
74
|
+
print("✓ Test passed: Project-specific config takes precedence")
|
|
75
|
+
|
|
76
|
+
finally:
|
|
77
|
+
# Restore original global config
|
|
78
|
+
if backup_config is not None:
|
|
79
|
+
with open(global_config_file, "w") as f:
|
|
80
|
+
f.write(backup_config)
|
|
81
|
+
elif global_config_file.exists():
|
|
82
|
+
global_config_file.unlink()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_global_config_fallback():
|
|
86
|
+
"""Test that global config is used when project-specific doesn't exist."""
|
|
87
|
+
# Create a temporary directory without project config
|
|
88
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
89
|
+
tmpdir_path = Path(tmpdir)
|
|
90
|
+
|
|
91
|
+
# Create global config
|
|
92
|
+
global_config_dir = Path.home() / ".mcp-ticketer"
|
|
93
|
+
global_config_dir.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
global_config_file = global_config_dir / "config.json"
|
|
95
|
+
|
|
96
|
+
global_config_data = {
|
|
97
|
+
"default_adapter": "github",
|
|
98
|
+
"adapters": {
|
|
99
|
+
"github": {
|
|
100
|
+
"owner": "GLOBAL-FALLBACK"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Backup existing global config if it exists
|
|
106
|
+
backup_config = None
|
|
107
|
+
if global_config_file.exists():
|
|
108
|
+
with open(global_config_file, "r") as f:
|
|
109
|
+
backup_config = f.read()
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
# Write temporary global config
|
|
113
|
+
with open(global_config_file, "w") as f:
|
|
114
|
+
json.dump(global_config_data, f)
|
|
115
|
+
|
|
116
|
+
# Mock Path.cwd() to return temp directory (no project config)
|
|
117
|
+
with mock.patch('pathlib.Path.cwd', return_value=tmpdir_path):
|
|
118
|
+
# Load config - should use global
|
|
119
|
+
config = load_config()
|
|
120
|
+
|
|
121
|
+
# Verify global config was loaded
|
|
122
|
+
assert config["default_adapter"] == "github", \
|
|
123
|
+
f"Expected 'github', got '{config['default_adapter']}'"
|
|
124
|
+
assert "github" in config["adapters"], \
|
|
125
|
+
"Expected 'github' adapter in config"
|
|
126
|
+
assert config["adapters"]["github"]["owner"] == "GLOBAL-FALLBACK", \
|
|
127
|
+
"Expected global config values"
|
|
128
|
+
|
|
129
|
+
print("✓ Test passed: Global config used when project config missing")
|
|
130
|
+
|
|
131
|
+
finally:
|
|
132
|
+
# Restore original global config
|
|
133
|
+
if backup_config is not None:
|
|
134
|
+
with open(global_config_file, "w") as f:
|
|
135
|
+
f.write(backup_config)
|
|
136
|
+
elif global_config_file.exists():
|
|
137
|
+
global_config_file.unlink()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_default_fallback():
|
|
141
|
+
"""Test that default config is used when neither project nor global exists."""
|
|
142
|
+
# Create a temporary directory without any config
|
|
143
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
144
|
+
tmpdir_path = Path(tmpdir)
|
|
145
|
+
|
|
146
|
+
# Backup and remove global config if it exists
|
|
147
|
+
global_config_file = Path.home() / ".mcp-ticketer" / "config.json"
|
|
148
|
+
backup_config = None
|
|
149
|
+
if global_config_file.exists():
|
|
150
|
+
with open(global_config_file, "r") as f:
|
|
151
|
+
backup_config = f.read()
|
|
152
|
+
global_config_file.unlink()
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
# Mock Path.cwd() to return temp directory
|
|
156
|
+
with mock.patch('pathlib.Path.cwd', return_value=tmpdir_path):
|
|
157
|
+
# Load config - should use defaults
|
|
158
|
+
config = load_config()
|
|
159
|
+
|
|
160
|
+
# Verify default config was loaded
|
|
161
|
+
assert config["adapter"] == "aitrackdown", \
|
|
162
|
+
f"Expected 'aitrackdown', got '{config.get('adapter')}'"
|
|
163
|
+
assert "config" in config, \
|
|
164
|
+
"Expected default config structure"
|
|
165
|
+
assert config["config"]["base_path"] == ".aitrackdown", \
|
|
166
|
+
"Expected default base_path"
|
|
167
|
+
|
|
168
|
+
print("✓ Test passed: Default config used when no configs exist")
|
|
169
|
+
|
|
170
|
+
finally:
|
|
171
|
+
# Restore original global config
|
|
172
|
+
if backup_config is not None:
|
|
173
|
+
global_config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
174
|
+
with open(global_config_file, "w") as f:
|
|
175
|
+
f.write(backup_config)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if __name__ == "__main__":
|
|
179
|
+
print("Testing configuration resolution order...")
|
|
180
|
+
print()
|
|
181
|
+
|
|
182
|
+
test_project_specific_config_takes_precedence()
|
|
183
|
+
test_global_config_fallback()
|
|
184
|
+
test_default_fallback()
|
|
185
|
+
|
|
186
|
+
print()
|
|
187
|
+
print("All tests passed! ✓")
|