continuum-code 0.2.0__py3-none-any.whl

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.
continuum/scan.py ADDED
@@ -0,0 +1,129 @@
1
+ """Repo scan: detect stack (dbt, airflow, node, python) and optional CODEOWNERS; produce draft continuum.yaml."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+
8
+ def scan_repo(repo_root: str | Path) -> dict:
9
+ """Detect repo signals. Returns a dict with has_dbt, has_airflow, has_node, has_python, codeowners_path."""
10
+ repo_root = Path(repo_root)
11
+ has_dbt = (repo_root / "dbt_project.yml").exists() or (repo_root / "models").is_dir()
12
+ has_airflow = (repo_root / "dags").is_dir() or (repo_root / "airflow.cfg").exists()
13
+ has_node = (repo_root / "package.json").exists()
14
+ has_python = (repo_root / "pyproject.toml").exists() or (repo_root / "requirements.txt").exists()
15
+ codeowners = repo_root / "CODEOWNERS"
16
+ if not codeowners.exists():
17
+ codeowners = repo_root / ".github" / "CODEOWNERS"
18
+ codeowners_path = str(codeowners) if codeowners.exists() else None
19
+ return {
20
+ "has_dbt": has_dbt,
21
+ "has_airflow": has_airflow,
22
+ "has_node": has_node,
23
+ "has_python": has_python,
24
+ "codeowners_path": codeowners_path,
25
+ }
26
+
27
+
28
+ def draft_yaml(signals: dict, answers: dict | None = None) -> str:
29
+ """Produce draft continuum.yaml from scan signals and optional wizard answers."""
30
+ answers = answers or {}
31
+ lines = ["version: 0.1", "scopes:"]
32
+ # Base repo scope
33
+ lines.append(" - id: repo")
34
+ lines.append(' match: ["**/*"]')
35
+ lines.append(" contracts:")
36
+ lines.append(" - type: define")
37
+ lines.append(' key: prod_ready')
38
+ lines.append(' value: "Add rules below; then run continuum check."')
39
+ # Wizard: banned deps
40
+ banned_deps = answers.get("banned_deps") or []
41
+ for dep in banned_deps:
42
+ dep = dep.strip()
43
+ if not dep:
44
+ continue
45
+ safe_id = dep.replace(".", "_").replace("-", "_")[:30]
46
+ lines.append(" - type: ban")
47
+ lines.append(f" id: ban_{safe_id}")
48
+ lines.append(" match:")
49
+ lines.append(f' deps: ["{dep}"]')
50
+ lines.append(f' message: "Dependency {dep} is not allowed."')
51
+ # Wizard: ask_first paths
52
+ ask_paths = answers.get("ask_first_paths") or []
53
+ if ask_paths:
54
+ paths_str = ", ".join(f'"{p.strip()}"' for p in ask_paths if p.strip())
55
+ if paths_str:
56
+ lines.append(" - type: ask_first")
57
+ lines.append(" id: confirm_sensitive")
58
+ lines.append(" match:")
59
+ lines.append(f" paths: [{paths_str}]")
60
+ lines.append(' prompt: "Touching sensitive paths. Confirm intent."')
61
+ # Wizard: banned commands
62
+ banned_cmds = answers.get("banned_commands") or []
63
+ if banned_cmds:
64
+ cmds_str = ", ".join(f'"{c.strip()}"' for c in banned_cmds if c.strip())
65
+ if cmds_str:
66
+ lines.append(" - type: ban")
67
+ lines.append(" id: ban_commands")
68
+ lines.append(" match:")
69
+ lines.append(f" commands: [{cmds_str}]")
70
+ lines.append(' message: "These commands are not allowed in code."')
71
+ # dbt
72
+ if signals.get("has_dbt"):
73
+ lines.append(" - id: dbt")
74
+ lines.append(' match: ["dbt_project.yml", "models/**", "macros/**", "snapshots/**"]')
75
+ lines.append(" precedence: 10")
76
+ lines.append(" contracts:")
77
+ lines.append(" - type: ask_first")
78
+ lines.append(" id: confirm_marts")
79
+ lines.append(' match:')
80
+ lines.append(' paths: ["models/marts/**"]')
81
+ lines.append(' prompt: "Touching dbt marts. Confirm: (A) additive (B) breaking (C) refactor-only"')
82
+ lines.append(" - type: require")
83
+ lines.append(" id: require_tests_marts")
84
+ lines.append(' match:')
85
+ lines.append(' paths: ["models/marts/**"]')
86
+ lines.append(" require:")
87
+ lines.append(" - kind: tests")
88
+ lines.append(' hint: "Add or update dbt tests for changed marts."')
89
+ # airflow
90
+ if signals.get("has_airflow"):
91
+ lines.append(" - id: airflow")
92
+ lines.append(' match: ["dags/**"]')
93
+ lines.append(" precedence: 10")
94
+ lines.append(" contracts:")
95
+ lines.append(" - type: ask_first")
96
+ lines.append(" id: confirm_dag_changes")
97
+ lines.append(' match:')
98
+ lines.append(' paths: ["dags/**"]')
99
+ lines.append(' prompt: "Touching DAGs. Confirm you intend to change orchestration."')
100
+ lines.append(" - type: ban")
101
+ lines.append(" id: ban_risky_commands")
102
+ lines.append(' match:')
103
+ lines.append(' commands: ["*full-refresh*", "*backfill*", "*clear*"]')
104
+ lines.append(' message: "Risky commands (full-refresh, backfill, clear) must not be embedded in DAGs."')
105
+ # backend (python or node)
106
+ if signals.get("has_python") or signals.get("has_node"):
107
+ paths = ["backend/**", "src/**", "app/**"]
108
+ if signals.get("has_python"):
109
+ paths.append("**/*.py")
110
+ if signals.get("has_node"):
111
+ paths.append("lib/**")
112
+ match_str = ", ".join(f'"{p}"' for p in paths[:4])
113
+ lines.append(" - id: backend")
114
+ lines.append(f" match: [{match_str}]")
115
+ lines.append(" precedence: 10")
116
+ lines.append(" contracts:")
117
+ lines.append(" - type: require")
118
+ lines.append(" id: require_tests")
119
+ lines.append(" match:")
120
+ lines.append(f" paths: [{match_str}]")
121
+ lines.append(" require:")
122
+ lines.append(" - kind: tests")
123
+ lines.append(' hint: "Add or adjust tests for changed modules."')
124
+ severity = answers.get("require_severity") or "block"
125
+ if severity == "warn":
126
+ lines.append(" severity: warn")
127
+ if signals.get("codeowners_path"):
128
+ lines.append(" # CODEOWNERS found at " + signals["codeowners_path"] + " – consider ask_first for those paths")
129
+ return "\n".join(lines)
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: continuum-code
3
+ Version: 0.2.0
4
+ Summary: Rule engine so coding agents obey repo rules (block/warn/ask) with clear explanations.
5
+ Author: Continuum
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pyyaml>=6.0
10
+ Requires-Dist: pydantic>=2.0
11
+ Requires-Dist: click>=8.0
12
+ Requires-Dist: mcp>=1.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=7.0; extra == "dev"
15
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
16
+
17
+ # Continuum
18
+
19
+ Rule engine so coding agents **obey repo rules** across Cursor, Claude, and ChatGPT: they get **blocked**, **warned**, or **asked** before breaking rules, with clear explanations of which rule fired and how to fix.
20
+
21
+ ## v0.1 promise
22
+
23
+ - Add `continuum.yaml` to your repo.
24
+ - Run `continuum check` locally and in CI.
25
+ - Cursor (MCP) agents are gated: **blocked** / **warned** / **clarification_required** before breaking rules.
26
+ - The system **explains** which rule fired and how to fix it.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install -e . # from repo
32
+ # or when published:
33
+ pip install continuum-code
34
+ ```
35
+
36
+ Requires Python 3.10+.
37
+
38
+ ### Development on Windows (WSL2)
39
+
40
+ Keep the repo on the WSL filesystem (e.g. `~/repos/continuum`) for best compatibility.
41
+
42
+ **One-time (PowerShell as Admin):**
43
+
44
+ ```powershell
45
+ wsl --install -d Ubuntu-24.04
46
+ ```
47
+
48
+ After WSL install, open Ubuntu and run:
49
+
50
+ ```bash
51
+ # Base (keep repo under WSL, e.g. ~/repos/continuum)
52
+ sudo apt update && sudo apt install -y build-essential git curl
53
+
54
+ # Python via uv (fast, clean venvs)
55
+ curl -LsSf https://astral.sh/uv/install.sh | sh
56
+ source ~/.local/bin/env # or restart shell
57
+ ```
58
+
59
+ **Per clone (inside WSL, in repo root):**
60
+
61
+ ```bash
62
+ cd ~/repos/continuum # or your path
63
+ uv venv
64
+ source .venv/bin/activate
65
+ uv pip install -e ".[dev]"
66
+ ```
67
+
68
+ **Run tests:**
69
+
70
+ ```bash
71
+ pytest
72
+ ```
73
+
74
+ **Run CLI:**
75
+
76
+ ```bash
77
+ continuum check
78
+ continuum init --pack python-fastapi
79
+ ```
80
+
81
+ For a broader Windows + WSL2 dev setup (Terminal, Docker, Antigravity safety), see your preferred guide.
82
+
83
+ ## Quick start
84
+
85
+ ```bash
86
+ # Create config (optional: use a pack)
87
+ continuum init
88
+ continuum init --pack python-fastapi # or node-backend, typescript-monorepo
89
+
90
+ # Check current changes (git diff)
91
+ continuum check
92
+ continuum check --staged # only staged
93
+ continuum check --base origin/main # diff against branch
94
+
95
+ # Explain why a rule fired
96
+ continuum explain
97
+ continuum explain ban_lodash
98
+
99
+ # List active contracts
100
+ continuum inspect
101
+ ```
102
+
103
+ ## Config: `continuum.yaml`
104
+
105
+ ```yaml
106
+ version: 0.1
107
+ scopes:
108
+ - id: repo
109
+ match: ["**/*"]
110
+ contracts:
111
+ - type: ban
112
+ id: ban_lodash
113
+ match:
114
+ deps: ["lodash"]
115
+ message: "Use native JS or approved utils."
116
+ - type: ask_first
117
+ id: confirm_migrations
118
+ match:
119
+ paths: ["**/migrations/**"]
120
+ prompt: "Touching migrations. Confirm: (A) add-only (B) destructive (C) refactor"
121
+ - id: backend
122
+ match: ["backend/**"]
123
+ precedence: 10
124
+ contracts:
125
+ - type: require
126
+ id: require_tests
127
+ match:
128
+ paths: ["backend/**"]
129
+ require:
130
+ - kind: tests
131
+ hint: "Add or adjust unit tests for changed modules."
132
+ ```
133
+
134
+ Contract types: **ban** (deps/paths/commands), **require** (tests/logging/ADR), **ask_first** (confirmation gate), **define** (metadata).
135
+
136
+ ## Escape hatch
137
+
138
+ To skip checks (e.g. emergency hotfix), set `CONTINUUM_SKIP=1`; `continuum check` will exit 0 without running contracts. Prefer adjusting rules (e.g. `severity: warn`) in `continuum.yaml` when possible. See [docs/adoption.md](docs/adoption.md).
139
+
140
+ ## CI (GitHub Action)
141
+
142
+ ```yaml
143
+ - uses: actions/checkout@v4
144
+ with:
145
+ fetch-depth: 0
146
+ - uses: ./actions/continuum-check
147
+ with:
148
+ base_ref: ${{ github.event.pull_request.base.sha }}
149
+ ```
150
+
151
+ Or in another repo: `uses: your-org/continuum/actions/continuum-check@v0.1` (and install `continuum` via pip in the action).
152
+
153
+ ## Cursor / MCP
154
+
155
+ Run the MCP server so the Cursor agent can call `continuum_check` before applying changes:
156
+
157
+ ```bash
158
+ continuum mcp --transport stdio
159
+ ```
160
+
161
+ Add to Cursor MCP config:
162
+
163
+ ```json
164
+ {
165
+ "mcpServers": {
166
+ "continuum": {
167
+ "command": "continuum",
168
+ "args": ["mcp", "--transport", "stdio"]
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ See [docs/cursor-mcp.md](docs/cursor-mcp.md) and [docs/demo.md](docs/demo.md) for setup and the “Refactor auth middleware” demo.
175
+
176
+ **Golden demo:** Run the 3 scenarios in [demo/README.md](demo/README.md) (dbt marts require, airflow ask-first, banned command) in under 10 minutes.
177
+
178
+ ## Packs
179
+
180
+ Starter configs:
181
+
182
+ - **node-backend** – Node/JS backend (ban lodash, require tests, ask on migrations).
183
+ - **python-fastapi** – FastAPI app (ban requests in favor of httpx, require tests, ask on migrations).
184
+ - **typescript-monorepo** – TS monorepo (ban lodash, require tests in packages).
185
+ - **data-dbt-airflow** – dbt + Airflow repos (ask_first on marts/DAGs, require tests on marts, ban risky commands).
186
+
187
+ ```bash
188
+ continuum init --pack python-fastapi
189
+ continuum init --pack data-dbt-airflow # dbt + Airflow
190
+ ```
191
+
192
+ ### 5-minute adoption (dbt + Airflow)
193
+
194
+ 1. `pip install continuum-code` (or `pip install -e .` from repo).
195
+ 2. `continuum init --pack data-dbt-airflow` → writes `continuum.yaml`.
196
+ 3. `continuum validate` → confirm the file is valid.
197
+ 4. `continuum check` (or `continuum check --base origin/main` for PRs).
198
+ 5. Add the GitHub Action for CI; optionally run `continuum mcp --transport stdio` for Cursor.
199
+
200
+ ## Next steps (after v0.1)
201
+
202
+ - Richer dependency detection (poetry, pnpm, pip-tools).
203
+ - Pattern bans (regex on diffs).
204
+ - Stricter “require” checks (e.g. tests touched when src touched).
205
+ - Decision diffs / supersession (v0.2).
206
+
207
+ ## License
208
+
209
+ MIT.
@@ -0,0 +1,18 @@
1
+ continuum/__init__.py,sha256=4DD2V2nT10peslESeuFpV1ViFbciCM_1FXkZbxSs1S8,210
2
+ continuum/change_summary.py,sha256=pBYwyStK8KvO5OPK8VVORQ9pqQ7agEmQBmc0-WBDbcY,15609
3
+ continuum/cli.py,sha256=MYh811DPqVgUpFnU6uTkl3IIrIIqpas8MrKraoqokl4,16264
4
+ continuum/contracts.py,sha256=liDj_UcbsZ-TQruLQLvDebuxqph35csvMh_NhQpnv8k,5712
5
+ continuum/engine.py,sha256=55E2i8fpQD2w2Z_812Li1GFmC5jdS8fwy-g3w_RU3C8,9088
6
+ continuum/mcp_server.py,sha256=pH3xSHYyLMgj3e4aI2A84yR-pH0_HSJ6oJHxma5SQ5Q,4932
7
+ continuum/recommend.py,sha256=YZeWPTmHOZFqGQ8vdtaKQJ-elxEP0NDfbNM5vu1xPLg,2416
8
+ continuum/scan.py,sha256=4hSUG0v-vVuONrId_nYjYR_aV9eZEGWHRX-KOMeLFJA,6133
9
+ continuum/packs/data-dbt-airflow/README.md,sha256=hNAQdHoMU-8oIxLEoTdTuk0VeJ-IRilLI5g7_hYu_jU,1805
10
+ continuum/packs/data-dbt-airflow/continuum.yaml,sha256=DfQnCGzcmuTw_ZUrhVzbmfZMi3GtdLYlJJzZSCaf5OA,1385
11
+ continuum/packs/node-backend/continuum.yaml,sha256=MM_lR4C6hpDhhK37EyZT9l5GwW1nsjDpykZrV1QWnp0,856
12
+ continuum/packs/python-fastapi/continuum.yaml,sha256=WHESGlkx7L_0VgZYiZ8L-yCYISWCAUPuaoJUfAxzfpo,905
13
+ continuum/packs/typescript-monorepo/continuum.yaml,sha256=MVFXp1-Pw03FwEUAfvtlcXZuTS63x-3Uw0KgJ8Ahlvk,841
14
+ continuum_code-0.2.0.dist-info/METADATA,sha256=aq8YMf58-ecgQmnuXTYd-lnmu6srRO3hRdIEYiILTEA,5654
15
+ continuum_code-0.2.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
16
+ continuum_code-0.2.0.dist-info/entry_points.txt,sha256=V8HMNBQVMiPLhcrAkCLpJYterSgesg0FjMkWa7s9YGA,49
17
+ continuum_code-0.2.0.dist-info/top_level.txt,sha256=TXST1D1spateEmtvhXZfWhbvAkUySKPQ6pmiLzqNqRg,10
18
+ continuum_code-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ continuum = continuum.cli:main
@@ -0,0 +1 @@
1
+ continuum