querysource 4.1.9__tar.gz → 4.1.11__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.
- querysource-4.1.11/.claude/commands/pr-review.md +480 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-brainstorm.md +4 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-done.md +118 -5
- querysource-4.1.11/.claude/commands/sdd-fromjira.md +335 -0
- querysource-4.1.11/.claude/commands/sdd-tojira.md +423 -0
- querysource-4.1.11/CHANGES.rst +54 -0
- {querysource-4.1.9 → querysource-4.1.11}/Makefile +9 -3
- {querysource-4.1.9 → querysource-4.1.11}/PKG-INFO +3 -3
- {querysource-4.1.9 → querysource-4.1.11}/pyproject.toml +11 -2
- {querysource-4.1.9 → querysource-4.1.11}/querysource/_version.py +3 -3
- querysource-4.1.11/querysource/handlers/_pagination.py +413 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/manager.py +145 -28
- {querysource-4.1.9 → querysource-4.1.11}/querysource/version.py +1 -1
- {querysource-4.1.9 → querysource-4.1.11}/querysource.egg-info/PKG-INFO +3 -3
- {querysource-4.1.9 → querysource-4.1.11}/querysource.egg-info/SOURCES.txt +8 -1
- {querysource-4.1.9 → querysource-4.1.11}/querysource.egg-info/requires.txt +2 -2
- querysource-4.1.11/tests/handlers/__init__.py +0 -0
- querysource-4.1.11/tests/handlers/conftest.py +173 -0
- querysource-4.1.11/tests/handlers/test_querymanager_pagination.py +560 -0
- querysource-4.1.9/CHANGES.rst +0 -16
- {querysource-4.1.9 → querysource-4.1.11}/.bumpversion.cfg +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/agents/code-reviewer.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/agents/sdd-worker.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-codereview.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-next.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-proposal.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-spec.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-start.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-status.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/commands/sdd-task.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/aws-cost-optimization.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/code-reviewer.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/cython-development.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/python-development.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/rust-development.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/using-git-worktrees.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/worktree-pr-and-clean.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/worktree-start-feature.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.claude/rules/worktree-status.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.github/dependabot.yml +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.github/workflows/codeql-analysis.yml +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.github/workflows/release.yml +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.isort.cfg +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.jupyter/jupyter_notebook_config.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/.pylintrc +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/CODE_OF_CONDUCT.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/CONTRIBUTING.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/INSTALL +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/LICENSE +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/MANIFEST.in +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/README.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/app.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/bin/README.md +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/gunicorn_config.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/mypy.ini +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/nav.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/pytest.ini +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/__cli__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/cache/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/cache/backends/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/cache/backends/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/cache/backends/memcache.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/cache/backends/redis.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/cache/base.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/conf.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/connections.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/arangodb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/athena.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/bigquery.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/cassandra.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/clickhouse.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/cockroachdb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/couchbase.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/couchdb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/countries.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/delta.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/documentdb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/dynamodb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/elastic.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/ga.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/gcalc.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/hazel.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/iceberg.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/influx.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/jdbc.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/jira.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/mariadb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/memcached.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/mongo.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/mysql.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/odbc.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/openweather.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/oracle.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/pg.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/postgres.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/py.typed +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/qs.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/redis.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/rest.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/rethink.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/sa.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/salesforce.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/scylladb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/sqlalchemy.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/sqlite.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/sqlserver.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/upc.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/drivers/zipcodeapi.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/handlers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/handlers/datasource.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/handlers/utils.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/datasources/models.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/events/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/exceptions.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/executor.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/log.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/multi.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/outputs/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/outputs/tableOutput/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/outputs/tableOutput/postgres.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/outputs/tableOutput/table.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/service.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/handlers/variables.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/connections.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/credentials.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/databases/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/databases/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/databases/bigquery.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/databases/db.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/databases/mongo.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/databases/rethink.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/http.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/playwright_service.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/queries.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/interfaces/selenium_service.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/libs/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/libs/encoders.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/libs/functions/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/libs/py.typed +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/models.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/arrow.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/dt.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/factory.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/iter.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/modin.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/pandas.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/dt/polars.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/output.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/bigquery.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/documentdb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/mongodb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/mysql.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/postgres.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/rethink.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/sa.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/TableOutput/table.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/tables/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/bokeh.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/clustering.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/csv.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/describe.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/eda.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/excel.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/html.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/json.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/pdf.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/pickle.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/plotly.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/profiling.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/report.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/table.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/tsv.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/txt.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/outputs/writers/xml.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/abstract.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/abstract.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/abstract.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/arangodb.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/arangodb.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/arangodb.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/bigquery.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/bigquery.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/bigquery.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/cql.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/cql.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/cql.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/deltatbl.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/deltatbl.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/deltatbl.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/elastic.cpp +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/elastic.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/elastic.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/iceberg.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/iceberg.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/iceberg.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/influx.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/influx.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/influx.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/mongo.cpp +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/mongo.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/mongo.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/parser.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/parser.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/parser.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/pgsql.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/pgsql.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/pgsql.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/rethink.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/rethink.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/rethink.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sosql.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sosql.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sosql.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sql.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sql.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sql.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sqlserver.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sqlserver.pxd +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/parsers/sqlserver.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/plugins/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/plugins/importer.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/plugins/sources/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/arangodb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/bigquery.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/cassandra.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/db.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/default.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/deltatbl.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/documentdb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/dummy.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/elastic.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/external.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/http.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/iceberg.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/influx.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/mysql.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/pg.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/py.typed +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/rest.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/rethink.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/salesforce.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/scylladb.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/amazon.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/countries.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/ga.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/geofcc.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/gmaps.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/graphcountries.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/graphql.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/http.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/hubspot.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/openweather.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/parsers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/parsers/amproduct.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/parsers/xpath.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/pokemon.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/populartimes.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/py.typed +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/rest.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/retailnext.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/rssapp.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/salesforce.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/scrapper.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/shoppertrack.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/swop.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/uap.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/upc.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/wm_stores.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/zammad.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sources/zipcodeapi.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sql.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/providers/sqlserver.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/py.typed +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/qs_parsers/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/base.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/executor.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/models.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/components/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/Concat.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/GroupBy.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/Info.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/Join.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/Melt.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/Merge.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/filter/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/operators/filter/flt.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/sources/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/sources/file.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/sources/query.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/Forecast.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/Map.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/abstract.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/correlation.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/crosstab.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/google/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/google/maps.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/pivot.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/tOrder.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/multi/transformations/tPandas.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/obj.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/queries/qs.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/scheduler/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/scheduler/jobs.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/scheduler/notifications.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/scheduler/scheduler.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/services.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/template/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/template/parser.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/converters.cpp +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/converters.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/dt/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/dt/filters.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/dt/transforms.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/py.typed +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/typedefs.c +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/typedefs.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/validators.cpp +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/types/validators.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/cache_serialization.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/events.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/fn.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/functions.cpp +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/functions.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/getfunc.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/handlers.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/parseqs.cpp +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/parseqs.pyx +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource/utils/validators.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource.egg-info/dependency_links.txt +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource.egg-info/entry_points.txt +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource.egg-info/not-zip-safe +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/querysource.egg-info/top_level.txt +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/run.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/Cargo.lock +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/Cargo.toml +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/pyproject.toml +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/arangodb_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/bigquery_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/cql_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/elastic_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/filter_common.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/flux_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/lib.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/mongo_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/mssql_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/parseqs.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/pgsql_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/rethink_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/safe_dict.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/soql_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/sql_parser.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/rust/src/validators.rs +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/setup.cfg +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/setup.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/static/notebook/bundle.js +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/templates/__init__.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/templates/base.html +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/templates/default.html +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/templates/default_table.html +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/templates/fontlist-v330.json +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/templates/fontlist-v390.json +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/templates/table_charts.html +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_api.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_arangodb_parser.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_column_filters.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_elastic_parser.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_eval.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_join_conditions.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_join_with_column_filter.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_rss.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_rust_parsers.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_scheduler_core.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_scheduler_integration.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_scheduler_jobs.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tests/test_scheduler_notifications.py +0 -0
- {querysource-4.1.9 → querysource-4.1.11}/tox.ini +0 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# /pr-review — Review a PR Against Jira Acceptance Criteria
|
|
2
|
+
|
|
3
|
+
Fetches a GitHub Pull Request and its associated Jira ticket, then reviews
|
|
4
|
+
whether the code changes satisfy the ticket's description and acceptance criteria.
|
|
5
|
+
Optionally converts the PR to draft if criteria are not met.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
/pr-review <PR_URL> <JIRA_KEY> [--auto-draft]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Examples
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/pr-review https://github.com/Trocdigital/navigator-dataintegrator-tasks/pull/4028 NAV-8036
|
|
17
|
+
/pr-review https://github.com/Trocdigital/navigator-dataintegrator-tasks/pull/4028 NAV-8036 --auto-draft
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Arguments
|
|
21
|
+
|
|
22
|
+
| Argument | Required | Description |
|
|
23
|
+
|----------------|----------|-------------|
|
|
24
|
+
| `<PR_URL>` | yes | Full GitHub PR URL (e.g., `https://github.com/org/repo/pull/123`) |
|
|
25
|
+
| `<JIRA_KEY>` | yes | Jira issue key (e.g., `NAV-8036`) |
|
|
26
|
+
| `--auto-draft` | no | If present AND criteria fail, convert PR to draft automatically |
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
- `gh` CLI installed and authenticated (`gh auth status`)
|
|
31
|
+
- `jq` installed (for JSON parsing)
|
|
32
|
+
- Jira credentials configured in `env/.env` (loaded at runtime via `navconfig`):
|
|
33
|
+
- `JIRA_INSTANCE` — e.g., `https://trocglobal.atlassian.net/`
|
|
34
|
+
- `JIRA_USERNAME` — email for Jira Cloud
|
|
35
|
+
- `JIRA_API_TOKEN` — API token (Personal Access Token)
|
|
36
|
+
|
|
37
|
+
To load these variables into the current shell for bash commands, run:
|
|
38
|
+
```bash
|
|
39
|
+
# Quick one-liner to export Jira vars from env/.env
|
|
40
|
+
eval "$(python -c "from navconfig import config; import os; [print(f'export {k}={v}') for k,v in os.environ.items() if k.startswith('JIRA_')]")"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Steps
|
|
44
|
+
|
|
45
|
+
### 1. Parse Input & Validate Prerequisites
|
|
46
|
+
|
|
47
|
+
Extract org, repo, and PR number from the URL:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Parse: https://github.com/Trocdigital/navigator-dataintegrator-tasks/pull/4028
|
|
51
|
+
PR_URL="$1"
|
|
52
|
+
JIRA_KEY="$2"
|
|
53
|
+
AUTO_DRAFT=false
|
|
54
|
+
if [[ "$3" == "--auto-draft" ]]; then AUTO_DRAFT=true; fi
|
|
55
|
+
|
|
56
|
+
# Extract components
|
|
57
|
+
REPO=$(echo "$PR_URL" | sed -E 's|https://github.com/([^/]+/[^/]+)/pull/.*|\1|')
|
|
58
|
+
PR_NUMBER=$(echo "$PR_URL" | sed -E 's|.*/pull/([0-9]+).*|\1|')
|
|
59
|
+
|
|
60
|
+
# Validate
|
|
61
|
+
gh auth status 2>/dev/null || echo "⚠️ gh CLI not authenticated. Run: gh auth login"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Load Jira credentials from `env/.env` via navconfig:
|
|
65
|
+
```bash
|
|
66
|
+
# Load Jira env vars using navconfig (reads env/.env)
|
|
67
|
+
eval "$(python -c "
|
|
68
|
+
from navconfig import config
|
|
69
|
+
import os
|
|
70
|
+
for k in ('JIRA_INSTANCE', 'JIRA_USERNAME', 'JIRA_API_TOKEN'):
|
|
71
|
+
v = os.environ.get(k, '')
|
|
72
|
+
if v:
|
|
73
|
+
print(f'export {k}={v}')
|
|
74
|
+
")"
|
|
75
|
+
|
|
76
|
+
# Strip trailing slash from JIRA_INSTANCE to avoid double-slash in URLs
|
|
77
|
+
JIRA_INSTANCE="${JIRA_INSTANCE%/}"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Verify Jira env vars are set:
|
|
81
|
+
```bash
|
|
82
|
+
[[ -z "$JIRA_INSTANCE" ]] && echo "⚠️ JIRA_INSTANCE not set" && exit 1
|
|
83
|
+
[[ -z "$JIRA_API_TOKEN" ]] && echo "⚠️ JIRA_API_TOKEN not set" && exit 1
|
|
84
|
+
[[ -z "$JIRA_USERNAME" ]] && echo "⚠️ JIRA_USERNAME not set" && exit 1
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Fetch GitHub PR Data
|
|
88
|
+
|
|
89
|
+
Collect three things from the PR: metadata, description, and the diff.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# PR metadata (title, state, author, base/head branches, labels, reviewers)
|
|
93
|
+
gh pr view "$PR_NUMBER" --repo "$REPO" --json title,state,author,baseRefName,headRefName,labels,reviewRequests,isDraft
|
|
94
|
+
|
|
95
|
+
# PR description (body)
|
|
96
|
+
gh pr view "$PR_NUMBER" --repo "$REPO" --json body --jq '.body'
|
|
97
|
+
|
|
98
|
+
# Full diff (the actual code changes)
|
|
99
|
+
gh pr diff "$PR_NUMBER" --repo "$REPO"
|
|
100
|
+
|
|
101
|
+
# List of changed files (for summary context)
|
|
102
|
+
gh pr diff "$PR_NUMBER" --repo "$REPO" --name-only
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Diff size guard**: If the diff exceeds ~8000 lines, switch to a summarized
|
|
106
|
+
approach — fetch only filenames + stats, then selectively read the most relevant
|
|
107
|
+
files based on the Jira ticket context:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
DIFF_LINES=$(gh pr diff "$PR_NUMBER" --repo "$REPO" | wc -l)
|
|
111
|
+
if [[ "$DIFF_LINES" -gt 8000 ]]; then
|
|
112
|
+
echo "⚠️ Large PR ($DIFF_LINES lines). Fetching file-level stats and sampling key files."
|
|
113
|
+
gh pr diff "$PR_NUMBER" --repo "$REPO" --stat
|
|
114
|
+
# Then selectively: gh api repos/$REPO/pulls/$PR_NUMBER/files --paginate
|
|
115
|
+
# and read only files matching patterns from the Jira ticket
|
|
116
|
+
fi
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. Fetch PR Comments & AI Bot Reviews
|
|
120
|
+
|
|
121
|
+
Collect PR comments, review comments, and reviews to surface findings from
|
|
122
|
+
automated reviewers (Gemini Code Assist, GitHub Copilot, CodeRabbit, etc.).
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# PR issue-level comments (general discussion)
|
|
126
|
+
gh api repos/$REPO/issues/$PR_NUMBER/comments --paginate \
|
|
127
|
+
--jq '.[] | {author: .user.login, authorType: .author_association, body: .body, created: .created_at}' \
|
|
128
|
+
| head -200
|
|
129
|
+
|
|
130
|
+
# PR review comments (inline on specific lines of code)
|
|
131
|
+
gh api repos/$REPO/pulls/$PR_NUMBER/comments --paginate \
|
|
132
|
+
--jq '.[] | {author: .user.login, path: .path, line: .line, body: .body, created: .created_at}' \
|
|
133
|
+
| head -200
|
|
134
|
+
|
|
135
|
+
# PR reviews (approve/request-changes/comment with body)
|
|
136
|
+
gh api repos/$REPO/pulls/$PR_NUMBER/reviews --paginate \
|
|
137
|
+
--jq '.[] | {author: .user.login, state: .state, body: .body, submitted: .submitted_at}' \
|
|
138
|
+
| head -200
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**AI bot detection**: Identify comments from known automated reviewers by
|
|
142
|
+
matching author login patterns:
|
|
143
|
+
|
|
144
|
+
| Bot | Login Pattern |
|
|
145
|
+
|-----|---------------|
|
|
146
|
+
| Gemini Code Assist | `gemini-code-assist[bot]` |
|
|
147
|
+
| GitHub Copilot | `copilot-pull-request-review[bot]`, `github-copilot[bot]` |
|
|
148
|
+
| CodeRabbit | `coderabbitai[bot]` |
|
|
149
|
+
| SonarCloud | `sonarcloud[bot]` |
|
|
150
|
+
| Codacy | `codacy-production[bot]` |
|
|
151
|
+
| Deepsource | `deepsource-autofix[bot]`, `deepsource-io[bot]` |
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Filter for known bot authors
|
|
155
|
+
KNOWN_BOTS="gemini-code-assist|copilot-pull-request-review|github-copilot|coderabbitai|sonarcloud|codacy-production|deepsource"
|
|
156
|
+
|
|
157
|
+
# Bot issue comments
|
|
158
|
+
gh api repos/$REPO/issues/$PR_NUMBER/comments --paginate \
|
|
159
|
+
--jq "[.[] | select(.user.login | test(\"$KNOWN_BOTS\"))]"
|
|
160
|
+
|
|
161
|
+
# Bot review comments (inline)
|
|
162
|
+
gh api repos/$REPO/pulls/$PR_NUMBER/comments --paginate \
|
|
163
|
+
--jq "[.[] | select(.user.login | test(\"$KNOWN_BOTS\"))]"
|
|
164
|
+
|
|
165
|
+
# Bot reviews
|
|
166
|
+
gh api repos/$REPO/pulls/$PR_NUMBER/reviews --paginate \
|
|
167
|
+
--jq "[.[] | select(.user.login | test(\"$KNOWN_BOTS\"))]"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**How to use bot findings in the review:**
|
|
171
|
+
|
|
172
|
+
- **Agree/Disagree**: For each substantive bot finding, state whether you agree
|
|
173
|
+
or disagree and why. Bot tools can produce false positives.
|
|
174
|
+
- **Incorporate valid findings**: If a bot found a real issue (bug, security
|
|
175
|
+
concern, missing edge case), include it in the Code Observations section
|
|
176
|
+
and credit the source (e.g., "Gemini Code Assist flagged...").
|
|
177
|
+
- **Dismiss false positives**: If a bot finding is incorrect or irrelevant,
|
|
178
|
+
note it briefly with reasoning so the PR author can ignore it confidently.
|
|
179
|
+
- **Resolved vs unresolved**: Check if the bot's comment was addressed in a
|
|
180
|
+
subsequent commit. If resolved, note it as such.
|
|
181
|
+
|
|
182
|
+
Add a dedicated section to the review report:
|
|
183
|
+
|
|
184
|
+
```markdown
|
|
185
|
+
## AI Bot Review Findings
|
|
186
|
+
|
|
187
|
+
### {Bot Name} ({N} comments)
|
|
188
|
+
|
|
189
|
+
| # | File | Finding | Our Assessment |
|
|
190
|
+
|---|------|---------|----------------|
|
|
191
|
+
| 1 | {path:line} | {summary of bot finding} | ✅ Agree / ❌ Disagree — {reason} / ✔️ Resolved |
|
|
192
|
+
|
|
193
|
+
{If no bot comments found: "No automated reviewer comments found on this PR."}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 4. Fetch Jira Ticket Data
|
|
197
|
+
|
|
198
|
+
Use the Jira REST API to get the ticket description and acceptance criteria.
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# Fetch issue with relevant fields
|
|
202
|
+
curl -s -u "$JIRA_USERNAME:$JIRA_API_TOKEN" \
|
|
203
|
+
-H "Content-Type: application/json" \
|
|
204
|
+
"$JIRA_INSTANCE/rest/api/3/issue/$JIRA_KEY?fields=summary,description,status,priority,issuetype,labels,components,customfield_10021,customfield_10022,customfield_10035" \
|
|
205
|
+
| jq '.'
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Acceptance criteria extraction**: The AC field varies per Jira instance.
|
|
209
|
+
Try the common custom fields in order:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Try customfield_10021, then 10022, then 10035
|
|
213
|
+
for FIELD in customfield_10021 customfield_10022 customfield_10035; do
|
|
214
|
+
AC=$(echo "$JIRA_RESPONSE" | jq -r ".fields.$FIELD // empty")
|
|
215
|
+
if [[ -n "$AC" ]]; then
|
|
216
|
+
echo "✅ Acceptance criteria found in $FIELD"
|
|
217
|
+
break
|
|
218
|
+
fi
|
|
219
|
+
done
|
|
220
|
+
|
|
221
|
+
# Fallback: check if AC is embedded in the description itself
|
|
222
|
+
if [[ -z "$AC" ]]; then
|
|
223
|
+
echo "⚠️ No dedicated AC field found. Will extract from description."
|
|
224
|
+
fi
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Also fetch the ticket's **subtasks** and **linked issues** for additional context:
|
|
228
|
+
```bash
|
|
229
|
+
curl -s -u "$JIRA_USERNAME:$JIRA_API_TOKEN" \
|
|
230
|
+
"$JIRA_INSTANCE/rest/api/3/issue/$JIRA_KEY?fields=subtasks,issuelinks" \
|
|
231
|
+
| jq '.fields.subtasks, .fields.issuelinks'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### 5. Build the Review Context
|
|
235
|
+
|
|
236
|
+
Assemble all gathered data into a structured prompt for Claude to reason over.
|
|
237
|
+
This is not a prompt you write — Claude Code IS the LLM. Present the data
|
|
238
|
+
in a structured format so Claude can analyze it directly:
|
|
239
|
+
|
|
240
|
+
```markdown
|
|
241
|
+
## PR Review Context
|
|
242
|
+
|
|
243
|
+
### Jira Ticket: {JIRA_KEY}
|
|
244
|
+
**Summary**: {ticket.summary}
|
|
245
|
+
**Status**: {ticket.status}
|
|
246
|
+
**Type**: {ticket.issuetype}
|
|
247
|
+
**Priority**: {ticket.priority}
|
|
248
|
+
|
|
249
|
+
#### Description
|
|
250
|
+
{ticket.description — rendered from ADF to markdown}
|
|
251
|
+
|
|
252
|
+
#### Acceptance Criteria
|
|
253
|
+
{acceptance_criteria — numbered list}
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
### Pull Request: {REPO}#{PR_NUMBER}
|
|
258
|
+
**Title**: {pr.title}
|
|
259
|
+
**Author**: {pr.author}
|
|
260
|
+
**Branch**: {pr.headRefName} → {pr.baseRefName}
|
|
261
|
+
**State**: {pr.state} | Draft: {pr.isDraft}
|
|
262
|
+
|
|
263
|
+
#### PR Description
|
|
264
|
+
{pr.body}
|
|
265
|
+
|
|
266
|
+
#### Changed Files
|
|
267
|
+
{file list with +/- stats}
|
|
268
|
+
|
|
269
|
+
#### Full Diff
|
|
270
|
+
{diff content — or sampled files for large PRs}
|
|
271
|
+
|
|
272
|
+
#### AI Bot Comments
|
|
273
|
+
{bot_name: [{path, line, finding}...] — or "None found"}
|
|
274
|
+
|
|
275
|
+
#### Human Review Comments
|
|
276
|
+
{reviewer: [{path, line, comment}...] — or "None found"}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### 6. Perform the Review
|
|
280
|
+
|
|
281
|
+
Analyze the PR against the Jira ticket using these review dimensions:
|
|
282
|
+
|
|
283
|
+
#### 5a. Acceptance Criteria Compliance
|
|
284
|
+
|
|
285
|
+
For EACH acceptance criterion, determine:
|
|
286
|
+
- **✅ Met**: The diff clearly implements the criterion.
|
|
287
|
+
- **⚠️ Partially Met**: Some aspects are present but incomplete or unclear.
|
|
288
|
+
- **❌ Not Met**: No evidence in the diff that this criterion is addressed.
|
|
289
|
+
- **🔍 Unable to Verify**: Requires runtime testing, external system, or
|
|
290
|
+
context not available in the diff (e.g., "works on mobile").
|
|
291
|
+
|
|
292
|
+
#### 5b. Description Alignment
|
|
293
|
+
|
|
294
|
+
Check if the PR's changes match the ticket description:
|
|
295
|
+
- Does the PR address the core problem/feature described?
|
|
296
|
+
- Are there changes in the PR that are NOT related to the ticket? (scope creep)
|
|
297
|
+
- Are there aspects of the description NOT addressed by the PR? (gaps)
|
|
298
|
+
|
|
299
|
+
#### 5c. Code Quality Observations (lightweight)
|
|
300
|
+
|
|
301
|
+
Not a full code review — focus on red flags:
|
|
302
|
+
- Obviously missing error handling in new code
|
|
303
|
+
- Hardcoded values that should be configurable
|
|
304
|
+
- Missing tests for new functionality
|
|
305
|
+
- Potential breaking changes
|
|
306
|
+
|
|
307
|
+
### 7. Generate the Review Report
|
|
308
|
+
|
|
309
|
+
Output a structured report:
|
|
310
|
+
|
|
311
|
+
```markdown
|
|
312
|
+
# PR Review: {REPO}#{PR_NUMBER} ↔ {JIRA_KEY}
|
|
313
|
+
|
|
314
|
+
**Date**: {today}
|
|
315
|
+
**Reviewer**: Claude Code (automated)
|
|
316
|
+
**Overall Verdict**: ✅ Approved | ⚠️ Needs Attention | ❌ Does Not Meet Criteria
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Acceptance Criteria Compliance
|
|
321
|
+
|
|
322
|
+
| # | Criterion | Status | Evidence |
|
|
323
|
+
|---|-----------|--------|----------|
|
|
324
|
+
| 1 | {criterion_1} | ✅/⚠️/❌/🔍 | {file:line or explanation} |
|
|
325
|
+
| 2 | {criterion_2} | ✅/⚠️/❌/🔍 | {file:line or explanation} |
|
|
326
|
+
| ... | | | |
|
|
327
|
+
|
|
328
|
+
**Score**: {met}/{total} criteria met ({percentage}%)
|
|
329
|
+
|
|
330
|
+
## Description Alignment
|
|
331
|
+
|
|
332
|
+
### ✅ Addressed
|
|
333
|
+
- {aspect 1 from description that IS covered}
|
|
334
|
+
|
|
335
|
+
### ❌ Gaps
|
|
336
|
+
- {aspect from description NOT covered by the PR}
|
|
337
|
+
|
|
338
|
+
### ⚠️ Out of Scope
|
|
339
|
+
- {changes in PR not related to the ticket}
|
|
340
|
+
|
|
341
|
+
## Code Observations
|
|
342
|
+
- {any red flags, brief}
|
|
343
|
+
|
|
344
|
+
## AI Bot Review Findings
|
|
345
|
+
|
|
346
|
+
{If bot comments were found, add a subsection per bot:}
|
|
347
|
+
|
|
348
|
+
### {Bot Name} ({N} comments)
|
|
349
|
+
|
|
350
|
+
| # | File | Finding | Our Assessment |
|
|
351
|
+
|---|------|---------|----------------|
|
|
352
|
+
| 1 | {path:line} | {summary of bot finding} | ✅ Agree / ❌ Disagree — {reason} / ✔️ Resolved |
|
|
353
|
+
|
|
354
|
+
{If no bot comments found: "No automated reviewer comments found on this PR."}
|
|
355
|
+
|
|
356
|
+
## Verdict & Recommendation
|
|
357
|
+
|
|
358
|
+
{Summary paragraph explaining the overall assessment}
|
|
359
|
+
|
|
360
|
+
**Action**: {APPROVE | REQUEST_CHANGES | CONVERT_TO_DRAFT}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### 8. Take Action (Optional)
|
|
364
|
+
|
|
365
|
+
Based on the verdict and flags:
|
|
366
|
+
|
|
367
|
+
#### If `--auto-draft` AND verdict is ❌:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# Convert PR to draft using GitHub GraphQL API
|
|
371
|
+
PR_NODE_ID=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json id --jq '.id')
|
|
372
|
+
gh api graphql -f query='
|
|
373
|
+
mutation {
|
|
374
|
+
convertPullRequestToDraft(input: {pullRequestId: "'"$PR_NODE_ID"'"}) {
|
|
375
|
+
pullRequest { isDraft }
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
'
|
|
379
|
+
echo "🔒 PR #$PR_NUMBER converted to draft — criteria not met."
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
#### Always: Post review as PR comment
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# Post the review report as a PR comment
|
|
386
|
+
gh pr comment "$PR_NUMBER" --repo "$REPO" --body "$REVIEW_REPORT"
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Optionally: Add labels
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
# Tag the PR with review status
|
|
393
|
+
if [[ "$VERDICT" == "approved" ]]; then
|
|
394
|
+
gh pr edit "$PR_NUMBER" --repo "$REPO" --add-label "review:passed"
|
|
395
|
+
elif [[ "$VERDICT" == "needs-attention" ]]; then
|
|
396
|
+
gh pr edit "$PR_NUMBER" --repo "$REPO" --add-label "review:needs-attention"
|
|
397
|
+
else
|
|
398
|
+
gh pr edit "$PR_NUMBER" --repo "$REPO" --add-label "review:blocked"
|
|
399
|
+
fi
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 9. Save Report (Optional)
|
|
403
|
+
|
|
404
|
+
If the user confirms, persist the review:
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
mkdir -p artifacts/reviews/
|
|
408
|
+
REPORT_FILE="artifacts/reviews/PR-${PR_NUMBER}-${JIRA_KEY}-review.md"
|
|
409
|
+
# Save report to file
|
|
410
|
+
git add "$REPORT_FILE"
|
|
411
|
+
git commit -m "review: PR #${PR_NUMBER} against ${JIRA_KEY}"
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### 10. Output Summary
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
✅ PR Review Complete: {REPO}#{PR_NUMBER} ↔ {JIRA_KEY}
|
|
418
|
+
|
|
419
|
+
Verdict: {verdict_emoji} {verdict_text}
|
|
420
|
+
Criteria: {met}/{total} met ({percentage}%)
|
|
421
|
+
|
|
422
|
+
{if auto-draft triggered}
|
|
423
|
+
🔒 PR converted to draft — criteria not met.
|
|
424
|
+
{end if}
|
|
425
|
+
|
|
426
|
+
Comment posted: {pr_comment_url}
|
|
427
|
+
Report saved: artifacts/reviews/PR-{PR_NUMBER}-{JIRA_KEY}-review.md
|
|
428
|
+
|
|
429
|
+
To approve the PR:
|
|
430
|
+
gh pr review {PR_NUMBER} --repo {REPO} --approve
|
|
431
|
+
|
|
432
|
+
To request changes:
|
|
433
|
+
gh pr review {PR_NUMBER} --repo {REPO} --request-changes --body "..."
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Edge Cases
|
|
437
|
+
|
|
438
|
+
- **No acceptance criteria found**: Use the ticket description as the evaluation
|
|
439
|
+
baseline. Warn that the review is based on description only.
|
|
440
|
+
- **PR already merged**: Warn and still produce the review (useful for auditing).
|
|
441
|
+
- **PR is already draft**: Skip the convert-to-draft step, note it in the report.
|
|
442
|
+
- **Private repo**: `gh` handles auth; Jira token handles Jira. No extra steps.
|
|
443
|
+
- **Jira ticket not found**: Error with clear message suggesting to verify the key
|
|
444
|
+
and server URL.
|
|
445
|
+
- **Large PR (>8000 lines)**: Use file-level stats + selective file reading.
|
|
446
|
+
Note in the report which files were fully reviewed vs. summarized.
|
|
447
|
+
- **Bot comments with outdated suggestions**: If a bot comment references code
|
|
448
|
+
that was changed in a subsequent commit, mark the finding as "Resolved" rather
|
|
449
|
+
than evaluating the stale suggestion.
|
|
450
|
+
- **Conflicting bot opinions**: If two bots disagree (e.g., Gemini says X is fine,
|
|
451
|
+
Copilot flags X), evaluate the code independently and state which bot is correct.
|
|
452
|
+
- **ADF (Atlassian Document Format)**: Jira Cloud v3 returns description as ADF JSON.
|
|
453
|
+
Parse it to extract text content, or use `renderedFields` expand:
|
|
454
|
+
```bash
|
|
455
|
+
curl ... "$JIRA_INSTANCE/rest/api/3/issue/$JIRA_KEY?expand=renderedFields" \
|
|
456
|
+
| jq '.renderedFields.description'
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Jira Description Rendering
|
|
460
|
+
|
|
461
|
+
Jira Cloud v3 API returns descriptions in ADF (Atlassian Document Format).
|
|
462
|
+
To get readable text, use the `renderedFields` expansion which returns HTML,
|
|
463
|
+
then convert to markdown-ish text:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
# Get rendered (HTML) description
|
|
467
|
+
RENDERED=$(curl -s -u "$JIRA_USERNAME:$JIRA_API_TOKEN" \
|
|
468
|
+
"$JIRA_INSTANCE/rest/api/3/issue/$JIRA_KEY?expand=renderedFields" \
|
|
469
|
+
| jq -r '.renderedFields.description')
|
|
470
|
+
|
|
471
|
+
# Strip HTML tags for plain text (basic)
|
|
472
|
+
DESCRIPTION=$(echo "$RENDERED" | sed 's/<[^>]*>//g' | sed '/^$/d')
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
## Reference
|
|
476
|
+
|
|
477
|
+
- Existing code review: `.claude/commands/sdd-codereview.md`
|
|
478
|
+
- Code reviewer agent: `.claude/agents/code-reviewer.md`
|
|
479
|
+
- GitHub CLI docs: `gh pr --help`
|
|
480
|
+
- Jira REST API v3: `https://developer.atlassian.com/cloud/jira/platform/rest/v3/`
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Explore a feature idea by generating multiple approaches with library and code references, then produce a brainstorm document that feeds into /sdd-spec.
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
# /sdd-brainstorm — Structured Idea Exploration
|
|
2
6
|
|
|
3
7
|
Explore a feature idea by generating multiple approaches with library and code references,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
model: haiku
|
|
3
|
+
description: Verify that a feature's tasks were implemented, push the branch, optionally resolve the linked Jira ticket, and clean up the worktree.
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
# /sdd-done — Verify, Push, and Cleanup a Feature
|
|
6
7
|
|
|
7
8
|
Verify that a feature's tasks were implemented in its worktree, ensure the branch is
|
|
8
|
-
pushed, and clean up the worktree.
|
|
9
|
+
pushed, and clean up the worktree. Optionally transitions the linked Jira ticket to
|
|
10
|
+
"Done" / "Resolved".
|
|
9
11
|
|
|
10
12
|
**This command runs on `dev` (or the main repo), NOT inside a worktree.**
|
|
11
13
|
It looks INTO the worktree to verify work, but modifies state only on `dev`.
|
|
@@ -14,8 +16,9 @@ It looks INTO the worktree to verify work, but modifies state only on `dev`.
|
|
|
14
16
|
```
|
|
15
17
|
/sdd-done FEAT-014
|
|
16
18
|
/sdd-done videoreel-visual-changes
|
|
17
|
-
/sdd-done FEAT-014 --dry-run
|
|
18
|
-
/sdd-done FEAT-014 --force
|
|
19
|
+
/sdd-done FEAT-014 --dry-run # show what would change, don't change anything
|
|
20
|
+
/sdd-done FEAT-014 --force # mark done even if some checks fail
|
|
21
|
+
/sdd-done FEAT-014 --resolve-jira # also transition the Jira ticket to Done
|
|
19
22
|
```
|
|
20
23
|
|
|
21
24
|
## Guardrails
|
|
@@ -185,7 +188,110 @@ After a successful merge, push `dev`:
|
|
|
185
188
|
git push origin dev
|
|
186
189
|
```
|
|
187
190
|
|
|
188
|
-
### 10.
|
|
191
|
+
### 10. Transition Jira Ticket (if --resolve-jira)
|
|
192
|
+
|
|
193
|
+
If `--resolve-jira` is passed AND the spec has a Jira key (set by `/sdd-tojira`):
|
|
194
|
+
|
|
195
|
+
**a) Extract the Jira key from the spec:**
|
|
196
|
+
```bash
|
|
197
|
+
# Look for "**Jira**: [NAV-8036](...)" or a "jira:" metadata field in the spec
|
|
198
|
+
JIRA_KEY=$(grep -oP '(?<=\*\*Jira\*\*: \[)[A-Z]+-\d+' sdd/specs/<feature>.spec.md)
|
|
199
|
+
# Or from the brainstorm "## Jira Source" table
|
|
200
|
+
if [[ -z "$JIRA_KEY" ]]; then
|
|
201
|
+
JIRA_KEY=$(grep -oP '(?<=\| Key \| )[A-Z]+-\d+' sdd/proposals/<key>-*.brainstorm.md)
|
|
202
|
+
fi
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If no Jira key is found, skip this step with a note:
|
|
206
|
+
```
|
|
207
|
+
ℹ️ No Jira key found in spec — skipping Jira transition.
|
|
208
|
+
To link a spec to Jira: /sdd-tojira <spec-path>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**b) Load Jira credentials:**
|
|
212
|
+
```bash
|
|
213
|
+
eval "$(python -c "from navconfig import config; import os; [print(f'export {k}={v}') for k,v in os.environ.items() if k.startswith('JIRA_')]")"
|
|
214
|
+
JIRA_INSTANCE="${JIRA_INSTANCE%/}"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
If `JIRA_INSTANCE` or `JIRA_API_TOKEN` are not set, warn and skip.
|
|
218
|
+
|
|
219
|
+
**c) Get available transitions for the ticket:**
|
|
220
|
+
|
|
221
|
+
Jira transitions are workflow-dependent — you cannot set a status directly.
|
|
222
|
+
First, fetch the available transitions:
|
|
223
|
+
|
|
224
|
+
**MCP path:**
|
|
225
|
+
```
|
|
226
|
+
jira_transition_issue(issue_key="<JIRA_KEY>") # list available transitions
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**curl fallback:**
|
|
230
|
+
```bash
|
|
231
|
+
TRANSITIONS=$(curl -s -u "$JIRA_USERNAME:$JIRA_API_TOKEN" \
|
|
232
|
+
"$JIRA_INSTANCE/rest/api/3/issue/$JIRA_KEY/transitions")
|
|
233
|
+
echo "$TRANSITIONS" | jq '.transitions[] | {id, name}'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**d) Find and execute the "Done" / "Resolved" transition:**
|
|
237
|
+
|
|
238
|
+
Look for a transition whose name matches (case-insensitive):
|
|
239
|
+
`Done`, `Resolved`, `Close`, `Ready for UAT`, `Complete`.
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Find the transition ID
|
|
243
|
+
TRANSITION_ID=$(echo "$TRANSITIONS" | jq -r '
|
|
244
|
+
.transitions[] |
|
|
245
|
+
select(.name | test("(?i)done|resolved|close|complete|ready for uat")) |
|
|
246
|
+
.id' | head -1)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
If found, execute it:
|
|
250
|
+
|
|
251
|
+
**MCP path:**
|
|
252
|
+
```
|
|
253
|
+
jira_transition_issue(issue_key="<JIRA_KEY>", transition_id="<TRANSITION_ID>")
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**curl fallback:**
|
|
257
|
+
```bash
|
|
258
|
+
curl -s -u "$JIRA_USERNAME:$JIRA_API_TOKEN" \
|
|
259
|
+
-H "Content-Type: application/json" \
|
|
260
|
+
-X POST "$JIRA_INSTANCE/rest/api/3/issue/$JIRA_KEY/transitions" \
|
|
261
|
+
-d "{\"transition\": {\"id\": \"$TRANSITION_ID\"}}"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
If multiple matching transitions exist, prefer in this order:
|
|
265
|
+
1. "Done"
|
|
266
|
+
2. "Resolved"
|
|
267
|
+
3. "Ready for UAT"
|
|
268
|
+
4. "Complete"
|
|
269
|
+
5. "Close"
|
|
270
|
+
|
|
271
|
+
If no matching transition is found:
|
|
272
|
+
```
|
|
273
|
+
⚠️ No "Done" or "Resolved" transition available for <JIRA_KEY>.
|
|
274
|
+
Current status: <current_status>
|
|
275
|
+
Available transitions: <list>
|
|
276
|
+
You may need to transition it manually in Jira.
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**e) Optionally resolve subtasks too:**
|
|
280
|
+
|
|
281
|
+
If the ticket has subtasks (created by `--with-subtasks` in `/sdd-tojira`),
|
|
282
|
+
transition each one that is still open:
|
|
283
|
+
```bash
|
|
284
|
+
SUBTASKS=$(curl -s -u "$JIRA_USERNAME:$JIRA_API_TOKEN" \
|
|
285
|
+
"$JIRA_INSTANCE/rest/api/3/issue/$JIRA_KEY?fields=subtasks" \
|
|
286
|
+
| jq -r '.fields.subtasks[].key')
|
|
287
|
+
|
|
288
|
+
for SUBTASK in $SUBTASKS; do
|
|
289
|
+
# Get transitions for this subtask, find "Done", execute
|
|
290
|
+
# Same logic as above
|
|
291
|
+
done
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### 11. Cleanup the Worktree
|
|
189
295
|
```bash
|
|
190
296
|
git worktree remove .claude/worktrees/feat-<FEAT-ID>-<slug>
|
|
191
297
|
```
|
|
@@ -204,7 +310,7 @@ Optionally delete the local feature branch (it's been merged):
|
|
|
204
310
|
git branch -d feat-<FEAT-ID>-<slug>
|
|
205
311
|
```
|
|
206
312
|
|
|
207
|
-
###
|
|
313
|
+
### 12. Output
|
|
208
314
|
```
|
|
209
315
|
✅ FEAT-<ID> — <title>: <N>/<total> tasks closed.
|
|
210
316
|
|
|
@@ -219,12 +325,19 @@ Worktree removed: .claude/worktrees/feat-<ID>-<slug>
|
|
|
219
325
|
Local branch deleted: feat-<ID>-<slug>
|
|
220
326
|
```
|
|
221
327
|
|
|
328
|
+
If `--resolve-jira` was used and succeeded:
|
|
329
|
+
```
|
|
330
|
+
Jira: NAV-8036 → Done ✅
|
|
331
|
+
Subtasks transitioned: 4/4
|
|
332
|
+
```
|
|
333
|
+
|
|
222
334
|
If ALL tasks were closed:
|
|
223
335
|
```
|
|
224
336
|
✅ FEAT-<ID> — <title>: all <N> tasks closed and merged into dev.
|
|
225
337
|
|
|
226
338
|
Worktree cleaned up.
|
|
227
339
|
Feature branch merged and deleted.
|
|
340
|
+
{if --resolve-jira} Jira NAV-8036 → Done ✅ {end if}
|
|
228
341
|
```
|
|
229
342
|
|
|
230
343
|
## Reference
|