quor 0.1.0__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.
- quor-0.1.0/.github/workflows/canary.yml +108 -0
- quor-0.1.0/.github/workflows/ci.yml +43 -0
- quor-0.1.0/.github/workflows/publish-testpypi.yml +84 -0
- quor-0.1.0/.github/workflows/release.yml +101 -0
- quor-0.1.0/.gitignore +46 -0
- quor-0.1.0/CHANGELOG.md +111 -0
- quor-0.1.0/CODE_OF_CONDUCT.md +69 -0
- quor-0.1.0/CONTRIBUTING.md +372 -0
- quor-0.1.0/LICENSE +201 -0
- quor-0.1.0/PKG-INFO +259 -0
- quor-0.1.0/README.md +221 -0
- quor-0.1.0/SECURITY.md +65 -0
- quor-0.1.0/docs/archive/architecture-exploration/engineering-patterns.md +1690 -0
- quor-0.1.0/docs/archive/product-discovery/competitive-research.md +943 -0
- quor-0.1.0/docs/archive/product-discovery/final-discovery.md +1726 -0
- quor-0.1.0/docs/archive/research/design-review.md +913 -0
- quor-0.1.0/docs/archive/research/zap-analysis.md +1955 -0
- quor-0.1.0/docs/final/ANTI_GOALS.md +166 -0
- quor-0.1.0/docs/final/CLAUDE.md +310 -0
- quor-0.1.0/docs/final/CONTRIBUTING.md +5 -0
- quor-0.1.0/docs/final/DECISIONS.md +742 -0
- quor-0.1.0/docs/final/IMPLEMENTATION_PLAN.md +509 -0
- quor-0.1.0/docs/final/PROJECT_BIBLE.md +512 -0
- quor-0.1.0/docs/final/PROJECT_STATUS.md +374 -0
- quor-0.1.0/docs/final/RELEASE_CRITERIA.md +181 -0
- quor-0.1.0/docs/final/RESEARCH_COMPLETION.md +185 -0
- quor-0.1.0/docs/final/ROADMAP.md +161 -0
- quor-0.1.0/pyproject.toml +130 -0
- quor-0.1.0/quor/__init__.py +1 -0
- quor-0.1.0/quor/__main__.py +104 -0
- quor-0.1.0/quor/adapters/__init__.py +0 -0
- quor-0.1.0/quor/adapters/base.py +39 -0
- quor-0.1.0/quor/adapters/claude.py +65 -0
- quor-0.1.0/quor/adapters/dispatcher.py +212 -0
- quor-0.1.0/quor/cli/__init__.py +0 -0
- quor-0.1.0/quor/cli/commands/__init__.py +0 -0
- quor-0.1.0/quor/cli/commands/doctor.py +190 -0
- quor-0.1.0/quor/cli/commands/explain.py +84 -0
- quor-0.1.0/quor/cli/commands/gain.py +54 -0
- quor-0.1.0/quor/cli/commands/init.py +210 -0
- quor-0.1.0/quor/cli/commands/validate.py +55 -0
- quor-0.1.0/quor/cli/commands/verify.py +52 -0
- quor-0.1.0/quor/cli/main.py +58 -0
- quor-0.1.0/quor/config/__init__.py +0 -0
- quor-0.1.0/quor/config/loader.py +43 -0
- quor-0.1.0/quor/config/model.py +60 -0
- quor-0.1.0/quor/errors.py +69 -0
- quor-0.1.0/quor/filters/__init__.py +0 -0
- quor-0.1.0/quor/filters/builtin/__init__.py +0 -0
- quor-0.1.0/quor/filters/builtin/build.toml +89 -0
- quor-0.1.0/quor/filters/builtin/cat.toml +50 -0
- quor-0.1.0/quor/filters/builtin/git.toml +131 -0
- quor-0.1.0/quor/filters/builtin/pytest.toml +59 -0
- quor-0.1.0/quor/filters/builtin/z_generic.toml +34 -0
- quor-0.1.0/quor/filters/loader.py +32 -0
- quor-0.1.0/quor/filters/registry.py +278 -0
- quor-0.1.0/quor/filters/trust.py +19 -0
- quor-0.1.0/quor/pipeline/__init__.py +0 -0
- quor-0.1.0/quor/pipeline/content_type.py +69 -0
- quor-0.1.0/quor/pipeline/engine.py +151 -0
- quor-0.1.0/quor/pipeline/mask.py +57 -0
- quor-0.1.0/quor/pipeline/plugin_loader.py +517 -0
- quor-0.1.0/quor/pipeline/stages/__init__.py +0 -0
- quor-0.1.0/quor/pipeline/stages/_utils.py +48 -0
- quor-0.1.0/quor/pipeline/stages/base.py +88 -0
- quor-0.1.0/quor/pipeline/stages/deduplicate_consecutive.py +68 -0
- quor-0.1.0/quor/pipeline/stages/group_repeated.py +162 -0
- quor-0.1.0/quor/pipeline/stages/max_tokens.py +126 -0
- quor-0.1.0/quor/pipeline/stages/remove_ansi.py +71 -0
- quor-0.1.0/quor/pipeline/stages/strip_lines.py +70 -0
- quor-0.1.0/quor/plugins/__init__.py +61 -0
- quor-0.1.0/quor/plugins/base.py +473 -0
- quor-0.1.0/quor/plugins/registry.py +349 -0
- quor-0.1.0/quor/py.typed +0 -0
- quor-0.1.0/quor/rewrite/__init__.py +0 -0
- quor-0.1.0/quor/rewrite/classifier.py +222 -0
- quor-0.1.0/quor/rewrite/lexer.py +198 -0
- quor-0.1.0/quor/rewrite/rules.py +145 -0
- quor-0.1.0/quor/tracking/__init__.py +0 -0
- quor-0.1.0/quor/tracking/db.py +310 -0
- quor-0.1.0/quor/tracking/schema.sql +28 -0
- quor-0.1.0/tests/conftest.py +24 -0
- quor-0.1.0/tests/fixtures/commands/compound.toml +82 -0
- quor-0.1.0/tests/fixtures/commands/env_prefix.toml +67 -0
- quor-0.1.0/tests/fixtures/commands/exclusions.toml +99 -0
- quor-0.1.0/tests/fixtures/commands/simple.toml +151 -0
- quor-0.1.0/tests/fixtures/commands/transparent_prefix.toml +80 -0
- quor-0.1.0/tests/fixtures/test_plugin/pyproject.toml +15 -0
- quor-0.1.0/tests/fixtures/test_plugin/quor_test_stage/__init__.py +0 -0
- quor-0.1.0/tests/fixtures/test_plugin/quor_test_stage/stage.py +21 -0
- quor-0.1.0/tests/integration/__init__.py +0 -0
- quor-0.1.0/tests/unit/__init__.py +0 -0
- quor-0.1.0/tests/unit/test_adapters.py +469 -0
- quor-0.1.0/tests/unit/test_cli.py +566 -0
- quor-0.1.0/tests/unit/test_fail_open.py +454 -0
- quor-0.1.0/tests/unit/test_filter_safety.py +507 -0
- quor-0.1.0/tests/unit/test_filters.py +483 -0
- quor-0.1.0/tests/unit/test_pipeline.py +486 -0
- quor-0.1.0/tests/unit/test_plugin_loader.py +701 -0
- quor-0.1.0/tests/unit/test_plugins.py +858 -0
- quor-0.1.0/tests/unit/test_rewrite.py +449 -0
- quor-0.1.0/tests/unit/test_stages.py +509 -0
- quor-0.1.0/tests/unit/test_tracking.py +602 -0
- quor-0.1.0/tests/unit/test_version.py +47 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
name: Canary — Claude Code Compatibility
|
|
2
|
+
|
|
3
|
+
# Runs weekly to catch Anthropic changing the Claude Code hook format before
|
|
4
|
+
# users do. Also triggerable manually for immediate spot-checks.
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: '0 8 * * 1' # Every Monday at 08:00 UTC
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
canary:
|
|
16
|
+
name: Hook compatibility — Claude Code latest
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python 3.11
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: '3.11'
|
|
26
|
+
|
|
27
|
+
- name: Set up Node.js (for Claude Code)
|
|
28
|
+
uses: actions/setup-node@v4
|
|
29
|
+
with:
|
|
30
|
+
node-version: 'lts/*'
|
|
31
|
+
|
|
32
|
+
- name: Install Claude Code (latest, unpinned)
|
|
33
|
+
# Not pinned intentionally — the canary exists to catch Anthropic
|
|
34
|
+
# changing hook format in a new release before our users hit it.
|
|
35
|
+
run: npm install -g @anthropic-ai/claude-code
|
|
36
|
+
|
|
37
|
+
- name: Record Claude Code version
|
|
38
|
+
run: |
|
|
39
|
+
echo "## Claude Code version" >> $GITHUB_STEP_SUMMARY
|
|
40
|
+
npm ls -g @anthropic-ai/claude-code --json 2>/dev/null \
|
|
41
|
+
| python3 -c "import sys,json; d=json.load(sys.stdin); print(list(d.get('dependencies',{}).values())[0].get('version','unknown'))" \
|
|
42
|
+
|| echo "version unavailable" >> $GITHUB_STEP_SUMMARY
|
|
43
|
+
|
|
44
|
+
- name: Install Quor from source
|
|
45
|
+
run: pip install -e ".[dev]"
|
|
46
|
+
|
|
47
|
+
- name: Install test plugin fixture
|
|
48
|
+
run: pip install -e ./tests/fixtures/test_plugin
|
|
49
|
+
|
|
50
|
+
- name: Verify hook responds to current Claude Code PreToolUse format
|
|
51
|
+
# Send the JSON format Claude Code actually sends via PreToolUse hook,
|
|
52
|
+
# verify the response is valid JSON with the expected command rewrite.
|
|
53
|
+
run: |
|
|
54
|
+
PAYLOAD='{"tool_name":"Bash","tool_input":{"command":"git status"}}'
|
|
55
|
+
RESULT=$(echo "$PAYLOAD" | python -m quor hook claude)
|
|
56
|
+
echo "Hook response: $RESULT"
|
|
57
|
+
|
|
58
|
+
# Verify: response is valid JSON and command is rewritten
|
|
59
|
+
echo "$RESULT" | python3 - <<'EOF'
|
|
60
|
+
import sys, json
|
|
61
|
+
try:
|
|
62
|
+
d = json.load(sys.stdin)
|
|
63
|
+
except json.JSONDecodeError as e:
|
|
64
|
+
print(f"FAIL: hook response is not valid JSON: {e}", file=sys.stderr)
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
cmd = d.get("tool_input", {}).get("command", "")
|
|
67
|
+
if "quor git status" not in cmd:
|
|
68
|
+
print(f"FAIL: expected 'quor git status' in command, got {cmd!r}", file=sys.stderr)
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
print(f"OK: command rewritten to {cmd!r}")
|
|
71
|
+
EOF
|
|
72
|
+
|
|
73
|
+
- name: Verify hook preserves extra JSON fields
|
|
74
|
+
# Claude Code may add extra fields to the payload; Quor must not drop them.
|
|
75
|
+
run: |
|
|
76
|
+
PAYLOAD='{"tool_name":"Bash","tool_input":{"command":"git status"},"session_id":"abc123","extra_field":"preserved"}'
|
|
77
|
+
RESULT=$(echo "$PAYLOAD" | python -m quor hook claude)
|
|
78
|
+
echo "$RESULT" | python3 - <<'EOF'
|
|
79
|
+
import sys, json
|
|
80
|
+
d = json.load(sys.stdin)
|
|
81
|
+
if d.get("session_id") != "abc123":
|
|
82
|
+
print("FAIL: extra fields were dropped by the hook", file=sys.stderr)
|
|
83
|
+
sys.exit(1)
|
|
84
|
+
if d.get("extra_field") != "preserved":
|
|
85
|
+
print("FAIL: extra_field was dropped by the hook", file=sys.stderr)
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
print("OK: extra JSON fields preserved")
|
|
88
|
+
EOF
|
|
89
|
+
|
|
90
|
+
- name: Verify quor init --claude writes valid settings.json
|
|
91
|
+
run: |
|
|
92
|
+
mkdir -p /tmp/canary-claude
|
|
93
|
+
python -m quor init --claude --yes --settings-path /tmp/canary-claude/settings.json
|
|
94
|
+
python3 -c "
|
|
95
|
+
import json, sys
|
|
96
|
+
with open('/tmp/canary-claude/settings.json') as f:
|
|
97
|
+
d = json.load(f)
|
|
98
|
+
hooks = d.get('hooks', {}).get('PreToolUse', [])
|
|
99
|
+
if not any('claude-hook.ps1' in str(h) for entry in hooks for h in entry.get('hooks', [])):
|
|
100
|
+
print('FAIL: hook not found in settings.json', file=sys.stderr)
|
|
101
|
+
sys.exit(1)
|
|
102
|
+
print('OK: hook registered in settings.json')
|
|
103
|
+
"
|
|
104
|
+
|
|
105
|
+
- name: Run full test suite
|
|
106
|
+
# The canary also proves Quor's test suite passes against the latest
|
|
107
|
+
# Claude Code version (package installed above).
|
|
108
|
+
run: pytest tests/ -q --tb=short -x
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: Python ${{ matrix.python-version }} / ${{ matrix.os }}
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
os: [ubuntu-latest, windows-latest]
|
|
20
|
+
python-version: ["3.11", "3.12"]
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
26
|
+
uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
|
|
30
|
+
- name: Install project with dev dependencies
|
|
31
|
+
run: pip install -e ".[dev]"
|
|
32
|
+
|
|
33
|
+
- name: Install test plugin fixture
|
|
34
|
+
run: pip install -e ./tests/fixtures/test_plugin
|
|
35
|
+
|
|
36
|
+
- name: Lint — ruff
|
|
37
|
+
run: ruff check quor/ tests/
|
|
38
|
+
|
|
39
|
+
- name: Type check — mypy
|
|
40
|
+
run: mypy quor/
|
|
41
|
+
|
|
42
|
+
- name: Tests
|
|
43
|
+
run: pytest tests/ --cov=quor --cov-report=term-missing
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: Publish to TestPyPI
|
|
2
|
+
|
|
3
|
+
# Manual dry-run publish used to validate packaging/publishing before tagging
|
|
4
|
+
# a real release. Trigger from the Actions tab, supplying the version you
|
|
5
|
+
# expect to publish (must match pyproject.toml).
|
|
6
|
+
#
|
|
7
|
+
# Mirrors release.yml's build/verify/publish structure as closely as possible
|
|
8
|
+
# so a TestPyPI dry run exercises the same path the real release will use.
|
|
9
|
+
# Independent of release.yml — does not touch the tag-triggered real-PyPI
|
|
10
|
+
# release path.
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
inputs:
|
|
15
|
+
version:
|
|
16
|
+
description: 'Expected version to publish (must match pyproject.toml, e.g. 0.1.0)'
|
|
17
|
+
required: true
|
|
18
|
+
type: string
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
name: Build and verify distribution
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
|
|
30
|
+
- name: Set up Python
|
|
31
|
+
uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: '3.11'
|
|
34
|
+
|
|
35
|
+
- name: Install build tooling
|
|
36
|
+
run: pip install build twine
|
|
37
|
+
|
|
38
|
+
- name: Build wheel and sdist
|
|
39
|
+
run: python -m build
|
|
40
|
+
|
|
41
|
+
- name: Verify distribution metadata
|
|
42
|
+
run: twine check dist/*
|
|
43
|
+
|
|
44
|
+
- name: Verify input version matches package version
|
|
45
|
+
run: |
|
|
46
|
+
INPUT_VERSION="${{ inputs.version }}"
|
|
47
|
+
PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
48
|
+
if [ "$INPUT_VERSION" != "$PKG_VERSION" ]; then
|
|
49
|
+
echo "::error::Input version ($INPUT_VERSION) does not match pyproject.toml version ($PKG_VERSION)"
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
echo "Input version $INPUT_VERSION matches package version $PKG_VERSION"
|
|
53
|
+
|
|
54
|
+
- name: Upload build artifacts
|
|
55
|
+
uses: actions/upload-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: dist
|
|
58
|
+
path: dist/
|
|
59
|
+
retention-days: 30
|
|
60
|
+
|
|
61
|
+
publish-testpypi:
|
|
62
|
+
name: Publish to TestPyPI
|
|
63
|
+
needs: build
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
environment: testpypi
|
|
66
|
+
steps:
|
|
67
|
+
- name: Download build artifacts
|
|
68
|
+
uses: actions/download-artifact@v4
|
|
69
|
+
with:
|
|
70
|
+
name: dist
|
|
71
|
+
path: dist/
|
|
72
|
+
|
|
73
|
+
- name: Check TestPyPI token is configured
|
|
74
|
+
run: |
|
|
75
|
+
if [ -z "${{ secrets.TEST_PYPI_API_TOKEN }}" ]; then
|
|
76
|
+
echo "::error::TEST_PYPI_API_TOKEN is not set for the 'testpypi' environment. Configure it under Settings > Environments > testpypi before this job can publish."
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
- name: Publish to TestPyPI
|
|
81
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
82
|
+
with:
|
|
83
|
+
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
|
84
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Triggered by pushing a version tag, e.g.: git tag v0.1.0 && git push origin v0.1.0
|
|
4
|
+
#
|
|
5
|
+
# Builds wheel + sdist, verifies them, attaches them to a GitHub Release,
|
|
6
|
+
# and (only if a PYPI_API_TOKEN secret is configured for the `pypi` GitHub
|
|
7
|
+
# environment) publishes to PyPI. Without that secret configured, the
|
|
8
|
+
# publish job runs and fails fast at the "Check PyPI token is configured"
|
|
9
|
+
# step rather than silently skipping — so a maintainer always gets a clear
|
|
10
|
+
# signal instead of a false "success".
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags:
|
|
15
|
+
- 'v*'
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build:
|
|
22
|
+
name: Build and verify distribution
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: '3.11'
|
|
31
|
+
|
|
32
|
+
- name: Install build tooling
|
|
33
|
+
run: pip install build twine
|
|
34
|
+
|
|
35
|
+
- name: Build wheel and sdist
|
|
36
|
+
run: python -m build
|
|
37
|
+
|
|
38
|
+
- name: Verify distribution metadata
|
|
39
|
+
run: twine check dist/*
|
|
40
|
+
|
|
41
|
+
- name: Verify tag matches package version
|
|
42
|
+
run: |
|
|
43
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
44
|
+
PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
45
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
46
|
+
echo "::error::Tag version ($TAG_VERSION) does not match pyproject.toml version ($PKG_VERSION)"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
echo "Tag $GITHUB_REF_NAME matches package version $PKG_VERSION"
|
|
50
|
+
|
|
51
|
+
- name: Upload build artifacts
|
|
52
|
+
uses: actions/upload-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: dist
|
|
55
|
+
path: dist/
|
|
56
|
+
retention-days: 30
|
|
57
|
+
|
|
58
|
+
github-release:
|
|
59
|
+
name: Create GitHub Release
|
|
60
|
+
needs: build
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- name: Download build artifacts
|
|
66
|
+
uses: actions/download-artifact@v4
|
|
67
|
+
with:
|
|
68
|
+
name: dist
|
|
69
|
+
path: dist/
|
|
70
|
+
|
|
71
|
+
- name: Create GitHub Release
|
|
72
|
+
uses: softprops/action-gh-release@v2
|
|
73
|
+
with:
|
|
74
|
+
files: dist/*
|
|
75
|
+
generate_release_notes: true
|
|
76
|
+
draft: false
|
|
77
|
+
prerelease: false
|
|
78
|
+
|
|
79
|
+
publish-pypi:
|
|
80
|
+
name: Publish to PyPI
|
|
81
|
+
needs: build
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
environment: pypi
|
|
84
|
+
steps:
|
|
85
|
+
- name: Download build artifacts
|
|
86
|
+
uses: actions/download-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: dist
|
|
89
|
+
path: dist/
|
|
90
|
+
|
|
91
|
+
- name: Check PyPI token is configured
|
|
92
|
+
run: |
|
|
93
|
+
if [ -z "${{ secrets.PYPI_API_TOKEN }}" ]; then
|
|
94
|
+
echo "::error::PYPI_API_TOKEN is not set for the 'pypi' environment. Configure it under Settings > Environments > pypi before this job can publish. Skipping publish for this run."
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
- name: Publish to PyPI
|
|
99
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
100
|
+
with:
|
|
101
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
quor-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.so
|
|
6
|
+
*.egg
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.eggs/
|
|
11
|
+
MANIFEST
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
ENV/
|
|
18
|
+
|
|
19
|
+
# uv
|
|
20
|
+
.python-version
|
|
21
|
+
|
|
22
|
+
# Testing
|
|
23
|
+
.pytest_cache/
|
|
24
|
+
.coverage
|
|
25
|
+
.coverage.*
|
|
26
|
+
htmlcov/
|
|
27
|
+
coverage.xml
|
|
28
|
+
|
|
29
|
+
# Type checking
|
|
30
|
+
.mypy_cache/
|
|
31
|
+
.ruff_cache/
|
|
32
|
+
|
|
33
|
+
# Distribution / packaging
|
|
34
|
+
*.whl
|
|
35
|
+
*.tar.gz
|
|
36
|
+
|
|
37
|
+
# IDE
|
|
38
|
+
.vscode/
|
|
39
|
+
.idea/
|
|
40
|
+
*.swp
|
|
41
|
+
*.swo
|
|
42
|
+
*~
|
|
43
|
+
|
|
44
|
+
# OS
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
quor-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Quor are documented here. Format loosely follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] — Internal Alpha
|
|
7
|
+
|
|
8
|
+
First public-quality release. Quor is a rule-based command-output
|
|
9
|
+
optimization and context-compression layer for AI coding assistants: it
|
|
10
|
+
runs your command, captures the output, and applies a deterministic,
|
|
11
|
+
fail-open filtering pipeline before the output reaches the assistant's
|
|
12
|
+
context window.
|
|
13
|
+
|
|
14
|
+
### Core pipeline (Phases 0-6)
|
|
15
|
+
|
|
16
|
+
- **ContentMask pipeline** — the `KEEP` / `COMPRESS` / `PROTECT` line-level
|
|
17
|
+
decision model that every compression stage operates on. `PROTECT` is
|
|
18
|
+
immutable once set: no later stage can downgrade it (ADR-003, "Core
|
|
19
|
+
Abstraction — ContentMask").
|
|
20
|
+
- **Five built-in compression stages** — `remove_ansi`, `strip_lines`,
|
|
21
|
+
`deduplicate_consecutive`, `group_repeated`, `max_tokens`.
|
|
22
|
+
- **Five built-in filters** — `git`, `pytest`, `build` (mypy/ruff), `cat`,
|
|
23
|
+
and a generic ANSI+truncation fallback, each with inline TOML tests.
|
|
24
|
+
- **Three-tier filter registry** — project > user > built-in precedence
|
|
25
|
+
(ADR "Filter Registry — Three-Tier Lookup").
|
|
26
|
+
- **Command rewriter and classifier** — quote-aware shell lexer, rule-based
|
|
27
|
+
command classification, 100+ fixture-driven tests.
|
|
28
|
+
- **Claude Code hook adapter** — intercepts the `PreToolUse` hook, rewrites
|
|
29
|
+
the command to route through Quor, preserves every extra JSON field
|
|
30
|
+
Claude Code sends.
|
|
31
|
+
- **SQLite + JSONL dual tracking** — background-thread writes, WAL mode,
|
|
32
|
+
never blocks the hook path (ADR "Persistence — Dual (SQLite + JSONL)").
|
|
33
|
+
- **Six CLI commands** — `init --claude`, `validate`, `explain`, `gain`,
|
|
34
|
+
`verify`, `doctor` — plus the `schema` utility command for the filter
|
|
35
|
+
JSON Schema. Both `quor` and `qr` are registered entry points.
|
|
36
|
+
|
|
37
|
+
### Plugin Infrastructure (Phase 8)
|
|
38
|
+
|
|
39
|
+
- Public `Plugin` Protocol (`quor.plugins.base`) — `@runtime_checkable`,
|
|
40
|
+
versioned via `QUOR_PLUGIN_API_VERSION`, lifecycle-managed
|
|
41
|
+
(`initialize` / `execute` / `shutdown`).
|
|
42
|
+
- `PluginRegistry` — three-tier registration (project > user > builtin),
|
|
43
|
+
deterministic execution order, fully fail-open execution.
|
|
44
|
+
- Deliberately kept separate from the existing `StageHandler` Protocol:
|
|
45
|
+
`StageHandler` is TOML-configurable, line-level, stateless compression;
|
|
46
|
+
`Plugin` is Python-coded, lifecycle-managed middleware for telemetry,
|
|
47
|
+
policy, and routing (ADR "Plugin Architecture — Two-Tier Separation").
|
|
48
|
+
|
|
49
|
+
### Plugin Discovery & Loading (Phase 9)
|
|
50
|
+
|
|
51
|
+
- Entry-point discovery for both `quor.compression_stage` and `quor.plugin`
|
|
52
|
+
groups via `importlib.metadata`, with a package-set-hash-invalidated
|
|
53
|
+
local cache.
|
|
54
|
+
- `api_version` compatibility check accepts any version `<= QUOR_PLUGIN_API_VERSION`
|
|
55
|
+
and rejects only newer ones — plugins built against an older API keep
|
|
56
|
+
working as the API evolves.
|
|
57
|
+
- `file://` escape hatch for loading a local `StageHandler` during
|
|
58
|
+
development without packaging it.
|
|
59
|
+
- `quor doctor` plugin diagnostics: lists discovered stages and plugins
|
|
60
|
+
(including each plugin's declared version), and flags load failures.
|
|
61
|
+
Tier is deliberately not reported for entry-point-discovered plugins —
|
|
62
|
+
`importlib.metadata` carries no signal that maps to project/user/builtin,
|
|
63
|
+
so this is a documented scope boundary, not a bug (see DECISIONS.md,
|
|
64
|
+
ADR "Plugin Architecture — Two-Tier Separation").
|
|
65
|
+
- End-to-end fail-open verification: a real (non-mock) plugin that raises
|
|
66
|
+
during `execute()` is driven through the actual dispatcher, confirming
|
|
67
|
+
the exception is isolated, a warning is emitted, and the hook still
|
|
68
|
+
returns valid output.
|
|
69
|
+
|
|
70
|
+
### Release Hardening
|
|
71
|
+
|
|
72
|
+
A dedicated pass to close reliability gaps before packaging:
|
|
73
|
+
|
|
74
|
+
- Eliminated the last local-machine dependency in the test suite (CLI
|
|
75
|
+
tests previously read the developer's real `~/.claude/settings.json`);
|
|
76
|
+
all tests now inject an isolated settings path.
|
|
77
|
+
- `ruff` and `mypy` are exact-pinned in dev dependencies; `pytest`/`pytest-cov`
|
|
78
|
+
use bounded ranges. CI now lints `tests/` as well as `quor/` (ADR
|
|
79
|
+
"Release Hardening — Dev Tooling Version Policy & CI Lint Scope").
|
|
80
|
+
- Removed `ExitCode.PLUGIN_ERROR` as dead code — every `PluginError` is
|
|
81
|
+
caught internally by Quor's fail-open contract and never reaches a
|
|
82
|
+
process exit code.
|
|
83
|
+
- Python compatibility verified by actually running the full suite (ruff,
|
|
84
|
+
mypy, pytest) on Python 3.11, 3.13, and 3.14 in isolated virtual
|
|
85
|
+
environments, not just static review.
|
|
86
|
+
|
|
87
|
+
### Testing
|
|
88
|
+
|
|
89
|
+
- **605 tests passing**, `ruff` and `mypy` clean on both `quor/` and
|
|
90
|
+
`tests/`.
|
|
91
|
+
- ≥80% coverage on `quor/pipeline/`, `quor/filters/`, and `quor/rewrite/`
|
|
92
|
+
(93% overall).
|
|
93
|
+
- Dedicated chaos/fail-open suite: corrupted TOML, malformed hook JSON,
|
|
94
|
+
permission errors, hook timeout, pathological regex (ReDoS) — all
|
|
95
|
+
degrade to the original, unfiltered output rather than crashing or
|
|
96
|
+
losing data.
|
|
97
|
+
- Error-safety snapshot tests across all 7 built-in filters, confirming
|
|
98
|
+
failure-relevant lines are never removed.
|
|
99
|
+
- CI on `windows-latest` and `ubuntu-latest`; a weekly canary workflow
|
|
100
|
+
installs unpinned `@anthropic-ai/claude-code` to catch upstream hook
|
|
101
|
+
format changes before users do.
|
|
102
|
+
|
|
103
|
+
### Known limitations
|
|
104
|
+
|
|
105
|
+
- The `AUDIT` / `OPTIMIZE` / `SIMULATE` operating-mode system is
|
|
106
|
+
display-only in this release — `quor doctor` and `quor gain` show the
|
|
107
|
+
configured mode, but the dispatcher does not yet branch on it. This is
|
|
108
|
+
an intentional, scoped roadmap item (see PROJECT_STATUS.md), not a bug.
|
|
109
|
+
- Not yet published to PyPI.
|
|
110
|
+
|
|
111
|
+
[0.1.0]: https://github.com/priyanshup/Quor/releases/tag/v0.1.0
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in
|
|
6
|
+
our community a harassment-free experience for everyone, regardless of age,
|
|
7
|
+
body size, visible or invisible disability, ethnicity, sex characteristics,
|
|
8
|
+
gender identity and expression, level of experience, education,
|
|
9
|
+
socio-economic status, nationality, personal appearance, race, religion, or
|
|
10
|
+
sexual identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment:
|
|
18
|
+
|
|
19
|
+
- Demonstrating empathy and kindness toward other people
|
|
20
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
21
|
+
- Giving and gracefully accepting constructive feedback
|
|
22
|
+
- Accepting responsibility and apologizing for mistakes, and learning from them
|
|
23
|
+
- Focusing on what is best for the community, not just the individual
|
|
24
|
+
|
|
25
|
+
Examples of unacceptable behavior:
|
|
26
|
+
|
|
27
|
+
- The use of sexualized language or imagery, and unwelcome sexual attention
|
|
28
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
29
|
+
- Public or private harassment
|
|
30
|
+
- Publishing others' private information without explicit permission
|
|
31
|
+
- Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
+
professional setting
|
|
33
|
+
|
|
34
|
+
Technical disagreements are settled by evidence — benchmark results, test
|
|
35
|
+
cases, and precedent in `docs/final/DECISIONS.md` — not by persistence or
|
|
36
|
+
volume. See `CONTRIBUTING.md` for how design and review decisions are made.
|
|
37
|
+
|
|
38
|
+
## Enforcement Responsibilities
|
|
39
|
+
|
|
40
|
+
Project maintainers are responsible for clarifying and enforcing our
|
|
41
|
+
standards of acceptable behavior and will take appropriate and fair
|
|
42
|
+
corrective action in response to any behavior deemed inappropriate,
|
|
43
|
+
threatening, offensive, or harmful.
|
|
44
|
+
|
|
45
|
+
## Scope
|
|
46
|
+
|
|
47
|
+
This Code of Conduct applies within all community spaces (issues, pull
|
|
48
|
+
requests, discussions) and also applies when an individual is officially
|
|
49
|
+
representing the project in public spaces.
|
|
50
|
+
|
|
51
|
+
## Enforcement
|
|
52
|
+
|
|
53
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
54
|
+
reported to the maintainers by opening a confidential report via GitHub
|
|
55
|
+
(see the repository's Security & Conduct contact in `SECURITY.md`), or by
|
|
56
|
+
emailing the maintainer listed in the repository's commit history. All
|
|
57
|
+
complaints will be reviewed and investigated promptly and fairly.
|
|
58
|
+
|
|
59
|
+
This project does not yet have a dedicated conduct-response team — as a
|
|
60
|
+
pre-1.0, single-maintainer project, reports are currently handled directly
|
|
61
|
+
by the project owner. This will be revisited as the contributor base grows.
|
|
62
|
+
|
|
63
|
+
## Attribution
|
|
64
|
+
|
|
65
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
66
|
+
version 2.1, available at
|
|
67
|
+
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
|
68
|
+
|
|
69
|
+
[homepage]: https://www.contributor-covenant.org
|