usecix 1.0.6__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.
Files changed (104) hide show
  1. usecix-1.0.6/LICENSE +37 -0
  2. usecix-1.0.6/PKG-INFO +110 -0
  3. usecix-1.0.6/README.md +167 -0
  4. usecix-1.0.6/README.pypi.md +79 -0
  5. usecix-1.0.6/apps/cli/cix/scripts/__init__.py +0 -0
  6. usecix-1.0.6/apps/cli/cix/scripts/_cloud_wait.py +271 -0
  7. usecix-1.0.6/apps/cli/cix/scripts/agent_framing_hook.py +78 -0
  8. usecix-1.0.6/apps/cli/cix/scripts/audit.py +195 -0
  9. usecix-1.0.6/apps/cli/cix/scripts/bash_workaround_hook.py +395 -0
  10. usecix-1.0.6/apps/cli/cix/scripts/build_tool_manifest.py +114 -0
  11. usecix-1.0.6/apps/cli/cix/scripts/catchup.py +352 -0
  12. usecix-1.0.6/apps/cli/cix/scripts/clean.py +418 -0
  13. usecix-1.0.6/apps/cli/cix/scripts/cli.py +82 -0
  14. usecix-1.0.6/apps/cli/cix/scripts/codex_policy_debug.py +92 -0
  15. usecix-1.0.6/apps/cli/cix/scripts/codex_policy_hook.py +366 -0
  16. usecix-1.0.6/apps/cli/cix/scripts/config.py +77 -0
  17. usecix-1.0.6/apps/cli/cix/scripts/convention_validator_hook.py +686 -0
  18. usecix-1.0.6/apps/cli/cix/scripts/coverage.py +294 -0
  19. usecix-1.0.6/apps/cli/cix/scripts/cross_project_advisor_hook.py +136 -0
  20. usecix-1.0.6/apps/cli/cix/scripts/doctor.py +535 -0
  21. usecix-1.0.6/apps/cli/cix/scripts/feedback.py +134 -0
  22. usecix-1.0.6/apps/cli/cix/scripts/file_claim_hook.py +177 -0
  23. usecix-1.0.6/apps/cli/cix/scripts/guard.py +360 -0
  24. usecix-1.0.6/apps/cli/cix/scripts/hooks_ctl.py +134 -0
  25. usecix-1.0.6/apps/cli/cix/scripts/impact.py +161 -0
  26. usecix-1.0.6/apps/cli/cix/scripts/index.py +638 -0
  27. usecix-1.0.6/apps/cli/cix/scripts/init.py +829 -0
  28. usecix-1.0.6/apps/cli/cix/scripts/md_edit_guard_hook.py +190 -0
  29. usecix-1.0.6/apps/cli/cix/scripts/memory_guard_hook.py +97 -0
  30. usecix-1.0.6/apps/cli/cix/scripts/memory_seeds/tool_surface.md +132 -0
  31. usecix-1.0.6/apps/cli/cix/scripts/orient.py +493 -0
  32. usecix-1.0.6/apps/cli/cix/scripts/post_search_hook.py +333 -0
  33. usecix-1.0.6/apps/cli/cix/scripts/post_write_hook.py +97 -0
  34. usecix-1.0.6/apps/cli/cix/scripts/precommit.py +890 -0
  35. usecix-1.0.6/apps/cli/cix/scripts/read_advisor_hook.py +424 -0
  36. usecix-1.0.6/apps/cli/cix/scripts/reconcile.py +279 -0
  37. usecix-1.0.6/apps/cli/cix/scripts/schema_pull.py +848 -0
  38. usecix-1.0.6/apps/cli/cix/scripts/session_start_hook.py +898 -0
  39. usecix-1.0.6/apps/cli/cix/scripts/source_of_truth_hook.py +118 -0
  40. usecix-1.0.6/apps/cli/cix/scripts/summarize.py +278 -0
  41. usecix-1.0.6/apps/cli/cix/scripts/version_check.py +139 -0
  42. usecix-1.0.6/apps/cli/cix/scripts/vital.py +464 -0
  43. usecix-1.0.6/apps/cli/cix/scripts/vital_sign.py +29 -0
  44. usecix-1.0.6/apps/cli/cix/scripts/wedge_ack.py +69 -0
  45. usecix-1.0.6/apps/client/cix/cloud/__init__.py +9 -0
  46. usecix-1.0.6/apps/client/cix/cloud/_changed_files.py +92 -0
  47. usecix-1.0.6/apps/client/cix/cloud/access_check.py +204 -0
  48. usecix-1.0.6/apps/client/cix/cloud/auth_check.py +196 -0
  49. usecix-1.0.6/apps/client/cix/cloud/client.py +225 -0
  50. usecix-1.0.6/apps/client/cix/cloud/config.py +45 -0
  51. usecix-1.0.6/apps/client/cix/cloud/credentials.py +69 -0
  52. usecix-1.0.6/apps/client/cix/cloud/dispatch.py +2443 -0
  53. usecix-1.0.6/apps/client/cix/cloud/freshness.py +151 -0
  54. usecix-1.0.6/apps/client/cix/cloud/license.py +128 -0
  55. usecix-1.0.6/apps/client/cix/cloud/login.py +503 -0
  56. usecix-1.0.6/apps/client/cix/cloud/login_gate.py +59 -0
  57. usecix-1.0.6/apps/client/cix/cloud/telemetry.py +139 -0
  58. usecix-1.0.6/apps/client/cix/cloud/token_refresh.py +68 -0
  59. usecix-1.0.6/apps/client/cix/cloud/transport.py +2078 -0
  60. usecix-1.0.6/apps/core/cix/core/__init__.py +48 -0
  61. usecix-1.0.6/apps/core/cix/core/diagnostics.py +266 -0
  62. usecix-1.0.6/apps/core/cix/core/edit_guard.py +327 -0
  63. usecix-1.0.6/apps/core/cix/core/get_lines.py +413 -0
  64. usecix-1.0.6/apps/core/cix/core/helpers.py +1196 -0
  65. usecix-1.0.6/apps/core/cix/core/precommit.py +106 -0
  66. usecix-1.0.6/apps/core/cix/core/presenter.py +310 -0
  67. usecix-1.0.6/apps/core/cix/core/registry.py +367 -0
  68. usecix-1.0.6/apps/core/cix/core/troubleshoot_log.py +155 -0
  69. usecix-1.0.6/apps/core/cix/core/working_tree.py +662 -0
  70. usecix-1.0.6/cix/__init__.py +1 -0
  71. usecix-1.0.6/cix/_indexed_extensions.py +68 -0
  72. usecix-1.0.6/cix/_ip_boundary.py +97 -0
  73. usecix-1.0.6/cix/_tool_manifest.py +1359 -0
  74. usecix-1.0.6/cix/install.py +1432 -0
  75. usecix-1.0.6/cix/integrations/__init__.py +2 -0
  76. usecix-1.0.6/cix/integrations/clients.py +50 -0
  77. usecix-1.0.6/cix/integrations/docs.py +347 -0
  78. usecix-1.0.6/cix/integrations/gemini.py +240 -0
  79. usecix-1.0.6/cix/integrations/windows.py +190 -0
  80. usecix-1.0.6/cix/lib/__init__.py +0 -0
  81. usecix-1.0.6/cix/lib/file_claims.py +63 -0
  82. usecix-1.0.6/cix/lib/git.py +59 -0
  83. usecix-1.0.6/cix/lib/naming.py +36 -0
  84. usecix-1.0.6/cix/lib/project_config.py +235 -0
  85. usecix-1.0.6/cix/lib/project_id.py +105 -0
  86. usecix-1.0.6/cix/lib/registration_guard.py +350 -0
  87. usecix-1.0.6/cix/lib/rewrite.py +70 -0
  88. usecix-1.0.6/cix/lib/schema_json.py +178 -0
  89. usecix-1.0.6/cix/lib/testdb.py +133 -0
  90. usecix-1.0.6/cix/mcp_server/__init__.py +0 -0
  91. usecix-1.0.6/cix/mcp_server/main.py +576 -0
  92. usecix-1.0.6/cix/mcp_server/watcher.py +507 -0
  93. usecix-1.0.6/cix/uninstall.py +538 -0
  94. usecix-1.0.6/packages/shared-schema/py/cix_shared_schema/__init__.py +17 -0
  95. usecix-1.0.6/packages/shared-schema/py/cix_shared_schema/parse_error.py +21 -0
  96. usecix-1.0.6/packages/shared-schema/py/cix_shared_schema/sync_envelope.py +125 -0
  97. usecix-1.0.6/pyproject.toml +115 -0
  98. usecix-1.0.6/setup.cfg +4 -0
  99. usecix-1.0.6/usecix.egg-info/PKG-INFO +110 -0
  100. usecix-1.0.6/usecix.egg-info/SOURCES.txt +126 -0
  101. usecix-1.0.6/usecix.egg-info/dependency_links.txt +1 -0
  102. usecix-1.0.6/usecix.egg-info/entry_points.txt +36 -0
  103. usecix-1.0.6/usecix.egg-info/requires.txt +25 -0
  104. usecix-1.0.6/usecix.egg-info/top_level.txt +2 -0
usecix-1.0.6/LICENSE ADDED
@@ -0,0 +1,37 @@
1
+ cix — Proprietary Software License
2
+ Copyright (c) 2026 cix. All rights reserved.
3
+
4
+ This software, including the cix client, command-line tools, MCP server,
5
+ and all associated files (collectively, the "Software"), is the proprietary
6
+ and confidential property of cix. The Software is licensed, not sold.
7
+
8
+ This is NOT open-source software. It is NOT licensed under the MIT license,
9
+ and it is NOT dual-licensed.
10
+
11
+ 1. Grant. Subject to a valid subscription and the cix Terms of Service, you
12
+ are granted a limited, non-exclusive, non-transferable, revocable license
13
+ to install and use the Software solely to interact with the cix cloud
14
+ service for your own internal development purposes.
15
+
16
+ 2. Restrictions. You may not, in whole or in part: (a) copy, redistribute,
17
+ sublicense, sell, rent, or lease the Software; (b) reverse engineer,
18
+ decompile, or disassemble the Software, or attempt to derive its source
19
+ code, parsing logic, or indexing heuristics; (c) create derivative works;
20
+ (d) remove or alter any proprietary notices; or (e) use the Software to
21
+ build a competing product.
22
+
23
+ 3. Ownership. All right, title, and interest in and to the Software,
24
+ including all intellectual property rights, remain exclusively with cix.
25
+
26
+ 4. Termination. This license terminates automatically if you breach any of
27
+ its terms. Upon termination you must cease all use and destroy all copies.
28
+
29
+ 5. No Warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
30
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
32
+
33
+ 6. Limitation of Liability. IN NO EVENT SHALL cix BE LIABLE FOR ANY CLAIM,
34
+ DAMAGES, OR OTHER LIABILITY ARISING FROM OR IN CONNECTION WITH THE
35
+ SOFTWARE OR ITS USE.
36
+
37
+ For licensing inquiries: legal@usecix.com
usecix-1.0.6/PKG-INFO ADDED
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: usecix
3
+ Version: 1.0.6
4
+ Summary: Git-anchored cloud-first code index + MCP for Claude, Codex, and Gemini
5
+ License-Expression: LicenseRef-cix-Proprietary
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: mcp>=1.0.0
10
+ Requires-Dist: pyjwt[crypto]>=2.8
11
+ Requires-Dist: tqdm>=4.65
12
+ Requires-Dist: watchfiles>=0.24
13
+ Provides-Extra: remote
14
+ Requires-Dist: uvicorn>=0.31; extra == "remote"
15
+ Requires-Dist: starlette>=0.41; extra == "remote"
16
+ Provides-Extra: db-pull
17
+ Requires-Dist: psycopg[binary]>=3.1; extra == "db-pull"
18
+ Requires-Dist: pymysql>=1.1; extra == "db-pull"
19
+ Requires-Dist: pymongo>=4.6; extra == "db-pull"
20
+ Requires-Dist: PyYAML>=6.0; extra == "db-pull"
21
+ Provides-Extra: test
22
+ Requires-Dist: testcontainers>=4.0; extra == "test"
23
+ Requires-Dist: psycopg[binary]>=3.1; extra == "test"
24
+ Requires-Dist: pymysql>=1.1; extra == "test"
25
+ Requires-Dist: pymongo>=4.6; extra == "test"
26
+ Requires-Dist: PyYAML>=6.0; extra == "test"
27
+ Provides-Extra: build
28
+ Requires-Dist: build>=1.0; extra == "build"
29
+ Requires-Dist: nuitka>=2.0; extra == "build"
30
+ Dynamic: license-file
31
+
32
+ # cix
33
+
34
+ **Git moves code. cix understands code. AI coding tools use cix to make safer changes.**
35
+
36
+ cix is a code-intelligence layer that sits between Git and AI coding tools
37
+ (Claude Code, Codex, Gemini). It indexes your committed source by Git commit
38
+ and exposes it to your AI client as a set of structured tools — symbol search,
39
+ navigation, schema lookup, impact analysis, and structured edits — so the
40
+ assistant works from what your repository *actually* contains instead of
41
+ guessing.
42
+
43
+ ## Why
44
+
45
+ AI coding tools are strong at writing code in isolation and weak at
46
+ understanding *your* repository. They re-read files they have already seen,
47
+ reinvent helpers that already exist, import modules that were deprecated, or
48
+ rename a function and miss half of its callers. cix gives the assistant a
49
+ precise, current answer to "what does this repository mean right now?" — so it
50
+ writes code that fits.
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ pip install usecix
56
+ ```
57
+
58
+ The heavy indexing runs in the cix cloud, not on your machine; the local
59
+ package is a thin client that connects over HTTPS.
60
+
61
+ ## Quick start
62
+
63
+ ```bash
64
+ # 1. Connect this machine to your cix account (opens a browser)
65
+ cix login
66
+
67
+ # 2. Register cix with your AI clients (Claude Code, Codex, Gemini)
68
+ cix install --client all
69
+
70
+ # 3. Restart your client so it picks up the cix tools
71
+
72
+ # 4. In a project you want indexed
73
+ cd ~/path/to/your/project
74
+ cix index
75
+ ```
76
+
77
+ Then open your AI client in that project and start working — it will use the
78
+ cix tools to search, read by symbol, check schemas, and assess impact before
79
+ editing.
80
+
81
+ ## Commands
82
+
83
+ cix installs a single `cix` dispatcher; each subcommand is also available as
84
+ `cix-<command>` (for example, `cix-index`).
85
+
86
+ | Command | What it does |
87
+ | --- | --- |
88
+ | `cix login` | Authenticate and fetch credentials |
89
+ | `cix install` | Install cix integrations (Claude, Codex, Gemini) |
90
+ | `cix uninstall` | Remove cix integrations |
91
+ | `cix index` | Build / update the index for the current project |
92
+ | `cix mcp` | Run the cix MCP server (stdio) |
93
+ | `cix serve` | Run the cix MCP server (remote SSE) |
94
+ | `cix doctor` | Diagnose your cix install and project setup |
95
+ | `cix config` | View / update per-project settings |
96
+
97
+ Run `cix <command> --help` for details on any command.
98
+
99
+ ## Requirements
100
+
101
+ - **Python 3.11+**
102
+ - **Git** — cix indexes committed source and tracks freshness by commit.
103
+ - **A GitHub-hosted repository** for the project you want indexed.
104
+ - **A supported AI client** — Claude Code, Codex CLI, and/or Gemini CLI.
105
+ - **A cix account** — `cix login` walks you through it.
106
+
107
+ ## License
108
+
109
+ cix is proprietary software. See the bundled `LICENSE` file. It is not open
110
+ source, and is not MIT- or dual-licensed.
usecix-1.0.6/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # cix — Code Index for Claude and Codex
2
+
3
+ > **Git moves code. cix understands code. AI clients use cix to make safer changes.**
4
+
5
+ cix is a semantic intelligence layer that sits between Git and AI coding tools (Claude Code, Codex, Gemini, ChatGPT, GitHub PR bots). It indexes committed source by Git SHA, exposes structured queries (symbols, routes, schemas, callers, impact), and validates AI-proposed edits before they hit the repo.
6
+
7
+ > **Status:** cix is in **v0 (cloud-first preview)**. The cloud index, MCP tool surface, convention hooks, and per-edit validation are used daily on the dev project.
8
+
9
+ > **Docs:** [Goals](docs/goals.md) · [Components](docs/components.md) · [Problem](docs/problem.md) · [Roadmap](docs/roadmap.md) · [Tools](docs/tools.md) · [Where content lives](docs/where_content_lives.md)
10
+
11
+ ---
12
+
13
+ ## How it works
14
+
15
+ cix is split across four repos, but you only install one:
16
+
17
+ - **`cix`** (this repo) — the thin client. CLI, MCP server, edit application, freshness gate. Ships to your machine via `pipx`.
18
+ - **`cix-api`** — the cloud backend. Owns the parser, the indexer, and the symbol DB. Clones repos via Git provider OAuth, parses with the proprietary parser, stores the symbol graph in cloud Neon. Never ships to clients.
19
+ - **`cix-cloud`** — auth, billing, telemetry, dashboard data feeds.
20
+ - **`cix-web`** — the dashboard at usecix.com.
21
+
22
+ When an AI tool calls a cix MCP tool (e.g. `search_code`), the local client routes it to cix-api. When you edit a file, the local client POSTs the new bytes to cix-api `/v1/validate`, which parses, returns ok/err, and populates an overlay index so the next read sees the fresh shape. The local process never holds the parser or the index — it's a thin client over the cloud.
23
+
24
+ See [Components](docs/components.md) for the full architecture and [Goals](docs/goals.md) for the contract.
25
+
26
+ ---
27
+
28
+ ## What cix gives an AI agent
29
+
30
+ Coding agents waste tokens rediscovering the same project on every prompt. They `Read` files they've read before, `Glob` directories that haven't moved, `Grep` strings whose home they already know. Each rediscovery costs latency, tokens, and — critically — context window.
31
+
32
+ cix replaces those blind loops with indexed navigation against the cloud index:
33
+
34
+ - **Search before write.** `search_code`, `get_symbol`, `find_usages` answer "does this exist? who calls it?" in milliseconds against a parser-built symbol graph that lives in cloud Neon.
35
+ - **Read by symbol, not by file.** `get_symbol`, `get_file_outline` return only the slice the agent needs. A 500-line file becomes a 30-line symbol.
36
+ - **Impact before edit.** `impact_analysis` answers "what would break if X changed?" — direct + transitive callers, affected HTTP routes, GraphQL handlers, tests, and data models, with a risk tier and reasons.
37
+ - **Schema before query.** `get_schema` returns parsed table definitions for Laravel, Django, Alembic / SQLAlchemy, Prisma, EF Core (and surfaces ORM evidence when no concrete schema is indexed), raw SQL — so the agent stops inventing column names.
38
+ - **Validation at edit time.** When the agent writes a file, the local cix client POSTs the new bytes to cix-api `/v1/validate`. Cloud parses, returns ok/err, and refreshes the overlay so the next read is current — no separate reindex step.
39
+ - **Convention enforcement at write time.** A PreToolUse hook blocks writes that violate `conventions.json` folder/naming rules and blocks new files whose stem collides with an existing indexed symbol.
40
+ - **Honest freshness.** Every read carries `canonical_sha` and `pending_edits`. cix never claims more certainty than it has.
41
+
42
+ ---
43
+
44
+ ## Quick start
45
+
46
+ From zero to cix working inside Claude / Codex in about three minutes:
47
+
48
+ ```bash
49
+ # 1. Install the package (pipx keeps it out of your project venvs)
50
+ pipx install git+https://github.com/usecix/cix
51
+
52
+ # 2. Register with your clients
53
+ # --client accepts: claude, codex, gemini, a comma-separated subset, or all
54
+ cix-install --client all
55
+
56
+ # 3. Connect this client to cix-api (one-time, opens a browser)
57
+ cix-login
58
+
59
+ # 4. Restart Claude Code, Codex, and/or Gemini CLI so they pick up the new MCP servers.
60
+
61
+ # 5. Inside each project you want indexed
62
+ cd ~/sites/myproject
63
+ cix-init --client all
64
+ # ▸ kicks off a cloud index of the repo via cix-api (~30-60s)
65
+ # ▸ MCP picks up the canonical index automatically when ready
66
+
67
+ # 6. Open your client in the project and start asking it to do things.
68
+ ```
69
+
70
+ cix-init also installs a Git push-side hook so subsequent pushes to your default branch trigger an incremental reindex via the GitHub App webhook. Out-of-band edits (manual edits in another editor) trigger an overlay revalidation through the same path Claude / Codex go through.
71
+
72
+ ---
73
+
74
+ ## What to try first
75
+
76
+ After install + `cix-init`, restart your client and give it a real task. You should see it call cix tools before reaching for `Read` / `Glob` / `Grep`. Good first prompts on a freshly indexed project:
77
+
78
+ - *"Where is the user authentication handler?"* — should call `search_code` or `find_backend_handler`, not `Glob`.
79
+ - *"Add a new component called X"* — should call `check_convention` and `search_code` before writing.
80
+ - *"What columns are on the invoices table?"* — should call `get_schema` before answering.
81
+
82
+ If the client jumps straight to file reads without calling cix, restart it (MCP servers load at client boot, not at `/clear`) and re-run `cix-doctor` to confirm registration + cix-api connectivity.
83
+
84
+ After a `git pull`: run `cix catchup` for a backward-looking briefing — new routes / removed services / schema changes since your last visit. Pair with `--mine` to filter to deltas your current branch touches. Team-tier; see [`docs/feature_cix_catchup.md`](docs/feature_cix_catchup.md).
85
+
86
+ ---
87
+
88
+ ## What works today
89
+
90
+ - **Cloud index via cix-api.** GitHub App OAuth → clone → parse → write to cloud Neon. Push webhook reindexes incrementally.
91
+ - **MCP tool surface** for symbol search, navigation, schema lookup, file outlines, impact analysis, structured edits, and refactor-rename. All routed through cix-api. Full list in [Tools](docs/tools.md).
92
+ - **Per-edit validation.** When the agent writes a file, the local client POSTs to cix-api `/v1/validate` and surfaces ok/err. No separate reindex step.
93
+ - **Pre-commit impact summary.** Every `git commit` prints a one-line headline plus a per-symbol caller tree before the commit lands — `Impact: 3 symbols changed, 14 callers affected across 2 files` and the routes / schema columns touched. Always exits 0 so it informs but never blocks. Configurable via `.cix/config.json` (`precommit.enabled`, `precommit.warn_threshold`, `precommit.max_callers_per_symbol`).
94
+ - **Silent Diff pre-commit guard.** Same hook, narrower job: interrupts only when your staged change deletes / renames / breaks the signature of a symbol that still has callers in files you haven't touched in the same commit. `[y]` continues, `[n]` aborts, `[show me]` expands. Fail-open on stale index / unreachable service / no TTY. Toggle with `cix config --no-guard`. See [feature_silent_diff.md](docs/feature_silent_diff.md).
95
+ - **Convention enforcement.** A PreToolUse hook blocks writes that violate `conventions.json` folder / naming rules, and blocks new files whose filename stem collides with an existing indexed symbol.
96
+ - **DB schema indexing** for Laravel, Django, Alembic / SQLAlchemy, Prisma, EF Core, and raw SQL. `get_schema` returns parsed table definitions; the instruction docs tell the client to call it before any model / migration / query.
97
+ - **Freshness metadata on every response** — `canonical_sha`, `pending_edits`, `trust_level`, `stale` — so the agent knows when to trust cix and when to re-check.
98
+ - **Multi-client support.** Claude Code, Codex CLI, Gemini CLI all get the MCP tools. Hooks (PreToolUse convention block, PostToolUse validate, SessionStart guide) are currently Claude-only.
99
+
100
+ See [Tools](docs/tools.md) for the full surface and [Components](docs/components.md) for the architecture.
101
+
102
+ ---
103
+
104
+ ## Known beta limitations
105
+
106
+ - **First-time index requires a Git remote.** cix-api clones the repo via the GitHub App. Repos without a remote — or repos behind a Git host we don't yet support — won't index. GitLab and Bitbucket OAuth are on the v1 path but not landed; today, GitHub-hosted repos only.
107
+ - **Hooks are Claude-only.** Codex and Gemini get the MCP tools but not the PreToolUse convention block, the PostToolUse validate, or the SessionStart tool-order guide. Parity is on the roadmap; it is not done.
108
+ - **No PyPI release yet.** Install is `pipx install git+https://...`. Tag-and-publish to PyPI lands with the v0.1 ship.
109
+
110
+ ---
111
+
112
+ ## How to report useful failures
113
+
114
+ A good failure report is reproducible by someone who has never seen your project. Please include:
115
+
116
+ 1. **The literal prompt** you gave the agent — not a paraphrase.
117
+ 2. **The cix tool call and its raw response** — including the `canonical_sha`, `pending_edits`, `trust_level`, `stale`, and `error_code` fields. If the agent skipped cix entirely, say so explicitly.
118
+ 3. **What the file or symbol actually looks like on disk** at the moment of the call — paste the relevant span or attach the file.
119
+ 4. **`cix-doctor` output** — captures install mode, cix-api connectivity, and client registration.
120
+ 5. **The cix version** — `pipx list | grep cix` or `pip show cix`.
121
+
122
+ What we don't need: long IDE transcripts, screenshots, or "it felt slow." A five-line repro is worth more than a 500-line trace.
123
+
124
+ ---
125
+
126
+ ## Requirements
127
+
128
+ - **Python 3.11+** — cix uses 3.11 stdlib features (`tomllib` and friends). `python3 --version` to check.
129
+ - **Git** — cix's freshness model and the canonical reindex both rely on a Git remote.
130
+ - **A GitHub-hosted repo** for the project you want indexed. GitLab and Bitbucket are on the v1 path; not yet supported.
131
+ - **A coding-agent client** — Claude Code, Codex CLI, and / or Gemini CLI. `cix-install` configures whichever you have.
132
+ - **A cix account** — `cix-login` walks you through it (free tier covers one public repo).
133
+ - **pipx** (recommended for install) — see [Why pipx](#why-pipx) below. Plain `pip` works too — see [Don't want pipx? Use pip](#dont-want-pipx-use-pip).
134
+
135
+ ### Why pipx
136
+
137
+ cix is a CLI tool, not a Python library you `import` from your code. `pipx` installs it into an isolated virtualenv and links the entry points (`cix`, `cix-init`, `cix-doctor`, `cix-mcp`) onto your `PATH`. Three concrete benefits:
138
+
139
+ - **No dependency collisions.** cix pins `tree-sitter`, `httpx`, and a handful of other libraries. With pipx those pins live in cix's own venv — they can't fight whatever your project venv pins.
140
+ - **Runnable from any cwd.** MCP clients spawn `cix-mcp` directly. No `cd ~/sites/cix && source venv/bin/activate` step before each run.
141
+ - **Clean upgrades and uninstalls.** `pipx upgrade cix` or `pipx uninstall cix` touches only cix's own venv — nothing else on the system.
142
+
143
+ It's the same install model as `ruff`, `black`, `poetry`, and most other CLI-first Python tools. To install pipx itself: `brew install pipx` on macOS, or `python3 -m pip install --user pipx && python3 -m pipx ensurepath` elsewhere.
144
+
145
+ ### Don't want pipx? Use pip
146
+
147
+ Plain `pip` in a virtualenv works too:
148
+
149
+ ```bash
150
+ python3 -m venv ~/.local/cix-venv
151
+ ~/.local/cix-venv/bin/pip install git+https://github.com/usecix/cix
152
+
153
+ # Then either put the venv's bin dir on PATH:
154
+ echo 'export PATH="$HOME/.local/cix-venv/bin:$PATH"' >> ~/.zshrc
155
+ # ...or symlink the entry points individually into ~/.local/bin.
156
+ ```
157
+
158
+ Installing into a project's existing venv or your global Python also works — but you take on the responsibility of keeping cix's dependencies from clashing with whatever else you have installed there. We don't recommend `sudo pip install` into the system Python on any platform.
159
+
160
+ ---
161
+
162
+ ## Start here
163
+
164
+ - **New to cix?** Read [The Problem](docs/problem.md) — why this exists.
165
+ - **Want the architecture?** [Components](docs/components.md) covers the four-repo split and the IP boundary.
166
+ - **Want the contract?** [Goals](docs/goals.md) lists the five invariants and the v1 done criteria.
167
+ - **Where are we headed?** [Roadmap](docs/roadmap.md) is the phased migration plan.
@@ -0,0 +1,79 @@
1
+ # cix
2
+
3
+ **Git moves code. cix understands code. AI coding tools use cix to make safer changes.**
4
+
5
+ cix is a code-intelligence layer that sits between Git and AI coding tools
6
+ (Claude Code, Codex, Gemini). It indexes your committed source by Git commit
7
+ and exposes it to your AI client as a set of structured tools — symbol search,
8
+ navigation, schema lookup, impact analysis, and structured edits — so the
9
+ assistant works from what your repository *actually* contains instead of
10
+ guessing.
11
+
12
+ ## Why
13
+
14
+ AI coding tools are strong at writing code in isolation and weak at
15
+ understanding *your* repository. They re-read files they have already seen,
16
+ reinvent helpers that already exist, import modules that were deprecated, or
17
+ rename a function and miss half of its callers. cix gives the assistant a
18
+ precise, current answer to "what does this repository mean right now?" — so it
19
+ writes code that fits.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install usecix
25
+ ```
26
+
27
+ The heavy indexing runs in the cix cloud, not on your machine; the local
28
+ package is a thin client that connects over HTTPS.
29
+
30
+ ## Quick start
31
+
32
+ ```bash
33
+ # 1. Connect this machine to your cix account (opens a browser)
34
+ cix login
35
+
36
+ # 2. Register cix with your AI clients (Claude Code, Codex, Gemini)
37
+ cix install --client all
38
+
39
+ # 3. Restart your client so it picks up the cix tools
40
+
41
+ # 4. In a project you want indexed
42
+ cd ~/path/to/your/project
43
+ cix index
44
+ ```
45
+
46
+ Then open your AI client in that project and start working — it will use the
47
+ cix tools to search, read by symbol, check schemas, and assess impact before
48
+ editing.
49
+
50
+ ## Commands
51
+
52
+ cix installs a single `cix` dispatcher; each subcommand is also available as
53
+ `cix-<command>` (for example, `cix-index`).
54
+
55
+ | Command | What it does |
56
+ | --- | --- |
57
+ | `cix login` | Authenticate and fetch credentials |
58
+ | `cix install` | Install cix integrations (Claude, Codex, Gemini) |
59
+ | `cix uninstall` | Remove cix integrations |
60
+ | `cix index` | Build / update the index for the current project |
61
+ | `cix mcp` | Run the cix MCP server (stdio) |
62
+ | `cix serve` | Run the cix MCP server (remote SSE) |
63
+ | `cix doctor` | Diagnose your cix install and project setup |
64
+ | `cix config` | View / update per-project settings |
65
+
66
+ Run `cix <command> --help` for details on any command.
67
+
68
+ ## Requirements
69
+
70
+ - **Python 3.11+**
71
+ - **Git** — cix indexes committed source and tracks freshness by commit.
72
+ - **A GitHub-hosted repository** for the project you want indexed.
73
+ - **A supported AI client** — Claude Code, Codex CLI, and/or Gemini CLI.
74
+ - **A cix account** — `cix login` walks you through it.
75
+
76
+ ## License
77
+
78
+ cix is proprietary software. See the bundled `LICENSE` file. It is not open
79
+ source, and is not MIT- or dual-licensed.
File without changes
@@ -0,0 +1,271 @@
1
+ """Poll a cix-api reindex job to completion, emitting one line per phase.
2
+
3
+ Shared by cix-init's _kick_cloud_index and cix-index's full-reindex
4
+ trigger so customers get the same UX (and the same Ctrl-C escape
5
+ hatch) regardless of which entry point started the job.
6
+
7
+ Cloud reindex jobs are async: the kickoff endpoint returns a job_id
8
+ immediately, the orchestrator runs in a Vercel sandbox for ~30-60s,
9
+ then writes status back. We print a line each time the backend reports
10
+ a new heartbeat stage — no fake percentage, no spinner pinned at 100%.
11
+ """
12
+
13
+ import sys
14
+ import time
15
+ from datetime import datetime, timezone
16
+ from typing import Any
17
+
18
+ from cix.cloud.config import CloudConfig
19
+ from cix.cloud.transport import TransportError, TransportNotConfigured, repos_index_job
20
+
21
+
22
+ POLL_INTERVAL_S = 2
23
+ TIMEOUT_S = 300
24
+
25
+
26
+ def wait_for_index_job(
27
+ config: CloudConfig,
28
+ job_id: str,
29
+ *,
30
+ label: str = "Indexing",
31
+ bar_indent: str = " ",
32
+ out=None,
33
+ ) -> dict[str, Any] | None:
34
+ """Poll ``/v1/repos/index/{job_id}`` until the job finishes.
35
+
36
+ Returns the final job row on success/failed, or ``None`` if the
37
+ user interrupted (Ctrl-C) or the wait timed out. In every
38
+ non-success case we print a one-line note so the customer knows
39
+ where things stand.
40
+
41
+ Emits one line per backend phase transition (sourced from the
42
+ indexer's heartbeat ``last_heartbeat_stage``) so the user sees real
43
+ motion through the run rather than a fake percentage:
44
+
45
+ {indent}→ preparing sandbox
46
+ {indent}→ cloning repo
47
+ {indent}→ refreshing files (3/17)
48
+ {indent}→ finalizing
49
+
50
+ Before the indexer posts its first heartbeat the job row carries
51
+ no stage (the sandbox is still booting/cloning), so we emit
52
+ "preparing sandbox" up-front — otherwise the first seconds of every
53
+ run read as a silent freeze. When the backend exposes no stage
54
+ detail at all we degrade to that single line.
55
+
56
+ On timeout we print the last observed phase and how long the
57
+ heartbeat has been frozen — a stuck sandbox (heartbeat age ≈ the
58
+ whole wait) reads very differently from a genuinely slow one
59
+ (heartbeat still advancing), and that distinction is the first
60
+ thing an operator needs.
61
+ """
62
+ out = out or sys.stdout
63
+ t0 = time.time()
64
+ current_stage: str | None = None
65
+ last_resp: dict[str, Any] | None = None
66
+ print(f"{bar_indent}→ preparing sandbox", file=out, flush=True)
67
+ try:
68
+ while True:
69
+ elapsed = int(time.time() - t0)
70
+ if elapsed > TIMEOUT_S:
71
+ print(_timeout_note(bar_indent, elapsed, last_resp), file=out)
72
+ return None
73
+
74
+ try:
75
+ resp = repos_index_job(config, job_id=job_id)
76
+ except (TransportError, TransportNotConfigured):
77
+ # Treat transient network errors as a missed poll, not a fatal
78
+ # condition. The job is still running cloud-side.
79
+ resp = None
80
+
81
+ if resp:
82
+ last_resp = resp
83
+ status = resp.get("status")
84
+ if status in ("success", "failed"):
85
+ return resp
86
+ stage = resp.get("last_heartbeat_stage")
87
+ if stage and stage != current_stage:
88
+ current_stage = stage
89
+ print(
90
+ f"{bar_indent}→ {_stage_label(stage)}",
91
+ file=out,
92
+ flush=True,
93
+ )
94
+ time.sleep(POLL_INTERVAL_S)
95
+ except KeyboardInterrupt:
96
+ print(
97
+ f"\n{bar_indent}→ {label} continues cloud-side (job={job_id[:12]}); "
98
+ f"check with `cix-index`.",
99
+ file=out,
100
+ )
101
+ return None
102
+
103
+
104
+
105
+ def print_job_result(
106
+ final: dict[str, Any] | None,
107
+ *,
108
+ started_at: float,
109
+ label: str = "Index",
110
+ indent: str = " ",
111
+ out=None,
112
+ ) -> None:
113
+ """Pretty-print the outcome of a wait_for_index_job call.
114
+
115
+ Separated from ``wait_for_index_job`` so callers that want to
116
+ customise the success/failure messaging can omit it. ``started_at``
117
+ is the wall-clock time the wait began; used to render duration.
118
+ """
119
+ if final is None:
120
+ # Already printed a note inside wait_for_index_job.
121
+ return
122
+ out = out or sys.stdout
123
+ elapsed = max(0, int(time.time() - started_at))
124
+ status = final.get("status")
125
+ mode = final.get("mode") # may be None on older cix-api responses
126
+ if status == "skip":
127
+ # Server short-circuited because nothing changed since the last
128
+ # cloud index. No sandbox provisioned, no work done.
129
+ print(f"{indent}✓ No changes to index — skipped ({elapsed}s).", file=out)
130
+ return
131
+ if status == "success":
132
+ symbols = final.get("symbol_count")
133
+ symbols_str = f"{symbols:,}" if isinstance(symbols, int) else "?"
134
+ if mode == "incremental":
135
+ files_touched = final.get("files_touched")
136
+ if isinstance(files_touched, int):
137
+ plural = "" if files_touched == 1 else "s"
138
+ # "5,446 total symbols" makes clear the count is the
139
+ # repo-wide total, not what this run processed.
140
+ print(
141
+ f"{indent}✓ Indexed {files_touched} file{plural}; "
142
+ f"{symbols_str} total symbols ({elapsed}s).",
143
+ file=out,
144
+ )
145
+ else:
146
+ # Old job rows pre-files_touched column. Fall back gracefully.
147
+ print(
148
+ f"{indent}✓ Indexed (incremental); {symbols_str} total symbols "
149
+ f"({elapsed}s).",
150
+ file=out,
151
+ )
152
+ elif mode == "full_rebuild":
153
+ print(
154
+ f"{indent}✓ Indexed {symbols_str} symbols (full rebuild, {elapsed}s).",
155
+ file=out,
156
+ )
157
+ else:
158
+ # Old cix-api response without mode field — keep prior phrasing.
159
+ suffix = f"{symbols_str} symbols" if symbols_str != "?" else "indexed"
160
+ print(f"{indent}✓ {label} ready ({suffix}, {elapsed}s).", file=out)
161
+ return
162
+ # Failure path. Surface the stage the job died at (strip the
163
+ # 'stopped-by:' marker prefix so the user sees a clean label) and
164
+ # the error message tail.
165
+ err = (final.get("error_message") or "").strip()
166
+ if err:
167
+ err = err.splitlines()[-1][:200]
168
+ stage = (final.get("last_heartbeat_stage") or "").strip()
169
+ if stage.startswith("stopped-by:"):
170
+ stage = stage[len("stopped-by:"):]
171
+ if stage:
172
+ print(
173
+ f"{indent}✗ {label} failed at {stage} (+{elapsed}s): "
174
+ f"{err or status}",
175
+ file=out,
176
+ )
177
+ else:
178
+ print(f"{indent}✗ {label} failed after {elapsed}s: {err or status}", file=out)
179
+
180
+ def _stage_label(stage: str) -> str:
181
+ """Map sandbox/indexer.py heartbeat stages to short human labels.
182
+
183
+ The indexer emits granular incremental stages (per-file refresh) and
184
+ coarser full-rebuild stages. We translate both into the honest, stable
185
+ vocabulary the user sees: preparing sandbox -> cloning repo -> detecting
186
+ changes -> refreshing files -> shipping batches -> finalizing -> wrapping
187
+ up. Unknown stages fall through as-is so a new heartbeat added cloud-side
188
+ still shows something useful before the CLI knows the new name.
189
+
190
+ The per-file incremental stage is "incremental-file-{idx}/{total}" — we
191
+ surface the "{idx}/{total}" counter as "refreshing files (3/17)" so the
192
+ user sees real, honest motion through the changed set rather than a raw
193
+ slug. No fake precision: the counter is the indexer's own file index.
194
+ """
195
+ if stage.startswith("incremental-file-"):
196
+ progress = stage[len("incremental-file-"):]
197
+ return f"refreshing files ({progress})"
198
+ return {
199
+ "starting": "starting",
200
+ "initializing": "starting",
201
+ "cloning": "cloning repo",
202
+ "clone-done": "preparing",
203
+ "incremental-starting": "detecting changes",
204
+ "incremental-done": "refreshing files",
205
+ "shipping-batches": "shipping batches",
206
+ "schema-extracting": "reading schema",
207
+ "history-walking": "recording history",
208
+ "index-status-posting": "finalizing",
209
+ "index-status-unconfirmed": "finalizing (retrying)",
210
+ "finalize-posting": "finalizing",
211
+ "commits-posting": "wrapping up",
212
+ "shutdown-signaling": "wrapping up",
213
+ }.get(stage, stage)
214
+
215
+ def _heartbeat_age_s(last_heartbeat_at: Any) -> int | None:
216
+ """Seconds since the indexer last emitted a heartbeat.
217
+
218
+ ``last_heartbeat_at`` is the ISO-8601 string from the job row
219
+ (``_serialise`` in cix-api). Returns None if absent or unparseable
220
+ so callers can fall back to a stage-only message. A large value is
221
+ the signal that the sandbox wedged rather than merely running slow.
222
+ """
223
+ if not isinstance(last_heartbeat_at, str) or not last_heartbeat_at.strip():
224
+ return None
225
+ try:
226
+ hb = datetime.fromisoformat(last_heartbeat_at)
227
+ except ValueError:
228
+ return None
229
+ if hb.tzinfo is None:
230
+ hb = hb.replace(tzinfo=timezone.utc)
231
+ return max(0, int((datetime.now(timezone.utc) - hb).total_seconds()))
232
+
233
+ def _timeout_note(
234
+ bar_indent: str,
235
+ elapsed: int,
236
+ last_resp: dict[str, Any] | None,
237
+ ) -> str:
238
+ """Build the message printed when the CLI poll wait exceeds TIMEOUT_S.
239
+
240
+ Replaces the old "still running after Ns" line, which discarded the
241
+ stage/heartbeat data the poll already had in hand. We now surface:
242
+
243
+ * the last phase the indexer reported (``last_heartbeat_stage``),
244
+ * how long that heartbeat has been frozen.
245
+
246
+ A heartbeat age close to ``elapsed`` means the sandbox wedged at
247
+ that phase (stuck); an age that keeps tracking the poll interval
248
+ means the job is merely slow. That one number is what tells an
249
+ operator which of the two they're looking at.
250
+ """
251
+ lines = [f"{bar_indent}⚠ Indexing still running after {elapsed}s."]
252
+ if last_resp:
253
+ stage = (last_resp.get("last_heartbeat_stage") or "").strip()
254
+ age = _heartbeat_age_s(last_resp.get("last_heartbeat_at"))
255
+ if stage:
256
+ phase = _stage_label(stage)
257
+ if age is not None:
258
+ frozen = " — heartbeat frozen" if age >= 30 else ""
259
+ lines.append(
260
+ f"{bar_indent} Last phase: {phase} "
261
+ f"(no heartbeat for {age}s{frozen})."
262
+ )
263
+ else:
264
+ lines.append(f"{bar_indent} Last phase: {phase}.")
265
+ elif age is not None:
266
+ lines.append(f"{bar_indent} No heartbeat for {age}s.")
267
+ lines.append(
268
+ f"{bar_indent} Job will keep running cloud-side; "
269
+ f"check later with `cix-index`."
270
+ )
271
+ return "\n".join(lines)