{"id":50472430,"url":"https://github.com/semcod/pyqual","last_synced_at":"2026-06-01T11:03:34.463Z","repository":{"id":347776364,"uuid":"1195240761","full_name":"semcod/pyqual","owner":"semcod","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-19T09:04:51.000Z","size":24271,"stargazers_count":0,"open_issues_count":41,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-19T10:20:44.645Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/semcod.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-29T12:24:42.000Z","updated_at":"2026-04-19T09:04:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/semcod/pyqual","commit_stats":null,"previous_names":["semcod/pyqual"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/semcod/pyqual","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Fpyqual","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Fpyqual/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Fpyqual/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Fpyqual/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/semcod","download_url":"https://codeload.github.com/semcod/pyqual/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semcod%2Fpyqual/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33771630,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-01T11:03:34.353Z","updated_at":"2026-06-01T11:03:34.457Z","avatar_url":"https://github.com/semcod.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![img.png](img.png)\n\n# pyqual\n\n## AI Cost Tracking\n\n![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.149-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)\n![AI Cost](https://img.shields.io/badge/AI%20Cost-$3.64-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-59.9h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)\n\n- 🤖 **LLM usage:** $3.6398 (237 commits)\n- 👤 **Human dev:** ~$5990 (59.9h @ $100/h, 30min dedup)\n\nGenerated on 2026-05-12 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)\n\n---\n\n\u003c!-- pyqual:badges:start --\u003e\n![Version](https://img.shields.io/badge/version-0.1.149-blue) ![Python](https://img.shields.io/badge/python-%3E%3D3.9-blue) ![License](https://img.shields.io/badge/license-Apache-2.0-green) ![AI Cost](https://img.shields.io/badge/AI%20Cost-%2433.75%20%28225%20commits%29-red) ![Human Time](https://img.shields.io/badge/Human%20Time-74.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fopenai%2Fgpt-5-mini-lightgrey)\n![pyqual](https://img.shields.io/badge/pyqual-pass-brightgreen) ![gates](https://img.shields.io/badge/gates-11%2F11-brightgreen) ![CC̄](https://img.shields.io/badge/CC%CC%84-5.5-brightgreen) ![coverage](https://img.shields.io/badge/coverage-37%25-red) ![vallm](https://img.shields.io/badge/vallm-93%25-brightgreen) ![critical](https://img.shields.io/badge/critical-28-red) ![errors](https://img.shields.io/badge/errors-5-orange) ![MI](https://img.shields.io/badge/MI-0-red) ![ruff](https://img.shields.io/badge/ruff-122-red) ![mypy](https://img.shields.io/badge/mypy-0-brightgreen) ![bandit](https://img.shields.io/badge/bandit-0%20high-brightgreen) ![docstrings](https://img.shields.io/badge/docstrings-0%25-red)\n\u003c!-- pyqual:badges:end --\u003e\n\n\n---\n\n**Declarative quality gate loops for AI-assisted development.**\n\nOne YAML file. One command. Pipeline iterates until your code meets quality thresholds.\n\n```bash\npip install pyqual\npyqual init\npyqual run\n```\n\n## The problem\n\nYou use Copilot, Claude, GPT. They generate code. But nobody checks if that code meets your quality standards before it hits code review. And nobody automatically iterates if it doesn't.\n\npyqual closes that gap: define metrics → run tools → check gates → if fail, LLM fixes → re-check → repeat until pass.\n\n## How it works\n\n```\npyqual.yaml defines everything:\n    ┌─────────────────────────────────────────┐\n    │  metrics:                               │\n    │    cc_max: 15        ← quality gates    │\n    │    vallm_pass_min: 90                   │\n    │    coverage_min: 80                     │\n    │                                         │\n    │  stages:                                │\n    │    - analyze  (code2llm)                │\n    │    - validate (vallm)                   │\n    │    - fix      (llx/aider, when: fail)   │\n    │    - test     (pytest)                  │\n    │                                         │\n    │  loop:                                  │\n    │    max_iterations: 3                    │\n    │    on_fail: report                      │\n    └─────────────────────────────────────────┘\n\npyqual run:\n    Iteration 1 → analyze → validate → fix → test → check gates\n                                                         │\n                                              ┌── PASS ──┴── FAIL ──┐\n                                              │                     │\n                                           Done ✅          Iteration 2...\n```\n\n## pyqual.yaml\n\n```yaml\npipeline:\n  name: quality-loop\n\n  metrics:\n    cc_max: 15           # cyclomatic complexity per function\n    vallm_pass_min: 90   # vallm validation pass rate (%)\n    coverage_min: 80     # test coverage (%)\n\n  stages:\n    - name: analyze\n      run: code2llm ./ -f toon,evolution\n\n    - name: validate\n      run: vallm batch ./ --recursive --errors-json \u003e .pyqual/errors.json\n\n    - name: fix\n      run: llx fix . --errors .pyqual/errors.json\n      when: metrics_fail    # only runs if gates fail\n      timeout: 300          # seconds (optional)\n      optional: false       # if true, failure is allowed\n\n    - name: test\n      run: pytest --cov --cov-report=json:.pyqual/coverage.json\n      when: always          # always | metrics_fail | metrics_pass\n\n  loop:\n    max_iterations: 3\n    on_fail: report         # report | create_ticket | block\n```\n\n### Stage options\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `name` | string | required | Stage identifier |\n| `run` | string | — | Shell command to execute |\n| `tool` | string | — | Built-in tool preset (alternative to `run`) |\n| `when` | string | `always` | `always`, `metrics_fail`, `metrics_pass` |\n| `timeout` | int | 0 | Seconds (0 = no limit) |\n| `optional` | bool | `false` | Allow failure without failing the pipeline (only skips if command doesn't exist) |\n\n### Loop options\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `max_iterations` | int | `3` | Maximum loop count |\n| `on_fail` | string | `report` | `report` — print summary; `create_ticket` — sync TODO.md via planfile; `block` — exit non-zero immediately |\n\n## CLI\n\n### `pyqual init`\n\n```bash\npyqual init [PATH]\n```\n\nCreate `pyqual.yaml` with sensible defaults in `PATH` (default: current directory).\n\n---\n\n### `pyqual run`\n\n```bash\npyqual run [OPTIONS]\n\nOptions:\n  -c, --config PATH     Config file  [default: pyqual.yaml]\n  -w, --workdir PATH    Working directory  [default: .]\n  -n, --dry-run         Preview pipeline without executing stages\n  -v, --verbose         Show live pipeline log to stderr\n```\n\nExecute the full quality-gate loop. Exits non-zero if gates are not met.\n\n```bash\npyqual run\npyqual run --config my-config.yaml --workdir ./src\npyqual run --dry-run          # preview which stages would run\npyqual run --verbose          # show stage output live\n```\n\n---\n\n### `pyqual gates`\n\n```bash\npyqual gates [OPTIONS]\n\nOptions:\n  -c, --config PATH     Config file  [default: pyqual.yaml]\n  -w, --workdir PATH    Working directory  [default: .]\n```\n\nCheck quality gates against currently collected metrics without running any stages. Useful after a manual tool run.\n\n---\n\n### `pyqual tune`\n\n```bash\npyqual tune [OPTIONS]\n\nOptions:\n  -a, --aggressive      More ambitious thresholds (90% of current)\n  -c, --conservative    Safer thresholds with margin (120% of current)\n  -d, --dry-run         Show changes without applying\n  -f, --config PATH     Config file  [default: pyqual.yaml]\n```\n\nAuto-tune quality gate thresholds based on collected metrics from recent pipeline runs. Analyzes current values and suggests optimal thresholds for `cc_max`, `vallm_pass_min`, `coverage_min`, and `secrets_found_max`.\n\n```bash\npyqual tune --dry-run          # Preview suggested changes\npyqual tune --aggressive       # Apply tighter thresholds\npyqual tune --conservative     # Apply safer thresholds\n```\n\n---\n\n### `pyqual status`\n\n```bash\npyqual status [OPTIONS]\n\nOptions:\n  -c, --config PATH     Config file  [default: pyqual.yaml]\n  -w, --workdir PATH    Working directory  [default: .]\n```\n\nShow pipeline config summary and all metrics currently found in `.pyqual/`.\n\n---\n\n### `pyqual logs`\n\n```bash\npyqual logs [OPTIONS]\n\nOptions:\n  -w, --workdir PATH    Working directory  [default: .]\n  -n, --tail INT        Show last N entries (0 = all)  [default: 0]\n  -l, --level TEXT      Filter by event type: stage_done, gate_check,\n                        pipeline_start, pipeline_end\n  -f, --failed          Show only failed stages/gates\n  -j, --json            Raw JSON lines (ideal for LLM/llx consumption)\n      --sql TEXT        Run arbitrary SQL against pipeline.db (advanced)\n```\n\nView structured pipeline logs from `.pyqual/pipeline.db` (written by `nfo` during every run).\n\n```bash\npyqual logs                            # all entries\npyqual logs --tail 20                  # last 20\npyqual logs --failed                   # only failures\npyqual logs --level gate_check         # only gate results\npyqual logs --json --failed            # JSON failures for LLM\npyqual logs --sql \"SELECT * FROM pipeline_logs WHERE level='WARNING'\"\n```\n\n---\n\n### `pyqual bulk-init`\n\n```bash\npyqual bulk-init PATH [OPTIONS]\n\nOptions:\n  -n, --dry-run         Preview without writing files\n      --no-llm          Heuristic classification only (no LLM calls)\n  -m, --model TEXT      Override LLM model for classification\n      --overwrite       Regenerate even if pyqual.yaml already exists\n      --show-schema     Print JSON schema used for LLM classification and exit\n  -j, --json            Output results as JSON\n```\n\nAuto-generate `pyqual.yaml` for every subdirectory in a workspace. Detects project type (Python, Node.js, PHP, Rust, Go, shell, mixed) via LLM classification with JSON schema or heuristic fallback. Never overwrites unless `--overwrite`.\n\n```bash\npyqual bulk-init /path/to/workspace\npyqual bulk-init /path/to/workspace --dry-run\npyqual bulk-init /path/to/workspace --no-llm\npyqual bulk-init /path/to/workspace --overwrite\npyqual bulk-init /path/to/workspace --show-schema\n```\n\n---\n\n### `pyqual bulk-run`\n\n```bash\npyqual bulk-run PATH [OPTIONS]\n\nOptions:\n  -p, --parallel INT    Max concurrent pyqual processes  [default: 4]\n  -n, --dry-run         Pass --dry-run to each project run\n  -t, --timeout INT     Per-project timeout in seconds (0 = no limit)  [default: 0]\n  -f, --filter TEXT     Only run matching project names (repeatable)\n      --no-live         Disable live dashboard, print final summary only\n  -v, --verbose         Show last output line per project in dashboard\n  -j, --json            Output final results as JSON\n```\n\nRun `pyqual` across all projects in a workspace with a real-time dashboard.\n\n```bash\npyqual bulk-run /path/to/workspace\npyqual bulk-run /path/to/workspace --parallel 8\npyqual bulk-run /path/to/workspace --filter mylib --filter webapp\npyqual bulk-run /path/to/workspace --timeout 600\npyqual bulk-run /path/to/workspace --no-live           # CI mode, no dashboard\npyqual bulk-run /path/to/workspace --json              # JSON output\n```\n\n**Live dashboard:**\n\n```\npyqual bulk-run  running:3  pass:12  fail:1  err:0  queue:43  total:59\n┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━┳━━━━━━┓\n┃Project       ┃  Status    ┃ Iter  ┃ Stage    ┃  Progress  ┃ Gates ┃ Time ┃\n┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━╇━━━━━━┩\n│aidesk        │ ✅ passed  │  2/3  │          │   100%     │  2/2  │12.3s │\n│allama        │ 🔄 running  │  1/3  │ validate │ ████░░░░░░ │  0/2  │ 8.5s │\n│blog-pactown  │ ❌ failed  │  3/3  │          │    60%     │  1/2  │45.2s │\n│…             │ ⏳ queued  │       │          │            │       │      │\n└──────────────┴────────────┴───────┴──────────┴────────────┴───────┴──────┘\n```\n\n---\n\n### `pyqual mcp-fix`\n\n```bash\npyqual mcp-fix [OPTIONS]\n\nOptions:\n  -w, --workdir PATH        Project directory on the host  [default: .]\n      --project-path TEXT   Path as seen by the MCP service container\n      --issues PATH         Gate-failure JSON  [default: .pyqual/errors.json]\n      --output PATH         Report output path  [default: .pyqual/llx_mcp.json]\n      --endpoint TEXT       MCP SSE endpoint URL\n      --model TEXT          Override the model selected by llx\n      --file TEXT           File to focus on (repeatable)\n      --use-docker          Run aider inside Docker\n      --docker-arg TEXT     Extra Docker arguments (repeatable)\n      --task TEXT           Analysis task hint for llx  [default: quick_fix]\n      --json                Print full JSON result\n```\n\nRun the llx-backed MCP fix workflow. Reads `.pyqual/errors.json`, sends issues to an MCP SSE service, and writes the result to `.pyqual/llx_mcp.json`.\n\nRequires `pip install pyqual[mcp]`. Set `PYQUAL_LLX_MCP_URL` or pass `--endpoint`.\n\n```bash\npyqual mcp-fix\npyqual mcp-fix --workdir . --project-path /workspace/project\npyqual mcp-fix --issues .pyqual/errors.json --model claude-3-5-sonnet\npyqual mcp-fix --file src/main.py --file src/utils.py\npyqual mcp-fix --json   # machine-readable result\n```\n\n---\n\n### `pyqual mcp-refactor`\n\n```bash\npyqual mcp-refactor [OPTIONS]\n```\n\nSame options as `mcp-fix` (except no `--task`). Runs the refactor workflow instead of the fix workflow.\n\n---\n\n### `pyqual mcp-service`\n\n```bash\npyqual mcp-service [OPTIONS]\n\nOptions:\n  --host TEXT   Host interface to bind to  [default: 0.0.0.0]\n  --port INT    Port to listen on  [default: 8000]\n```\n\nStart the persistent llx MCP SSE service with `/health` and `/metrics` endpoints. Requires `pip install pyqual[mcp]`.\n\n---\n\n### `pyqual tickets`\n\n```bash\npyqual tickets todo    [OPTIONS]   # sync TODO.md via planfile markdown backend\npyqual tickets github  [OPTIONS]   # sync GitHub Issues via planfile GitHub backend\npyqual tickets all     [OPTIONS]   # sync both\n\nCommon options:\n  -w, --workdir PATH    Repository root  [default: .]\n      --dry-run         Preview without changing files\n      --direction TEXT  from | to | both  [default: both]\n```\n\nPlanfile-backed ticket management. When `on_fail: create_ticket` is set, pyqual automatically calls `tickets todo` after a failed run.\n\n```bash\npyqual tickets todo --dry-run                  # preview\npyqual tickets github --direction to           # push to GitHub\npyqual tickets all --direction from            # pull from all backends\n```\n\nRequires `planfile` (included). For GitHub: set `GITHUB_TOKEN`.\n\nSee [`examples/ticket_workflow/`](examples/ticket_workflow/) for a complete example.\n\n---\n\n### `pyqual plugin`\n\n```bash\npyqual plugin list    [--tag TAG]          # list available plugins\npyqual plugin search  QUERY               # search by name/description/tag\npyqual plugin info    NAME                # show details + YAML snippet\npyqual plugin add     NAME [--workdir .]  # append config to pyqual.yaml\npyqual plugin remove  NAME [--workdir .]  # remove config from pyqual.yaml\npyqual plugin validate     [--workdir .]  # check configured plugins\n```\n\nManage built-in metric collector plugins.\n\n```bash\npyqual plugin list\npyqual plugin list --tag security\npyqual plugin search llm\npyqual plugin info llx-mcp-fixer\npyqual plugin add security\npyqual plugin remove llm-bench\n```\n\n**Built-in plugins:**\n\n| Name | Tags | Metrics produced |\n|------|------|-----------------|\n| `llm-bench` | llm, benchmark | `pass_at_1`, `code_bleu`, `ai_generated_pct` |\n| `hallucination` | llm, rag | `faithfulness_score`, `hallucination_rate`, `prompt_token_efficiency` |\n| `sbom` | security, compliance | `sbom_coverage`, `vuln_supply_chain` |\n| `i18n` | i18n, l10n | `i18n_coverage`, `i18n_missing` |\n| `a11y` | accessibility | `a11y_issues`, `a11y_critical` |\n| `repo-metrics` | git, health | `bus_factor`, `commit_frequency`, `contributor_diversity` |\n| `security` | security, secrets | `secrets_found`, `vuln_critical`, `vuln_total` |\n| `llx-mcp-fixer` | mcp, llx, fix | `llx_fix_success`, `llx_fix_returncode`, `llx_tool_calls` |\n\nSee [`examples/custom_plugins/`](examples/custom_plugins/) for building your own.\n\n---\n\n### `pyqual doctor`\n\n```bash\npyqual doctor\n```\n\nCheck availability of all external tools (bandit, mypy, ruff, pylint, flake8, radon, interrogate, vulture, pytest, trufflehog, gitleaks, docker, code2llm, vallm, uvicorn). Prints install commands for missing tools.\n\n---\n\n### `pyqual tools`\n\n```bash\npyqual tools\n```\n\nList all built-in tool presets that can be used as `tool:` shortcuts in pipeline stages (e.g., `tool: ruff`, `tool: pytest`, `tool: code2llm`).\n\n## Python API\n\n```python\nfrom pyqual import Pipeline, PyqualConfig\n\nconfig = PyqualConfig.load(\"pyqual.yaml\")\npipeline = Pipeline(config, workdir=\"./my-project\")\nresult = pipeline.run()\n\nif result.final_passed:\n    print(f\"All gates passed in {result.iteration_count} iterations\")\nelse:\n    print(\"Gates not met — check result.iterations for details\")\n    for it in result.iterations:\n        for gate in it.gates:\n            print(f\"  {gate}\")\n```\n\nCheck gates without running stages:\n\n```python\nfrom pyqual import PyqualConfig, GateSet\n\nconfig = PyqualConfig.load(\"pyqual.yaml\")\nresults = GateSet(config.gates).check_all()\nfor r in results:\n    print(\"✅\" if r.passed else \"❌\", r)\n```\n\nSee [`examples/basic/`](examples/basic/) for more API patterns.\n\n## LLM Integration\n\npyqual includes built-in LLM support via [liteLLM](https://litellm.ai/). Configure via `.env`:\n\nThe convenience wrapper lives upstream in `llx.llm`; `pyqual` re-exports it so existing imports keep working.\n\n```bash\nOPENROUTER_API_KEY=sk-or-v1-...\nLLM_MODEL=openrouter/qwen/qwen3-coder-next\n```\n\n```python\nfrom pyqual import get_llm\n\nllm = get_llm()\nresponse = llm.complete(\"Explain Python decorators\")\nprint(response.content)\nprint(f\"Cost: ${response.cost:.4f}\")\n```\n\nSee [`examples/llx/`](examples/llx/) for a full LLM-backed pipeline.\n\n## Docker-backed MCP fixer/refactor\n\nThe MCP client, service, workflow orchestration (`LlxMcpRunResult`, `run_llx_fix_workflow`, `run_llx_refactor_workflow`), issue parsing, and prompt building live in the upstream `llx` package (≥ 0.1.47). pyqual re-exports them for backward compatibility. Install `pyqual[mcp]` to enable.\n\n```bash\ndocker compose -f examples/llm_fix/docker-compose.yml up --build -d\npyqual plugin add llx-mcp-fixer\npyqual run\n```\n\nThe plugin writes results to `.pyqual/llx_mcp.json`, gated via `llx_fix_*` metrics.\n\n```bash\n# Run workflows directly\npyqual mcp-fix --workdir . --project-path /workspace/project\npyqual mcp-refactor --workdir . --project-path /workspace/project\n\n# Run standalone service\npyqual mcp-service --host 0.0.0.0 --port 8000\n```\n\nSet `PYQUAL_LLX_MCP_URL` to point clients at the service. See [`examples/llm_fix/`](examples/llm_fix/) for a complete Docker Compose setup.\n\n## Metric sources\n\npyqual automatically collects from `.toon` files and `.pyqual/` artifacts:\n\n| Source | File | Metrics |\n|--------|------|---------|\n| **Analysis** | `project/analysis.toon.yaml` | `cc` (CC̄), `critical` |\n| **Validation** | `project/validation.toon.yaml` | `vallm_pass` |\n| | `.pyqual/errors.json` | `error_count` |\n| **Coverage** | `.pyqual/coverage.json` | `coverage` |\n| **Performance** | `.pyqual/asv.json` | `bench_regression`, `bench_time` |\n| | `.pyqual/mem.json` | `mem_usage`, `cpu_time` |\n| **Security** | `.pyqual/bandit.json` | `bandit_high`, `bandit_medium`, `bandit_low` |\n| | `.pyqual/secrets.json` | `secrets_severity`, `secrets_count` |\n| | `.pyqual/vulns.json` | `vuln_critical`, `vuln_count` |\n| | `.pyqual/sbom.json` | `sbom_compliance`, `license_blacklist` |\n| **Project health** | `.pyqual/vulture.json` | `unused_count` |\n| | `.pyqual/pyroma.json` | `pyroma_score` |\n| | `.pyqual/git_metrics.json` | `git_branch_age`, `todo_count` |\n| **LLM / AI** | `.pyqual/humaneval.json` | `llm_pass_rate` |\n| | `.pyqual/llm_analysis.json` | `llm_cc`, `hallucination_rate`, `prompt_bias_score`, `agent_efficiency` |\n| | `.pyqual/llx_mcp.json` | `llx_fix_success`, `llx_fix_returncode`, `llx_tool_calls`, `llx_fix_tier_rank` |\n| | `.pyqual/costs.json` | `ai_cost` |\n| **Linting** | `.pyqual/ruff.json` | `ruff_errors`, `ruff_fatal`, `ruff_warnings` |\n| | `.pyqual/pylint.json` | `pylint_errors`, `pylint_fatal`, `pylint_error`, `pylint_warnings`, `pylint_score` |\n| | `.pyqual/flake8.json` | `flake8_violations`, `flake8_errors`, `flake8_warnings`, `flake8_conventions` |\n| | `.pyqual/mypy.json` | `mypy_errors` |\n| **Documentation** | `.pyqual/interrogate.json` | `docstring_coverage`, `docstring_total`, `docstring_missing` |\n\nCustom metrics: subclass `MetricCollector` and register with `PluginRegistry` — see [`examples/custom_plugins/`](examples/custom_plugins/).\n\n## Gate operators\n\nMetric key suffixes translate to comparison operators:\n\n```yaml\nmetrics:\n  cc_max: 15           # cc ≤ 15\n  coverage_min: 80     # coverage ≥ 80\n  critical_max: 0      # critical ≤ 0\n  error_count_max: 5   # error_count ≤ 5\n  vallm_pass_min: 90   # vallm_pass ≥ 90\n  mypy_errors_eq: 0    # mypy_errors = 0\n```\n\n| Suffix | Operator |\n|--------|----------|\n| `_max` | ≤ |\n| `_min` | ≥ |\n| `_lt` | \u003c |\n| `_gt` | \u003e |\n| `_eq` | = |\n\n## Integration with ecosystem\n\npyqual orchestrates — it does not implement the analysis tools:\n\n- **[code2llm](https://github.com/wronai/code2llm)** — code analysis → pyqual reads `.toon` output\n- **[vallm](https://github.com/wronai/vallm)** — AI validation → pyqual reads pass rates\n- **[llx](https://github.com/wronai/llx)** — LLM routing, MCP workflows, issue parsing (requires Python ≥ 3.10)\n- **[planfile](https://github.com/wronai/planfile)** — ticket management → pyqual syncs TODO.md and GitHub Issues\n- **costs** — AI spend tracking → pyqual can gate on `ai_cost`\n- **algitex** — imports pyqual as a dependency for its `go` command\n\n## Examples\n\nSee [`examples/`](examples/) for ready-to-use configurations.\n\n### Basics\n\n| Example | Description |\n|---------|-------------|\n| [`basic/`](examples/basic/) | Python API — Pipeline, GateSet, minimal one-liner |\n| [`python-package/`](examples/python-package/) | Standard Python package (src-layout) |\n| [`python-flat/`](examples/python-flat/) | Flat project without src/ |\n| [`monorepo/`](examples/monorepo/) | Multiple packages in one repository |\n\n### Quality \u0026 Linting\n\n| Example | Tools |\n|---------|-------|\n| [`linters/`](examples/linters/) | ruff, pylint, flake8, mypy, interrogate |\n| [`security/`](examples/security/) | bandit, pip-audit, trufflehog, gitleaks, sbom |\n| [`custom_gates/`](examples/custom_gates/) | Dynamic thresholds, composite scoring |\n| [`custom_plugins/`](examples/custom_plugins/) | Build your own `MetricCollector` plugins |\n\n### AI \u0026 LLM\n\n| Example | Description |\n|---------|-------------|\n| [`llx/`](examples/llx/) | Standalone llx integration — model auto-selection |\n| [`llm_fix/`](examples/llm_fix/) | Docker-backed llx MCP fix workflow |\n\n### CI/CD\n\n| Example | Platform |\n|---------|----------|\n| [`github-actions/`](examples/github-actions/) | GitHub Actions — PR checks, artifacts, coverage |\n| [`gitlab-ci/`](examples/gitlab-ci/) | GitLab CI — coverage reports, caching |\n\n### Advanced\n\n| Example | Key Feature |\n|---------|-------------|\n| [`multi_gate_pipeline/`](examples/multi_gate_pipeline/) | 21-gate production pipeline (linters + security + AI + testing) |\n| [`ticket_workflow/`](examples/ticket_workflow/) | Auto TODO.md + GitHub Issues on gate failure |\n| [`project_analysis/`](examples/project_analysis/) | Gating on code2llm / toon analysis metrics |\n\n## Why not add this to algitex?\n\nalgitex has 29,448 lines, CC̄=3.6, 64 critical issues, vallm pass 42.8%. Adding more features makes it worse. pyqual does one thing well: declarative quality gate loops. algitex imports pyqual. Both improve.\n\n## License\n\nLicensed under Apache-2.0.\n## Status\n\n_Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 13:45 UTC_\n\n| Metric | Value |\n|---|---|\n| HEAD | `4fe6df6` |\n| Coverage | — |\n| Failing tests | — |\n| Commits in last cycle | 50 |\n\n\u003e Refactors to the documentation and code analysis engine, addition of configuration management docs, numerous automated TODO batch fixes, and targeted refactors to reduce complexity in high-CC functions.\n\n\u003c!-- taskill:status:end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemcod%2Fpyqual","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsemcod%2Fpyqual","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemcod%2Fpyqual/lists"}