├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .ruff.toml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── .gitignore ├── astro.config.mjs ├── favicon.svg ├── package-lock.json ├── package.json ├── public │ └── kit.png ├── src │ ├── components │ │ ├── Card.astro │ │ ├── CardGroup.astro │ │ ├── CasedCard.astro │ │ ├── Changelog.astro │ │ ├── Frame.astro │ │ ├── PageIntro.astro │ │ └── Update.astro │ ├── content │ │ ├── config.ts │ │ └── docs │ │ │ ├── README.mdx │ │ │ ├── api │ │ │ ├── code_searcher.mdx │ │ │ ├── dependency-analyzer.mdx │ │ │ ├── docstring-indexer.mdx │ │ │ ├── repository.mdx │ │ │ ├── rest-api.mdx │ │ │ ├── summarizer.mdx │ │ │ └── summary-searcher.mdx │ │ │ ├── changelog.mdx │ │ │ ├── core-concepts │ │ │ ├── code-summarization.mdx │ │ │ ├── context-assembly.mdx │ │ │ ├── dependency-analysis.mdx │ │ │ ├── docstring-indexing.mdx │ │ │ ├── llm-context-best-practices.mdx │ │ │ ├── repository-api.mdx │ │ │ ├── repository-versioning.mdx │ │ │ ├── search-approaches.mdx │ │ │ ├── semantic-search.mdx │ │ │ └── tool-calling-with-kit.mdx │ │ │ ├── development │ │ │ ├── roadmap.mdx │ │ │ └── running-tests.mdx │ │ │ ├── extending │ │ │ └── adding-languages.mdx │ │ │ ├── index.mdx │ │ │ ├── introduction │ │ │ ├── cli.mdx │ │ │ ├── overview.mdx │ │ │ ├── quickstart.mdx │ │ │ └── usage-guide.mdx │ │ │ ├── mcp │ │ │ └── using-kit-with-mcp.md │ │ │ ├── pr-reviewer.mdx │ │ │ ├── recipes.mdx │ │ │ └── tutorials │ │ │ ├── ai_pr_reviewer.mdx │ │ │ ├── codebase-qa-bot.mdx │ │ │ ├── codebase_summarizer.mdx │ │ │ ├── dependency_graph_visualizer.mdx │ │ │ ├── docstring_search.mdx │ │ │ ├── dump_repo_map.mdx │ │ │ ├── exploring-kit-interactively.mdx │ │ │ ├── integrating_supersonic.mdx │ │ │ ├── ollama.mdx │ │ │ └── recipes.mdx │ ├── env.d.ts │ └── styles │ │ ├── fonts │ │ ├── IBMPlexSansV.ttf │ │ └── iAWriterQuattroV.ttf │ │ └── theme.css └── tsconfig.json ├── package-lock.json ├── pyproject.toml ├── scripts ├── benchmark.py ├── format.sh ├── index.py ├── release.sh ├── test.sh ├── test_ollama_local.py └── typecheck.sh ├── src └── kit │ ├── __init__.py │ ├── api │ ├── __init__.py │ ├── app.py │ └── registry.py │ ├── cli.py │ ├── code_searcher.py │ ├── context_extractor.py │ ├── dependency_analyzer │ ├── __init__.py │ ├── dependency_analyzer.py │ ├── python_dependency_analyzer.py │ └── terraform_dependency_analyzer.py │ ├── docstring_indexer.py │ ├── llm_context.py │ ├── mcp │ ├── __init__.py │ ├── __main__.py │ ├── main.py │ └── server.py │ ├── pr_review │ ├── README.md │ ├── ROADMAP.md │ ├── __init__.py │ ├── __main__.py │ ├── agentic_reviewer.py │ ├── cache.py │ ├── config.py │ ├── cost_tracker.py │ ├── debug.py │ ├── diff_parser.py │ ├── example_reviews │ │ ├── README.md │ │ ├── biopython_204_documentation_fix.md │ │ ├── fastapi_11935_standard_dependencies.md │ │ ├── model_comparison_fastapi_11935.md │ │ └── react_dev_6986_branding_menu.md │ ├── file_prioritizer.py │ ├── line_ref_fixer.py │ ├── matrix_tester.py │ ├── reviewer.py │ ├── test_accuracy.py │ └── validator.py │ ├── queries │ ├── c │ │ └── tags.scm │ ├── go │ │ └── tags.scm │ ├── hcl │ │ └── tags.scm │ ├── java │ │ └── tags.scm │ ├── javascript │ │ └── tags.scm │ ├── python │ │ └── tags.scm │ ├── ruby │ │ └── tags.scm │ ├── rust │ │ └── tags.scm │ └── typescript │ │ └── tags.scm │ ├── repo_mapper.py │ ├── repository.py │ ├── summaries.py │ ├── tool_schemas.py │ ├── tree_sitter_symbol_extractor.py │ └── vector_searcher.py ├── test.sh ├── tests ├── README.md ├── conftest.py ├── evals │ └── pr_test_set.txt ├── examples │ ├── python_dependency_analysis │ │ ├── README.md │ │ └── analyze_python_deps.py │ └── terraform_dependency_analysis │ │ ├── README.md │ │ ├── analyze_terraform_deps.py │ │ ├── compute.tf │ │ ├── database.tf │ │ ├── dns.tf │ │ ├── main.tf │ │ ├── network.tf │ │ ├── outputs.tf │ │ ├── security.tf │ │ ├── storage.tf │ │ └── variables.tf ├── fixtures │ └── realistic_repo │ │ ├── __init__.py │ │ ├── app.py │ │ ├── models │ │ ├── __init__.py │ │ └── user.py │ │ ├── services │ │ ├── __init__.py │ │ ├── auth.py │ │ └── db.py │ │ └── utils.py ├── golden_go.go ├── golden_hcl.tf ├── golden_python.py ├── golden_python_complex.py ├── golden_rust.rs ├── golden_typescript.ts ├── golden_typescript_complex.ts ├── integration │ └── test_api_live.py ├── mcp │ ├── test_call_tool_content.py │ └── test_server.py ├── sample_code │ ├── c_sample.c │ ├── go_sample.go │ ├── hcl_sample.hcl │ ├── java_sample.java │ ├── javascript_sample.js │ ├── python_sample.py │ ├── ruby_sample.rb │ ├── rust_sample.rs │ ├── tf_sample.tf │ ├── tsx_sample.tsx │ └── typescript_sample.ts ├── test_api_ref.py ├── test_cli.py ├── test_cli_integration.py ├── test_cli_ref.py ├── test_code_searcher.py ├── test_context_assembler.py ├── test_context_assembler_limits.py ├── test_context_extractor.py ├── test_cross_file_impact.py ├── test_diff_integration.py ├── test_diff_parser.py ├── test_docstring_incremental.py ├── test_docstring_indexer.py ├── test_golden_symbols.py ├── test_hcl_symbols.py ├── test_java_symbols.py ├── test_line_accuracy_validation.py ├── test_line_ref_fix.py ├── test_llm_line_accuracy.py ├── test_mcp_ref.py ├── test_ollama_integration.py ├── test_ollama_integration_real.py ├── test_pr_review.py ├── test_python_dependency_analyzer.py ├── test_real_pr_accuracy.py ├── test_registry_deterministic.py ├── test_repo.py ├── test_repo_integration.py ├── test_repo_mapper.py ├── test_resource_loading.py ├── test_ruby_c_symbols.py ├── test_sample_code_extraction.py ├── test_summaries.py ├── test_summarizer.py ├── test_symbol_extraction_multilang.py ├── test_symbol_extractor_additional.py ├── test_terraform_dependency_analyzer.py ├── test_tool_schemas.py ├── test_tree_sitter_languages.py ├── test_tree_sitter_symbol_extractor.py ├── test_typescript_symbol_extraction.py └── test_vector_searcher.py └── uv.lock /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up Python 15 | uses: actions/setup-python@v5 16 | with: 17 | python-version: '3.13' 18 | - name: Install uv 19 | run: pip install uv 20 | - name: Set up virtualenv and install deps 21 | run: | 22 | # Create a new virtual environment 23 | uv venv .venv 24 | # Activate the virtual environment 25 | source .venv/bin/activate 26 | # Install runtime and dev dependencies from pyproject.toml 27 | uv pip install -e .[dev,all] 28 | - name: Lint and Type Check 29 | run: | 30 | # Activate the virtual environment 31 | source .venv/bin/activate 32 | # Run mypy type checking 33 | mypy src/kit 34 | # Run ruff linter 35 | ruff check . 36 | # Run ruff formatter check (non-modifying) 37 | ruff format --check . 38 | - name: Run Tests 39 | run: | 40 | # Activate the virtual environment 41 | source .venv/bin/activate 42 | # Run tests via scripts/test.sh 43 | bash scripts/test.sh -v 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # Distribution / packaging 7 | .Python 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | downloads/ 12 | eggs/ 13 | .eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | .venv/ 23 | 24 | # Installer logs 25 | pip-log.txt 26 | pip-delete-this-directory.txt 27 | 28 | # Unit test / coverage reports 29 | htmlcov/ 30 | .tox/ 31 | .nox/ 32 | .coverage 33 | .coverage.* 34 | .cache 35 | nosetests.xml 36 | coverage.xml 37 | *.cover 38 | .hypothesis/ 39 | .pytest_cache/ 40 | test_repo* 41 | 42 | # Jupyter Notebook 43 | .ipynb_checkpoints 44 | 45 | # pyenv 46 | .python-version 47 | 48 | # mypy 49 | .mypy_cache/ 50 | .dmypy.json 51 | 52 | # VSCode 53 | .vscode/ 54 | 55 | # macOS 56 | .DS_Store 57 | .vercel 58 | 59 | # Kit specific cache and data directory 60 | .kit/ 61 | # .kit/docstring_db/ # Covered by .kit/ 62 | 63 | # Test results and matrix testing artifacts 64 | test-results/ 65 | *.json 66 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | # Base configuration from pyproject.toml 2 | line-length = 120 3 | target-version = "py310" 4 | 5 | [lint] 6 | select = ["E", "F", "W", "I", "RUF"] 7 | # Ignore unicode dash issues - these are stylistic 8 | ignore = ["RUF002", "RUF003", "E741"] 9 | 10 | # Ignore specific issues in certain files 11 | [lint.per-file-ignores] 12 | # Example files don't need strict import ordering 13 | "tests/examples/**/*.py" = ["E402"] 14 | "src/kit/mcp/__main__.py" = ["E402"] 15 | 16 | # Test files can have long lines in test data 17 | "tests/**/*.py" = ["E501"] 18 | 19 | # Allow mutable default attributes in these files 20 | "src/kit/tree_sitter_symbol_extractor.py" = ["RUF012", "E501"] 21 | "src/kit/summaries.py" = ["RUF012", "E501"] 22 | 23 | # MCP files have complex formatting needs and may exceed line limits 24 | "src/kit/mcp/**/*.py" = ["E501"] 25 | 26 | # Library files with long lines we'll fix later 27 | "src/kit/**/*.py" = ["E501"] 28 | 29 | # Files to skip import sorting completely 30 | "**/__init__.py" = ["F401"] 31 | 32 | [lint.isort] 33 | known-first-party = ["kit", "cased_kit"] -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.5.0] - 2025-05-31 4 | 5 | ### Changed 6 | - **BREAKING**: Made `sentence-transformers` and `chromadb` optional dependencies 7 | - Basic installation no longer includes ML/PyTorch dependencies (~50MB vs ~2GB) 8 | - Semantic search features now require `pip install cased-kit[ml]` 9 | - Full installation available via `pip install cased-kit[all]` 10 | 11 | ### Added 12 | - New installation options for different use cases 13 | - Comprehensive PR reviewer documentation at `/pr-reviewer` 14 | - CI/CD integration examples for GitHub Actions 15 | 16 | ### Fixed 17 | - Corrected PR review pricing documentation (actual: $0.01-0.05, not $0.10-0.30) 18 | - Removed references to deprecated Simple mode 19 | 20 | ### Improved 21 | - PR reviewer now shows model information in logs 22 | - Added real-world PR review examples with actual costs 23 | - Enhanced documentation organization -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Cased 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | .vercel 23 | -------------------------------------------------------------------------------- /docs/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config"; 2 | import starlight from "@astrojs/starlight"; 3 | import starlightLLMsTXT from "starlight-llms-txt"; 4 | 5 | // https://astro.build/config 6 | export default defineConfig({ 7 | site: "https://kit.cased.com", 8 | integrations: [ 9 | starlight({ 10 | title: "kit ", 11 | plugins: [starlightLLMsTXT()], 12 | social: [ 13 | { 14 | icon: "github", 15 | href: "https://github.com/cased/kit", 16 | label: "GitHub", 17 | }, 18 | ], 19 | customCss: [ 20 | // Path to your custom CSS file, relative to the project root 21 | "./src/styles/theme.css", 22 | ], 23 | markdown: { headingLinks: false }, 24 | sidebar: [ 25 | { 26 | label: " Introduction", 27 | items: [ 28 | "introduction/overview", 29 | "introduction/quickstart", 30 | "introduction/usage-guide", 31 | "introduction/cli", 32 | "pr-reviewer", 33 | "changelog" 34 | ], 35 | }, 36 | { 37 | label: " Core Concepts", 38 | items: [ 39 | // Manually specify order, starting with repository-api 40 | "core-concepts/repository-api", 41 | "core-concepts/search-approaches", 42 | "core-concepts/code-summarization", 43 | "core-concepts/docstring-indexing", 44 | "core-concepts/tool-calling-with-kit", 45 | "core-concepts/repository-versioning", 46 | "core-concepts/semantic-search", 47 | "core-concepts/dependency-analysis", 48 | "core-concepts/llm-context-best-practices", 49 | "core-concepts/context-assembly", 50 | ], 51 | }, 52 | { 53 | label: " Tutorials", 54 | items: [ 55 | "tutorials/ai_pr_reviewer", 56 | "tutorials/codebase-qa-bot", 57 | "tutorials/codebase_summarizer", 58 | "tutorials/dependency_graph_visualizer", 59 | "tutorials/docstring_search", 60 | "tutorials/dump_repo_map", 61 | "tutorials/integrating_supersonic", 62 | "tutorials/recipes", 63 | "tutorials/ollama", 64 | ], 65 | }, 66 | { 67 | label: " API Reference", 68 | autogenerate: { directory: "api" }, 69 | }, 70 | { 71 | label: " MCP", 72 | items: [ 73 | "mcp/using-kit-with-mcp", 74 | ], 75 | }, 76 | { 77 | label: " Development", 78 | autogenerate: { directory: "development" }, 79 | }, 80 | { 81 | label: " Extending Kit", 82 | autogenerate: { directory: "extending" }, 83 | }, 84 | ], 85 | }), 86 | ], 87 | }); 88 | -------------------------------------------------------------------------------- /docs/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kit-docs", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/starlight": "^0.34.2", 14 | "starlight-llms-txt": "^0.5.1", 15 | "astro": "^5.7.10", 16 | "astro-icon": "^1.1.5", 17 | "sharp": "^0.33.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/public/kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cased/kit/7b2d248e06f9105dd51ef7968c0573041c19a80b/docs/public/kit.png -------------------------------------------------------------------------------- /docs/src/components/Card.astro: -------------------------------------------------------------------------------- 1 | --- 2 | export interface Props { 3 | title: string; 4 | href?: string; 5 | icon?: string; 6 | } 7 | const { title, href, icon } = Astro.props; 8 | const Tag = href ? 'a' : 'div'; 9 | --- 10 | 11 |
12 | {icon &&
{icon}
} 13 |
14 |
{title}
15 |
16 |
17 |
18 |
19 | 20 | 61 | -------------------------------------------------------------------------------- /docs/src/components/CardGroup.astro: -------------------------------------------------------------------------------- 1 | --- 2 | export interface Props { cols?: number } 3 | const { cols = 2 } = Astro.props; 4 | const grid = `repeat(${cols}, minmax(0, 1fr))`; 5 | --- 6 |
7 | 8 |
9 | -------------------------------------------------------------------------------- /docs/src/components/CasedCard.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | href?: string; 5 | } 6 | 7 | const { title, href } = Astro.props; 8 | const Tag = href ? "a" : "div"; 9 | --- 10 | 11 |
12 | 16 | { 17 | Astro.slots.has("icon") && ( 18 | 19 | 20 | 21 | ) 22 | } 23 |

24 | {title} 25 |

26 |
27 | 28 |
29 |
30 |
31 | 32 | 87 | -------------------------------------------------------------------------------- /docs/src/components/Changelog.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import type { CollectionEntry } from "astro:content"; 4 | 5 | const entries = await getCollection("changelog"); 6 | const sortedEntries = entries.sort( 7 | (a, b) => b.data.date.getTime() - a.data.date.getTime() 8 | ); 9 | 10 | // Render each entry 11 | const renderedEntries = await Promise.all( 12 | sortedEntries.map(async (entry) => { 13 | const { Content } = await entry.render(); 14 | return { entry, Content }; 15 | }) 16 | ); 17 | --- 18 | 19 |
20 | {entries.length === 0 &&

No changelog entries found.

} 21 | { 22 | renderedEntries.map(({ entry, Content }) => ( 23 |
24 |
25 | 28 |

{entry.data.title}

29 |
30 |
31 | 32 |
33 |
34 | )) 35 | } 36 |
37 | 38 | 85 | -------------------------------------------------------------------------------- /docs/src/components/Frame.astro: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | --- 4 | 5 |
6 | 7 |
8 | 9 | 25 | -------------------------------------------------------------------------------- /docs/src/components/PageIntro.astro: -------------------------------------------------------------------------------- 1 | --- 2 | export interface Props { 3 | class?: string; 4 | } 5 | 6 | const { class: className } = Astro.props; 7 | --- 8 | 9 |
10 | 11 |
12 | 13 | 25 | 26 | 37 | -------------------------------------------------------------------------------- /docs/src/components/Update.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | label: string; 4 | } 5 | 6 | const { label } = Astro.props; 7 | --- 8 | 9 |
10 |
{label}
11 |
12 | 13 |
14 |
15 | 16 | 52 | -------------------------------------------------------------------------------- /docs/src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection } from 'astro:content'; 2 | import { docsSchema } from '@astrojs/starlight/schema'; 3 | 4 | export const collections = { 5 | docs: defineCollection({ schema: docsSchema() }), 6 | }; 7 | -------------------------------------------------------------------------------- /docs/src/content/docs/README.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Documentation 3 | description: Documentation for kit. 4 | --- 5 | 6 | This uses [Starlight](https://starlight.astro.build) to build the documentation. 7 | 8 | ## 🧞 Commands 9 | 10 | All commands are run from the root of the project, from a terminal: 11 | 12 | | Command | Action | 13 | | :------------------------ | :----------------------------------------------- | 14 | | `pnpm install` | Installs dependencies | 15 | | `pnpm dev` | Starts local dev server at `localhost:4321` | 16 | | `pnpm build` | Build your production site to `./dist/` | 17 | | `pnpm preview` | Preview your build locally, before deploying | 18 | | `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` | 19 | | `pnpm astro -- --help` | Get help using the Astro CLI | 20 | 21 | ## 👀 Want to learn more? 22 | 23 | Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). 24 | -------------------------------------------------------------------------------- /docs/src/content/docs/api/code_searcher.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CodeSearcher API 3 | --- 4 | 5 | import { Aside } from '@astrojs/starlight/components'; 6 | 7 | This page details the API for the `CodeSearcher` class, used for performing text and regular expression searches across your repository. 8 | 9 | ## Initialization 10 | 11 | To use the `CodeSearcher`, you first need to initialize it with the path to your repository: 12 | 13 | ```python 14 | from kit.code_searcher import CodeSearcher 15 | 16 | searcher = CodeSearcher(repo_path="/path/to/your/repo") 17 | # Or, if you have a kit.Repository object: 18 | searcher = repo.get_code_searcher() 19 | ``` 20 | 21 | 24 | 25 | ## `SearchOptions` Dataclass 26 | 27 | The `search_text` method uses a `SearchOptions` dataclass to control search behavior. You can import it from `kit.code_searcher`. 28 | 29 | ```python 30 | from kit.code_searcher import SearchOptions 31 | ``` 32 | 33 | **Fields:** 34 | 35 | * `case_sensitive` (bool): 36 | * If `True` (default), the search query is case-sensitive. 37 | * If `False`, the search is case-insensitive. 38 | * `context_lines_before` (int): 39 | * The number of lines to include before each matching line. Defaults to `0`. 40 | * `context_lines_after` (int): 41 | * The number of lines to include after each matching line. Defaults to `0`. 42 | * `use_gitignore` (bool): 43 | * If `True` (default), files and directories listed in the repository's `.gitignore` file will be excluded from the search. 44 | * If `False`, `.gitignore` rules are ignored. 45 | 46 | ## Methods 47 | 48 | ### `search_text(query: str, file_pattern: str = "*.py", options: Optional[SearchOptions] = None) -> List[Dict[str, Any]]` 49 | 50 | Searches for a text pattern (which can be a regular expression) in files matching the `file_pattern`. 51 | 52 | * **Parameters:** 53 | * `query` (str): The text pattern or regular expression to search for. 54 | * `file_pattern` (str): A glob pattern specifying which files to search in. Defaults to `"*.py"` (all Python files). 55 | * `options` (Optional[SearchOptions]): An instance of `SearchOptions` to customize search behavior. If `None`, default options are used. 56 | * **Returns:** 57 | * `List[Dict[str, Any]]`: A list of dictionaries, where each dictionary represents a match and contains: 58 | * `"file"` (str): The relative path to the file from the repository root. 59 | * `"line_number"` (int): The 1-indexed line number where the match occurred. 60 | * `"line"` (str): The content of the matching line (with trailing newline stripped). 61 | * `"context_before"` (List[str]): A list of strings, each being a line of context before the match. 62 | * `"context_after"` (List[str]): A list of strings, each being a line of context after the match. 63 | * **Raises:** 64 | * The method includes basic error handling for file operations and will print an error message to the console if a specific file cannot be processed, then continue with other files. 65 | 66 | **Example Usage:** 67 | 68 | ```python 69 | from kit.code_searcher import CodeSearcher, SearchOptions 70 | 71 | # Assuming 'searcher' is an initialized CodeSearcher instance 72 | 73 | # Basic search for 'my_function' in Python files 74 | results_basic = searcher.search_text("my_function") 75 | 76 | # Case-insensitive search with 2 lines of context before and after 77 | custom_options = SearchOptions( 78 | case_sensitive=False, 79 | context_lines_before=2, 80 | context_lines_after=2 81 | ) 82 | results_with_options = searcher.search_text( 83 | query=r"my_variable\s*=\s*\d+", # Example regex query 84 | file_pattern="*.txt", 85 | options=custom_options 86 | ) 87 | 88 | for match in results_with_options: 89 | print(f"Found in {match['file']} at line {match['line_number']}:") 90 | for before_line in match['context_before']: 91 | print(f" {before_line}") 92 | print(f"> {match['line']}") 93 | for after_line in match['context_after']: 94 | print(f" {after_line}") 95 | print("---") 96 | -------------------------------------------------------------------------------- /docs/src/content/docs/api/summarizer.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Summarizer API 3 | --- 4 | 5 | import { Aside } from '@astrojs/starlight/components'; 6 | 7 | This page details the API for the `Summarizer` class, used for interacting with LLMs for code summarization tasks. 8 | 9 | ## Initialization 10 | 11 | Details on how to initialize the `Summarizer` (likely via `repo.get_summarizer()`). 12 | 13 | 16 | 17 | ## Methods 18 | 19 | ### `summarize_file(file_path: str) -> str` 20 | 21 | Summarizes the content of the specified file. 22 | 23 | * **Parameters:** 24 | * `file_path` (str): The path to the file within the repository. 25 | * **Returns:** 26 | * `str`: The summary generated by the LLM. 27 | * **Raises:** 28 | * `FileNotFoundError`: If the `file_path` does not exist in the repo. 29 | * `LLMError`: If there's an issue communicating with the LLM. 30 | 31 | 32 | ### `summarize_function(file_path: str, function_name: str) -> str` 33 | 34 | Summarizes a specific function within the specified file. 35 | 36 | * **Parameters:** 37 | * `file_path` (str): The path to the file containing the function. 38 | * `function_name` (str): The name of the function to summarize. 39 | * **Returns:** 40 | * `str`: The summary generated by the LLM. 41 | * **Raises:** 42 | * `FileNotFoundError`: If the `file_path` does not exist in the repo. 43 | * `SymbolNotFoundError`: If the function cannot be found in the file. 44 | * `LLMError`: If there's an issue communicating with the LLM. 45 | 46 | ### `summarize_class(file_path: str, class_name: str) -> str` 47 | 48 | Summarizes a specific class within the specified file. 49 | 50 | * **Parameters:** 51 | * `file_path` (str): The path to the file containing the class. 52 | * `class_name` (str): The name of the class to summarize. 53 | * **Returns:** 54 | * `str`: The summary generated by the LLM. 55 | * **Raises:** 56 | * `FileNotFoundError`: If the `file_path` does not exist in the repo. 57 | * `SymbolNotFoundError`: If the class cannot be found in the file. 58 | * `LLMError`: If there's an issue communicating with the LLM. 59 | 60 | ## Configuration 61 | 62 | Details on the configuration options (`OpenAIConfig`, etc.). 63 | This is typically handled when calling `repo.get_summarizer(config=...)` or via environment variables read by the default `OpenAIConfig`. 64 | 65 | The `Summarizer` currently uses `OpenAIConfig` for its LLM settings. When a `Summarizer` is initialized without a specific config object, it creates a default `OpenAIConfig` with the following parameters: 66 | 67 | * `api_key` (str, optional): Your OpenAI API key. Defaults to the `OPENAI_API_KEY` environment variable. If not found, an error will be raised. 68 | * `model` (str): The OpenAI model to use. Defaults to `"gpt-4o"`. 69 | * `temperature` (float): Sampling temperature for the LLM. Defaults to `0.7`. 70 | * `max_tokens` (int): The maximum number of tokens to generate in the summary. Defaults to `1000`. 71 | 72 | You can customize this by creating an `OpenAIConfig` instance and passing it to `repo.get_summarizer()`: 73 | 74 | ```python 75 | from kit.summaries import OpenAIConfig 76 | 77 | # Example: Customize model and temperature 78 | my_config = OpenAIConfig(model="o3-mini", temperature=0.2) 79 | summarizer = repo.get_summarizer(config=my_config) 80 | 81 | # Now summarizer will use o3-mini with temperature 0.2 82 | summary = summarizer.summarize_file("path/to/your/file.py") 83 | ``` 84 | -------------------------------------------------------------------------------- /docs/src/content/docs/api/summary-searcher.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: SummarySearcher API 3 | description: API documentation for the SummarySearcher class. 4 | --- 5 | 6 | The `SummarySearcher` class provides a simple way to query an index built by [`DocstringIndexer`](/api/docstring-indexer). It takes a search query, embeds it using the same embedding function used for indexing, and retrieves the most semantically similar summaries from the vector database. 7 | 8 | ## Constructor 9 | 10 | **Class: `SummarySearcher`** 11 | *(defined in `kit/docstring_indexer.py`)* 12 | 13 | The `SummarySearcher` is typically initialized with an instance of `DocstringIndexer`. It uses the `DocstringIndexer`'s configured backend and embedding function to perform searches. 14 | 15 | ```python 16 | from kit.docstring_indexer import DocstringIndexer, SummarySearcher 17 | 18 | # Assuming 'indexer' is an already initialized DocstringIndexer instance 19 | # indexer = DocstringIndexer(repo=my_repo, summarizer=my_summarizer) 20 | # indexer.build() # Ensure the index is built 21 | 22 | searcher = SummarySearcher(indexer=indexer) 23 | ``` 24 | 25 | **Parameters:** 26 | 27 | * **`indexer`** (`DocstringIndexer`, required): 28 | An instance of `DocstringIndexer` that has been configured and preferably has had its `build()` method called. The `SummarySearcher` will use this indexer's `backend` and `embed_fn`. See the [`DocstringIndexer API docs`](./docstring-indexer) for more details on the indexer. 29 | 30 | ## Methods 31 | 32 | ### `search` 33 | 34 | **Method: `SummarySearcher.search`** 35 | *(defined in `kit/docstring_indexer.py`)* 36 | 37 | Embeds the given `query` string and searches the vector database (via the indexer's backend) for the `top_k` most similar document summaries. 38 | 39 | ```python 40 | query_text = "How is user authentication handled?" 41 | results = searcher.search(query=query_text, top_k=3) 42 | 43 | for result in results: 44 | print(f"Found in: {result.get('file_path')} ({result.get('symbol_name')})") 45 | print(f"Score: {result.get('score')}") 46 | print(f"Summary: {result.get('summary')}") 47 | print("----")} 48 | ``` 49 | 50 | **Parameters:** 51 | 52 | * **`query`** (`str`, required): 53 | The natural language query string to search for. 54 | * **`top_k`** (`int`, default: `5`): 55 | The maximum number of search results to return. 56 | 57 | **Returns:** `List[Dict[str, Any]]` 58 | 59 | A list of dictionaries, where each dictionary represents a search hit. 60 | Each hit typically includes metadata, a score, an ID, and the summary text. 61 | -------------------------------------------------------------------------------- /docs/src/content/docs/changelog.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Changelog" 3 | description: "Track changes and improvements in Kit releases" 4 | --- 5 | 6 | # Changelog 7 | 8 | All notable changes to Kit will be documented in this file. 9 | 10 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 11 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 12 | 13 | ## [0.6.3] 14 | 15 | ### 🐛 Bug Fixes 16 | 17 | - **Symbol Type Extraction Fix**: Fixed bug where some symbol types were incorrectly processed 18 | - Classes and other symbol types no longer have characters incorrectly stripped 19 | - Added comprehensive test coverage for symbol type processing edge cases 20 | 21 | --- 22 | 23 | ## [0.6.2] 24 | 25 | ### 🎉 Major Features 26 | 27 | - **Ollama Support**: Complete local LLM inference support with Ollama 28 | - Zero-cost PR reviews with local models 29 | - Support for popular models like DeepSeek R1, Qwen2.5-coder, CodeLlama 30 | - Automatic provider detection from model names (e.g., `deepseek-r1:latest`) 31 | - First-class integration with kit's repository intelligence 32 | 33 | - **DeepSeek R1 Reasoning Model Support** 34 | - **Thinking Token Stripping**: Automatically removes `...` tags from reasoning models 35 | - Clean, professional output without internal reasoning clutter 36 | - Preserves the analytical capabilities while improving output quality 37 | - Works in both summarization and PR review workflows 38 | 39 | - **Plain Output Mode**: New `--plain` / `-p` flag for pipe-friendly output 40 | - Removes all formatting and status messages 41 | - Perfect for piping to Claude Code or other AI tools 42 | - Enables powerful multi-stage AI workflows (e.g., `kit review -p | claude`) 43 | - Quiet mode suppresses all progress/status output 44 | 45 | ### ✨ Enhanced Features 46 | 47 | - **CLI Improvements** 48 | - Added `--version` flag to display current kit version 49 | - Model override support: `--model` / `-m` flag for per-review model selection 50 | - Better error messages and help text 51 | 52 | - **Documentation** 53 | - Comprehensive Ollama integration guides 54 | - Claude Code workflow examples 55 | - Multi-stage AI analysis patterns 56 | - Updated CLI reference with new flags 57 | 58 | ### 🔧 Developer Experience 59 | 60 | - **Community** 61 | - Added Discord community server for support and discussions 62 | - Improved README with better getting started instructions 63 | 64 | - **Testing** 65 | - Comprehensive test suite for thinking token stripping 66 | - Ollama integration tests with mock scenarios 67 | - PR reviewer test coverage for new features 68 | 69 | ### 💰 Cost Optimization 70 | 71 | - **Free Local Analysis**: Use Ollama for zero-cost code analysis 72 | - **Hybrid Workflows**: Combine free local analysis with premium cloud implementation 73 | - **Provider Switching**: Automatic provider detection and switching 74 | 75 | --- 76 | 77 | ## [0.6.1] 78 | 79 | ### 🔧 Improvements 80 | 81 | - Enhanced line number accuracy in PR reviews 82 | - Improved debug output for troubleshooting 83 | - Better test coverage for core functionality 84 | - Performance optimizations for large repositories 85 | 86 | ### 🐛 Bug Fixes 87 | 88 | - Fixed edge cases in symbol extraction 89 | - Improved error handling for malformed diffs 90 | - Better validation for GitHub URLs 91 | 92 | --- 93 | 94 | ## [0.6.0] 95 | 96 | ### 🎉 Major Features 97 | 98 | - Advanced PR reviews 99 | - Enhanced line number context and accuracy fore reviews 100 | - Comprehensive cost tracking and pricing updates for reviews 101 | - Improved repository intelligence with better symbol analysis 102 | 103 | ### ✨ Enhanced Features 104 | 105 | - Better diff parsing and analysis 106 | - Enhanced file prioritization algorithms for reviews 107 | - Improved cost breakdown reporting 108 | 109 | --- 110 | 111 | ## Links 112 | 113 | - [GitHub Releases](https://github.com/cased/kit/releases) 114 | - [Issues](https://github.com/cased/kit/issues) -------------------------------------------------------------------------------- /docs/src/content/docs/core-concepts/context-assembly.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Assembling Context 3 | --- 4 | 5 | When you send code to an LLM you usually **don’t** want the entire repository – 6 | just the *most relevant* bits. `ContextAssembler` helps you stitch those bits 7 | together into a single prompt-sized string. 8 | 9 | ## Why you need it 10 | 11 | * **Token limits** – GPT-4o tops out at ~128k tokens; some models less. 12 | * **Signal-to-noise** – Cut boilerplate, focus the model on what matters. 13 | * **Automatic truncation** – Keeps prompts within your chosen character budget. 14 | 15 | ## Quick start 16 | 17 | ```python 18 | from kit import Repository, ContextAssembler 19 | 20 | repo = Repository("/path/to/project") 21 | 22 | # Assume you already have chunks, e.g. from repo.search_semantic() 23 | chunks = repo.search_text("jwt decode") 24 | 25 | assembler = ContextAssembler(max_chars=12_000) 26 | context = assembler.from_chunks(chunks) 27 | 28 | print(context) # → Ready to drop into your chat prompt 29 | ``` 30 | 31 | `chunks` can be any list of dicts that include a `code` key – the helper trims 32 | and orders them by length until the budget is filled. 33 | 34 | ### Fine-tuning 35 | 36 | | Parameter | Default | Description | 37 | |-----------|---------|-------------| 38 | | `max_chars` | `12000` | Rough character cap for the final string. | 39 | | `separator` | `"\n\n---\n\n"` | Separator inserted between chunks. | 40 | | `header` / `footer` | `""` | Optional strings prepended/appended. | 41 | 42 | ```python 43 | assembler = ContextAssembler( 44 | max_chars=8000, 45 | header="### Code context\n", 46 | footer="\n### End context", 47 | ) 48 | ``` 49 | 50 | ## Combining with other tools 51 | 52 | 1. **Vector search → assemble → chat** 53 | ```python 54 | chunks = repo.search_semantic("retry backoff", embed_fn, top_k=10) 55 | prompt = assembler.from_chunks(chunks) 56 | response = my_llm.chat(prompt + "\n\nQ: …") 57 | ``` 58 | 2. **Docstring search first** – Use `SummarySearcher` for high-level matches, 59 | then pull full code for those files via `repo.context`. 60 | 3. **Diff review bots** – Feed only the changed lines + surrounding context. 61 | 62 | ## API reference 63 | 64 | ```python 65 | from kit.llm_context import ContextAssembler 66 | ``` 67 | 68 | ### `__init__(repo, *, title=None)` 69 | 70 | Constructs a new `ContextAssembler`. 71 | 72 | * `repo`: A `kit.repository.Repository` instance. 73 | * `title` (optional): A string to prepend to the assembled context. 74 | 75 | ### `from_chunks(chunks, max_chars=12000, separator="...", header="", footer="")` 76 | 77 | This is the primary method for assembling context from a list of code chunks. 78 | 79 | * `chunks`: A list of dictionaries, each with a `"code"` key. 80 | * `max_chars`: Target maximum character length for the output string. 81 | * `separator`: String to insert between chunks. 82 | * `header` / `footer`: Optional strings to wrap the entire context. 83 | 84 | Returns a single string with concatenated, truncated chunks. 85 | 86 | ### Other methods 87 | 88 | While `from_chunks` is the most common entry point, `ContextAssembler` also offers methods to add specific types of context if you're building a prompt manually: 89 | 90 | * `add_diff(diff_text)`: Adds a Git diff. 91 | * `add_file(file_path, highlight_changes=False)`: Adds the full content of a file. 92 | * `add_symbol_dependencies(file_path, max_depth=1)`: Adds content of files that `file_path` depends on. 93 | * `add_search_results(results, query)`: Formats and adds semantic search results. 94 | * `format_context()`: Returns the accumulated context as a string. 95 | -------------------------------------------------------------------------------- /docs/src/content/docs/core-concepts/repository-api.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Repository Interface 3 | --- 4 | 5 | import { Aside } from "@astrojs/starlight/components"; 6 | 7 | The `kit.Repository` object is the backbone of the library. It serves as your primary interface for accessing, analyzing, and understanding codebases, regardless of their language or location (local path or remote Git URL). 8 | 9 | ## Why the `Repository` Object? 10 | 11 | Interacting directly with code across different languages, file structures, and potential locations (local vs. remote) can be cumbersome. The `Repository` object provides a **unified and consistent abstraction layer** to handle this complexity. 12 | 13 | Key benefits include: 14 | 15 | - **Unified Access:** Provides a single entry point to read files, extract code structures (symbols), perform searches, and more. 16 | - **Location Agnostic:** Works seamlessly with both local file paths and remote Git repository URLs (handling cloning and caching automatically when needed). 17 | - **Language Abstraction:** Leverages `tree-sitter` parsers under the hood to understand the syntax of various programming languages, allowing you to work with symbols (functions, classes, etc.) in a standardized way. 18 | - **Foundation for Tools:** Acts as the foundation upon which you can build higher-level developer tools and workflows, such as documentation generators, AI code reviewers, or semantic search engines. 19 | 20 | ## What Can You Do with a `Repository`? 21 | 22 | Once you instantiate a `Repository` object pointing to your target codebase: 23 | 24 | ```python 25 | from kit import Repository 26 | 27 | # Point to a local project 28 | my_repo = Repository("/path/to/local/project") 29 | 30 | # Or point to a remote GitHub repo 31 | # github_repo = Repository("https://github.com/owner/repo-name") 32 | 33 | # Or analyze a specific version 34 | # versioned_repo = Repository("https://github.com/owner/repo-name", ref="v1.2.3") 35 | ``` 36 | 37 | You can perform various code intelligence tasks: 38 | 39 | - **Explore Structure:** Get the file tree (`.get_file_tree()`). 40 | - **Read Content:** Access the raw content of specific files (`.get_file_content()`). 41 | - **Understand Code:** Extract detailed information about functions, classes, and other symbols (`.extract_symbols()`). 42 | - **Access Git Metadata:** Get current commit SHA, branch, and remote URL (`.current_sha`, `.current_branch`, `.remote_url`). 43 | - **Search & Navigate:** Find text patterns (`.search_text()`) or semantically similar code (`.search_semantic()`). 44 | - **Analyze Dependencies:** Find where symbols are defined and used (`.find_symbol_usages()`). 45 | - **Prepare for LLMs:** Chunk code intelligently by lines or symbols (`.chunk_file_by_lines()`, `.chunk_file_by_symbols()`) and get code context around specific lines (`.extract_context_around_line()`). 46 | - **Integrate with AI:** Obtain configured summarizers (`.get_summarizer()`) or vector searchers (`.get_vector_searcher()`) for advanced AI workflows. 47 | - **Export Data:** Save the file tree, symbol information, or full repository index to structured formats like JSON (`.write_index()`, `.write_symbols()`, etc.). 48 | 49 | The following table lists some of the key classes and tools you can access through the `Repository` object: 50 | 51 | | Class/Tool | Description | 52 | | ------------------ | ---------------------------------------------- | 53 | | `Summarizer` | Generate summaries of code using LLMs | 54 | | `VectorSearcher` | Query vector index of code for semantic search | 55 | | `DocstringIndexer` | Build vector index of LLM-generated summaries | 56 | | `SummarySearcher` | Query that index | 57 | 58 | 59 | 63 | 64 | 65 | 73 | -------------------------------------------------------------------------------- /docs/src/content/docs/core-concepts/search-approaches.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Searching 3 | --- 4 | 5 | Not sure **which `kit` feature to reach for**? Use this page as a mental map of 6 | search-and-discovery tools – from plain-text grep all the way to LLM-powered 7 | semantic retrieval. 8 | 9 | ## Decision table 10 | 11 | | Your goal | Best tool | One-liner | Docs | 12 | |-----------|-----------|-----------|------| 13 | | Find an exact string or regex | `repo.search_text()` | `repo.search_text("JWT", "*.go")` | [Text search](/docs/core-concepts/semantic-search#exact-keyword) | 14 | | List symbols in a file | `repo.extract_symbols()` | `repo.extract_symbols("src/db.py")` | [Repository API](/docs/core-concepts/repository-api) | 15 | | See where a function is used | `repo.find_symbol_usages()` | `repo.find_symbol_usages("login")` | ^ | 16 | | Get a concise overview of a file / function | `Summarizer` | `summarizer.summarize_file(path)` | [Code summarization](/docs/core-concepts/code-summarization) | 17 | | Semantic search over **raw code chunks** | `VectorSearcher` | `repo.search_semantic()` | [Semantic search](/docs/core-concepts/semantic-search) | 18 | | Semantic search over **LLM summaries** | `DocstringIndexer` + `SummarySearcher` | see below | [Docstring index](/docs/core-concepts/docstring-indexing) | 19 | | Build an LLM prompt with only the *relevant* code | `ContextAssembler` | `assembler.from_chunks(chunks)` | [Context assembly](/docs/core-concepts/context-assembly) | 20 | 21 | > **Tip:** You can mix-and-match. For instance, run a docstring search first, 22 | > then feed the matching files into `ContextAssembler` for an LLM chat. 23 | 24 | ## Approaches in detail 25 | 26 | ### 1. Plain-text / regex search 27 | 28 | Fast, zero-setup, works everywhere. Use when you *know* what string you’re 29 | looking for. 30 | 31 | ```python 32 | repo.search_text("parse_jwt", file_pattern="*.py") 33 | ``` 34 | 35 | ### 2. Symbol indexing 36 | 37 | `extract_symbols()` uses **tree-sitter** queries (Python, JS, Go, etc.) to list 38 | functions, classes, variables – handy for nav trees or refactoring tools. 39 | 40 | ### 3. LLM summarization 41 | 42 | Generate natural-language summaries for files, classes, or functions with 43 | `Summarizer`. Great for onboarding or API docs. 44 | 45 | ### 4. Vector search (raw code) 46 | 47 | `VectorSearcher` chunks code (symbols or lines) → embeds chunks → stores them in 48 | a local vector database. Good when wording of the query is *similar* to the 49 | code. 50 | 51 | ### 5. Docstring vector search 52 | 53 | `DocstringIndexer` first *summarizes* code, then embeds the summary. The 54 | resulting vectors capture **intent**, not syntax; queries like “retry back-off 55 | logic” match even if the code uses exponential delays without those words. 56 | 57 | --- 58 | 59 | Still unsure? Start with text-search (cheap), move to vector search (smart), 60 | and layer summaries when you need *meaning* over *matching*. 61 | -------------------------------------------------------------------------------- /docs/src/content/docs/development/running-tests.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Running Tests 3 | --- 4 | 5 | To run tests using uv and pytest, first ensure you have the development dependencies installed: 6 | 7 | ```sh 8 | # Install all deps 9 | uv pip install -e . 10 | ``` 11 | 12 | Then, run the full test suite using: 13 | 14 | ```sh 15 | uv run pytest 16 | ``` 17 | 18 | Or to run a specific test file: 19 | 20 | ```sh 21 | uv run pytest tests/test_hcl_symbols.py 22 | ``` 23 | 24 | ## Code Style and Formatting 25 | 26 | Kit uses [Ruff](https://docs.astral.sh/ruff/) for linting, formatting, and import sorting with a line length of 120 characters. Our configuration can be found in `pyproject.toml`. 27 | 28 | To check your code against our style guidelines: 29 | 30 | ```sh 31 | # Run linting checks 32 | ruff check . 33 | 34 | # Check format (doesn't modify files) 35 | ruff format --check . 36 | ``` 37 | 38 | To automatically fix linting issues and format your code: 39 | 40 | ```sh 41 | # Fix linting issues 42 | ruff check --fix . 43 | 44 | # Format code 45 | ruff format . 46 | ``` 47 | 48 | These checks are enforced in CI, so we recommend running them locally before pushing changes. 49 | -------------------------------------------------------------------------------- /docs/src/content/docs/extending/adding-languages.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding New Languages 3 | --- 4 | 5 | - To add a new language: 6 | 1. Add a tree-sitter grammar and build it (see [tree-sitter docs](https://tree-sitter.github.io/tree-sitter/creating-parsers)). 7 | 2. Add a `queries//tags.scm` file with queries for symbols you want to extract. 8 | 3. Add the file extension to `TreeSitterSymbolExtractor.LANGUAGES`. 9 | 4. Write/expand tests for the new language. 10 | 11 | **Why?** 12 | - This approach lets you support any language with a tree-sitter grammar—no need to change core logic. 13 | - `tags.scm` queries make symbol extraction flexible and community-driven. 14 | -------------------------------------------------------------------------------- /docs/src/content/docs/introduction/overview.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | --- 4 | 5 | ## kit: Code Intelligence Toolkit 6 | 7 | A modular, production-grade toolkit for codebase mapping, symbol extraction, code search, and LLM-powered developer workflows. Supports multi-language codebases via `tree-sitter`. 8 | 9 | `kit` features a "mid-level API" to build your own custom tools, applications, agents, and workflows: easily build code review bots, semantic code search, documentation generators, and more. 10 | 11 | `kit` is **free and open source** with a permissive MIT license. Check it out on [GitHub](https://github.com/cased/kit). 12 | 13 | ## Installation 14 | ### Install from PyPI 15 | ```bash 16 | # Basic installation (includes PR reviewer, no ML dependencies) 17 | pip install cased-kit 18 | 19 | # With semantic search features (includes PyTorch, sentence-transformers) 20 | pip install cased-kit[ml] 21 | 22 | # Everything (all features) 23 | pip install cased-kit[all] 24 | ``` 25 | 26 | ### Install from Source 27 | ```bash 28 | git clone https://github.com/cased/kit.git 29 | cd kit 30 | uv venv .venv 31 | source .venv/bin/activate 32 | uv pip install -e . 33 | ``` 34 | 35 | ## Why Use kit? 36 | 37 | `kit` helps with: 38 | 39 | * **Unifying Code Access:** Provides a single, consistent `Repository` object to interact with files, symbols, and search across diverse codebases, regardless of language. 40 | * **Deep Code Understanding:** Leverages `tree-sitter` for accurate, language-specific parsing, enabling reliable symbol extraction and structural analysis across an entire codebase. 41 | * **Bridging Code and LLMs:** Offers tools specifically designed to chunk code effectively and retrieve relevant context for large language models, powering smarter AI developer tools. 42 | 43 | ## Core Philosophy 44 | 45 | `kit` aims to be a **toolkit** for building applications, agents, and workflows. 46 | It handles the low-level parsing and indexing complexity, and allows you to adapt these components to your specific needs. 47 | 48 | We believe the building blocks for code intelligence and LLM workflows for developer tools should be free and open source, 49 | so you can build amazing products and experiences. 50 | 51 | 52 | ## Where to Go Next 53 | 54 | * **Dive into the API:** Explore the [Core Concepts](/core-concepts/repository-api) to understand the `Repository` object and its capabilities. 55 | * **Build Something:** Follow the [Tutorials](/tutorials/ai_pr_reviewer) for step-by-step guides on creating practical tools. 56 | 57 | ## LLM Documentation 58 | 59 | This documentation site provides generated text files suitable for LLM consumption: 60 | 61 | - [`/llms.txt`](/llms.txt): Entrypoint file following the llms.txt standard. 62 | - [`/llms-full.txt`](/llms-full.txt): Complete documentation content concatenated into a single file. 63 | - [`/llms-small.txt`](/llms-small.txt): Minified documentation content for models with smaller context windows. 64 | -------------------------------------------------------------------------------- /docs/src/content/docs/introduction/quickstart.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Quickstart 3 | --- 4 | 5 | ```bash 6 | git clone https://github.com/cased/kit.git 7 | cd kit 8 | uv venv .venv 9 | source .venv/bin/activate 10 | uv pip install -e . 11 | ``` 12 | 13 | Now, you can use kit! 14 | kit ships with a demonstration repository at `tests/fixtures/` you can use to get started. 15 | 16 | Try this simple Python script (e.g., save as `test_kit.py` in the `kit` directory you cloned): 17 | 18 | ```python 19 | import kit 20 | import os 21 | 22 | # Path to the demo repository 23 | repo_path = "tests/fixtures/realistic_repo" 24 | 25 | print(f"Loading repository at: {repo_path}") 26 | # Ensure you have cloned the 'kit' repository and are in its root directory 27 | # for this relative path to work correctly. 28 | repo = kit.Repository(repo_path) 29 | 30 | # Print the first 5 Python files found in the demo repo 31 | print("\nFound Python files in the demo repo (first 5):") 32 | count = 0 33 | for file in repo.files('*.py'): 34 | print(f"- {file.path}") 35 | count += 1 36 | if count >= 5: 37 | break 38 | 39 | if count == 0: 40 | print("No Python files found in the demo repository.") 41 | 42 | # Extract symbols from a specific file in the demo repo (e.g., app.py) 43 | target_file = 'app.py' 44 | print(f"\nExtracting symbols from {target_file} in the demo repo (first 5):") 45 | try: 46 | symbols = repo.extract_symbols(target_file) 47 | if symbols: 48 | for i, symbol in enumerate(symbols): 49 | print(f"- {symbol.name} ({symbol.kind}) at line {symbol.range.start.line}") 50 | if i >= 4: 51 | break 52 | else: 53 | print(f"No symbols found or file not parseable: {target_file}") 54 | except FileNotFoundError: 55 | print(f"File not found: {target_file}") 56 | except Exception as e: 57 | print(f"An error occurred extracting symbols: {e}") 58 | 59 | ``` 60 | 61 | Run it with `python test_kit.py`. 62 | 63 | Next, explore the [Usage Guide](/introduction/usage-guide) to understand the core concepts. 64 | -------------------------------------------------------------------------------- /docs/src/content/docs/mcp/using-kit-with-mcp.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using kit with MCP 3 | description: Learn how to use kit with the Model Context Protocol (MCP) for AI-powered code understanding 4 | --- 5 | 6 | Note: MCP support is currently in alpha. 7 | 8 | The Model Context Protocol (MCP) provides a unified API for codebase operations, making it easy to integrate kit's capabilities with AI tools and IDEs. This guide will help you set up and use kit with MCP. 9 | 10 | Kit provides a MCP server implementation that exposes its code intelligence capabilities through a standardized protocol. When using kit as an MCP server, you gain access to: 11 | 12 | - **Code Search**: Perform text-based and semantic code searches 13 | - **Code Analysis**: Extract symbols, find symbol usages, and analyze dependencies 14 | - **Code Summarization**: Create natural language summaries of code 15 | - **File Navigation**: Explore file trees and repository structure 16 | 17 | This document guides you through setting up and using `kit` with MCP-compatible tools like Cursor or Claude Desktop. 18 | 19 | ## What is MCP? 20 | 21 | MCP (Model Context Protocol) is a specification that allows AI agents and development tools to interact with your codebase programmatically via a local server. `kit` implements an MCP server to expose its code intelligence features. 22 | 23 | ## Available MCP Tools in `kit` 24 | 25 | Currently, `kit` exposes the following functionalities via MCP tools: 26 | 27 | * `open_repository`: Opens a local or remote Git repository. Supports `ref` parameter for specific commits, tags, or branches. 28 | * `get_file_tree`: Retrieves the file and directory structure of the open repository. 29 | * `get_file_content`: Reads the content of a specific file. 30 | * `search_code`: Performs text-based search across repository files. 31 | * `extract_symbols`: Extracts functions, classes, and other symbols from a file. 32 | * `find_symbol_usages`: Finds where a specific symbol is used across the repository. 33 | * `get_code_summary`: Provides AI-generated summaries for files, functions, or classes. 34 | * `get_git_info`: Retrieves git metadata including current SHA, branch, and remote URL. 35 | 36 | ### Opening Repositories with Specific Versions 37 | 38 | The `open_repository` tool supports analyzing specific versions of repositories using the optional `ref` parameter: 39 | 40 | ```json 41 | { 42 | "tool": "open_repository", 43 | "arguments": { 44 | "path_or_url": "https://github.com/owner/repo", 45 | "ref": "v1.2.3" 46 | } 47 | } 48 | ``` 49 | 50 | The `ref` parameter accepts: 51 | - **Commit SHAs**: `"abc123def456"` 52 | - **Tags**: `"v1.2.3"`, `"release-2024"` 53 | - **Branches**: `"main"`, `"develop"`, `"feature-branch"` 54 | 55 | ### Accessing Git Metadata 56 | 57 | Use the `get_git_info` tool to access repository metadata: 58 | 59 | ```json 60 | { 61 | "tool": "get_git_info", 62 | "arguments": { 63 | "repo_id": "your-repo-id" 64 | } 65 | } 66 | ``` 67 | 68 | This returns information like current commit SHA, branch name, and remote URL - useful for understanding what version of code you're analyzing. 69 | 70 | More MCP features are coming soon. 71 | 72 | ## Setup 73 | 74 | 1. After installing `kit`, configure your MCP-compatible client by adding a stanza like this to your settings: 75 | 76 | Available environment variables for the `env` section: 77 | - `OPENAI_API_KEY` 78 | - `KIT_MCP_LOG_LEVEL` 79 | 80 | ```json 81 | { 82 | "mcpServers": { 83 | "kit-mcp": { 84 | "command": "python", 85 | "args": ["-m", "kit.mcp"], 86 | "env": { 87 | "KIT_MCP_LOG_LEVEL": "DEBUG" 88 | } 89 | } 90 | } 91 | } 92 | ``` 93 | 94 | The `python` executable invoked must be the one where `cased-kit` is installed. 95 | If you see `ModuleNotFoundError: No module named 'kit'`, ensure the Python 96 | interpreter your MCP client is using is the correct one. -------------------------------------------------------------------------------- /docs/src/content/docs/tutorials/docstring_search.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Build a Docstring Search Engine 3 | --- 4 | 5 | In this tutorial you'll build a semantic search tool on top of `kit` 6 | using **docstring-based indexing**. 7 | 8 | Why docstrings? Summaries distill *intent* rather than syntax. Embedding these 9 | short natural-language strings lets the vector DB focus on meaning, giving you 10 | relevant hits even when the literal code differs (e.g., `retry()` vs 11 | `attempt_again()`). It also keeps the index small (one embedding per file or 12 | symbol instead of dozens of raw-code chunks). 13 | 14 | --- 15 | 16 | ## 1. Install dependencies 17 | 18 | ```bash 19 | uv pip install kit sentence-transformers chromadb 20 | ``` 21 | 22 | ## 2. Initialise a repo and summarizer 23 | 24 | ```python 25 | import kit 26 | from kit import Repository, DocstringIndexer, Summarizer, SummarySearcher 27 | from sentence_transformers import SentenceTransformer 28 | 29 | REPO_PATH = "/path/to/your/project" 30 | repo = Repository(REPO_PATH) 31 | 32 | summarizer = repo.get_summarizer() # defaults to OpenAIConfig 33 | ``` 34 | 35 | ## 3. Build the docstring index 36 | 37 | ```python 38 | embed_model = SentenceTransformer("all-MiniLM-L6-v2") 39 | embed_fn = lambda txt: embed_model.encode(txt).tolist() 40 | 41 | indexer = DocstringIndexer(repo, summarizer, embed_fn) 42 | indexer.build() # writes REPO_PATH/.kit_cache/docstring_db 43 | ``` 44 | 45 | The first run will take time depending on repo size and LLM latency. 46 | Summaries are cached inside the vector DB (and in a meta.json within the persist_dir), 47 | so subsequent runs are cheap if code hasn't changed. 48 | 49 | ## 4. Query the index 50 | 51 | ```python 52 | searcher = indexer.get_searcher() 53 | 54 | results = searcher.search("How is the retry back-off implemented?", top_k=3) 55 | for hit in results: 56 | print(f"→ File: {hit.get('file_path', 'N/A')}\n Summary: {hit.get('summary', 'N/A')}") 57 | ``` 58 | 59 | You now have a semantic code searcher, using powerful docstring summaries, 60 | as easy as that. 61 | 62 | -------------------------------------------------------------------------------- /docs/src/content/docs/tutorials/dump_repo_map.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dump Repo Map 3 | --- 4 | 5 | import { Aside } from '@astrojs/starlight/components'; 6 | 7 | This tutorial explains how to use `kit` to dump a complete map of your repository—including the file tree and all extracted symbols—as a JSON file. This is useful for further analysis, visualization, or integration with other tools. `kit` provides a convenient method on the `Repository` object to achieve this directly. 8 | 9 | ## Step 1: Create the Script 10 | 11 | Create a Python script named `dump_repo_map.py` with the following content. This script uses `argparse` to accept the repository path and the desired output file path. 12 | 13 | ```python 14 | # dump_repo_map.py 15 | from kit import Repository # Import the main Repository class 16 | import argparse 17 | import sys 18 | import os 19 | 20 | def main(): 21 | parser = argparse.ArgumentParser(description="Dump a repository's file tree and symbols as JSON using kit.") 22 | parser.add_argument("repo_path", help="Path to the repository directory.") 23 | parser.add_argument("output_file", help="Path to the output JSON file.") 24 | args = parser.parse_args() 25 | 26 | repo_path = args.repo_path 27 | if not os.path.isdir(repo_path): 28 | print(f"Error: Repository path not found or not a directory: {repo_path}", file=sys.stderr) 29 | sys.exit(1) 30 | 31 | try: 32 | print(f"Initializing repository at: {repo_path}", file=sys.stderr) 33 | repo = Repository(repo_path) 34 | 35 | print(f"Dumping repository index to: {args.output_file}", file=sys.stderr) 36 | repo.write_index(args.output_file) # Use the direct method 37 | 38 | print(f"Successfully wrote repository map to {args.output_file}", file=sys.stderr) 39 | except Exception as e: 40 | print(f"Error processing repository: {e}", file=sys.stderr) 41 | sys.exit(1) 42 | 43 | if __name__ == "__main__": 44 | main() 45 | ``` 46 | 47 | --- 48 | 49 | ## Step 2: Run the Script 50 | 51 | Save the code above as `dump_repo_map.py`. You can then run it from your terminal, providing the path to the repository you want to map and the desired output file name: 52 | 53 | ```sh 54 | python dump_repo_map.py /path/to/repo repo_map.json 55 | ``` 56 | 57 | This will create a JSON file (e.g., `repo_map.json`) containing the structure and symbols of your codebase. 58 | 59 | --- 60 | 61 | ## Example JSON Output 62 | 63 | The output JSON file will contain a `file_tree` (also aliased as `files`) and a `symbols` map. 64 | 65 | ```json 66 | { 67 | "file_tree": [ 68 | { 69 | "path": "src", 70 | "is_dir": true, 71 | "name": "src", 72 | "size": 0 73 | }, 74 | { 75 | "path": "src/main.py", 76 | "is_dir": false, 77 | "name": "main.py", 78 | "size": 1024 79 | }, 80 | { 81 | "path": "README.md", 82 | "is_dir": false, 83 | "name": "README.md", 84 | "size": 2048 85 | } 86 | // ... more files and directories 87 | ], 88 | "files": [ 89 | // ... same content as file_tree ... 90 | ], 91 | "symbols": { 92 | "src/main.py": [ 93 | { 94 | "type": "function", 95 | "name": "main", 96 | "start_line": 10, 97 | "end_line": 25, 98 | "code": "def main():\n pass" 99 | }, 100 | { 101 | "type": "class", 102 | "name": "App", 103 | "start_line": 30, 104 | "end_line": 55 105 | } 106 | ], 107 | "src/utils.py": [ 108 | { 109 | "type": "function", 110 | "name": "helper", 111 | "start_line": 5, 112 | "end_line": 12 113 | } 114 | ] 115 | // ... more files and their symbols 116 | } 117 | } 118 | ``` 119 | 120 | 123 | 124 | --- 125 | 126 | ## Integration Ideas 127 | 128 | - Use the JSON output to feed custom dashboards or documentation tools. 129 | - Integrate with code search or visualization tools. 130 | - Use for code audits, onboarding, or automated reporting. 131 | 132 | --- 133 | 134 | ## Conclusion 135 | 136 | With `kit`, you can easily export a structured map of your repository using `repo.write_index()`, making this data readily available for various downstream use cases and custom tooling. 137 | -------------------------------------------------------------------------------- /docs/src/content/docs/tutorials/integrating_supersonic.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Integrating with Supersonic 3 | description: Using kit for code analysis and Supersonic for automated PR creation. 4 | --- 5 | 6 | import { Aside } from '@astrojs/starlight/components'; 7 | 8 | `kit` excels at understanding and analyzing codebases, while [Supersonic](https://github.com/cased/supersonic) provides a high-level Python API specifically designed for programmatically creating GitHub Pull Requests. Combining them allows you to build powerful workflows that analyze code, generate changes, and automatically propose those changes via PRs. 9 | 10 | 14 | 15 | ## The Workflow: Analyze with `kit`, Act with `Supersonic` 16 | 17 | A typical integration pattern looks like this: 18 | 19 | 1. **Analyze Code with `kit`**: Use `kit.Repository` methods like `extract_symbols`, `find_symbol_usages`, or `search_semantic` to understand the codebase or identify areas for modification. 20 | 2. **Generate Changes**: Based on the analysis (potentially involving an LLM), generate the new code content or identify necessary file modifications. 21 | 3. **Create PR with `Supersonic`**: Use `Supersonic`'s simple API (`create_pr_from_content`, `create_pr_from_file`, etc.) to package the generated changes into a new Pull Request on GitHub. 22 | 23 | ## Example: AI Refactoring Suggestion 24 | 25 | Imagine an AI tool that uses `kit` to analyze a Python file, identifies a potential refactoring, generates the improved code, and then uses `Supersonic` to create a PR. 26 | 27 | ```python 28 | import kit 29 | from supersonic import Supersonic 30 | import os 31 | 32 | # Assume kit.Repository is initialized with a local path 33 | LOCAL_REPO_PATH = "/path/to/your/local/repo/clone" 34 | # repo_analyzer = kit.Repository(LOCAL_REPO_PATH) 35 | # Note: kit analysis methods like extract_symbols would still be used here in a real scenario. 36 | 37 | # Assume 'ai_generate_refactoring' is your function that uses an LLM 38 | # potentially fed with context from kit (not shown here for brevity) 39 | def ai_generate_refactoring(original_code: str) -> str: 40 | # ... your AI logic here ... 41 | improved_code = original_code.replace("old_function", "new_function") # Simplified example 42 | return improved_code 43 | 44 | # --- Configuration --- 45 | GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") 46 | REPO_OWNER_SLASH_NAME = "your-org/your-repo" # For Supersonic PR creation 47 | RELATIVE_FILE_PATH = "src/legacy_module.py" # Relative path within the repo 48 | FULL_FILE_PATH = os.path.join(LOCAL_REPO_PATH, RELATIVE_FILE_PATH) 49 | TARGET_BRANCH = "main" # Or dynamically determine 50 | 51 | # --- Main Workflow --- 52 | 53 | try: 54 | # 1. Get original content (assuming local repo) 55 | if not os.path.exists(FULL_FILE_PATH): 56 | print(f"Error: File not found at {FULL_FILE_PATH}") 57 | exit() 58 | 59 | with open(FULL_FILE_PATH, 'r') as f: 60 | original_content = f.read() 61 | 62 | # 2. Generate Changes (using AI or other logic) 63 | refactored_content = ai_generate_refactoring(original_content) 64 | 65 | if refactored_content != original_content: 66 | # 3. Create PR with Supersonic 67 | supersonic_client = Supersonic(GITHUB_TOKEN) 68 | pr_title = f"AI Refactor: Improve {RELATIVE_FILE_PATH}" 69 | pr_body = f""" 70 | AI analysis suggests refactoring in `{RELATIVE_FILE_PATH}`. 71 | 72 | This PR applies the suggested changes. Please review carefully. 73 | """ 74 | 75 | pr_url = supersonic_client.create_pr_from_content( 76 | repo=REPO_OWNER_SLASH_NAME, 77 | content=refactored_content, 78 | upstream_path=RELATIVE_FILE_PATH, # Path within the target repo 79 | title=pr_title, 80 | description=pr_body, 81 | base_branch=TARGET_BRANCH, 82 | labels=["ai-refactor", "needs-review"], 83 | draft=True # Good practice for AI suggestions 84 | ) 85 | print(f"Successfully created PR: {pr_url}") 86 | else: 87 | print("No changes generated.") 88 | 89 | except Exception as e: 90 | print(f"An error occurred: {e}") 91 | 92 | ``` 93 | 94 | This example illustrates how `kit`'s analytical strengths can be combined with `Supersonic`'s action-oriented PR capabilities to build powerful code automation. 95 | -------------------------------------------------------------------------------- /docs/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /docs/src/styles/fonts/IBMPlexSansV.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cased/kit/7b2d248e06f9105dd51ef7968c0573041c19a80b/docs/src/styles/fonts/IBMPlexSansV.ttf -------------------------------------------------------------------------------- /docs/src/styles/fonts/iAWriterQuattroV.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cased/kit/7b2d248e06f9105dd51ef7968c0573041c19a80b/docs/src/styles/fonts/iAWriterQuattroV.ttf -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/base" 3 | } 4 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kit", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "cased-kit" 3 | version = "0.6.3" 4 | description = "A modular toolkit for LLM-powered codebase understanding." 5 | authors = [ 6 | { name = "Cased", email = "ted@cased.com" } 7 | ] 8 | readme = "README.md" 9 | requires-python = ">=3.10" 10 | license = {text = "MIT"} 11 | dependencies = [ 12 | "tree-sitter-language-pack>=0.7.2", 13 | "pathspec>=0.11.1", 14 | "pytest>=8.3.5", 15 | "numpy>=1.25", 16 | "fastapi==0.110.0", 17 | "uvicorn[standard]>=0.20", 18 | "typer>=0.9,<0.15", 19 | "click>=8.0,<8.2", 20 | "openai>=1.0.0", 21 | "tiktoken>=0.4.0", 22 | "anthropic>=0.20.0", 23 | "google-genai>=1.14.0", 24 | "python-hcl2>=7.2.0", 25 | "mypy", 26 | "ruff", 27 | "mcp>=1.8.0,<2.0.0", 28 | "redis>=5.0.0", 29 | "requests>=2.25.0", 30 | "pyyaml>=6.0", 31 | "types-PyYAML>=6.0.12.20250516", # Type stubs for yaml 32 | "types-requests>=2.32.0.20250515", # Type stubs for requests 33 | ] 34 | 35 | [project.urls] 36 | Homepage = "https://github.com/cased/kit" 37 | 38 | [project.scripts] 39 | kit = "kit.cli:app" 40 | kit-mcp = "kit.mcp:main" 41 | 42 | [tool.setuptools] 43 | package-dir = {"" = "src"} 44 | 45 | [tool.setuptools.packages.find] 46 | where = ["src"] 47 | 48 | [tool.setuptools.package-data] 49 | "kit.queries" = ["*/*.scm"] 50 | "kit" = ["queries/*/*/*.scm"] 51 | 52 | [build-system] 53 | requires = ["setuptools>=61.0"] 54 | build-backend = "setuptools.build_meta" 55 | 56 | [tool.pytest.ini_options] 57 | minversion = "6.0" 58 | addopts = "-ra -q" 59 | testpaths = [ 60 | "tests" 61 | ] 62 | python_files = "test_*.py" 63 | python_classes = "Test*" 64 | python_functions = "test_*" 65 | markers = [ 66 | "asyncio: mark test as asyncio to run with pytest-asyncio", 67 | "integration: marks tests as integration tests (may be slower)", 68 | "llm: marks tests that call LLM APIs (expensive, requires API keys)", 69 | "expensive: marks tests that are expensive/slow to run", 70 | "performance: marks tests that measure performance characteristics", 71 | "ci_skip: marks tests that should be skipped in CI environments", 72 | ] 73 | 74 | [tool.mypy] 75 | ignore_missing_imports = true 76 | 77 | [project.optional-dependencies] 78 | dev = [ 79 | "build", # build wheels 80 | "twine", # publish to PyPI 81 | ] 82 | test-api = [ 83 | "fastapi", # For TestClient 84 | "pytest" # Already in core, but good to list for a test group 85 | ] 86 | ml = [ 87 | "sentence-transformers>=2.2.0", # For VectorSearcher and DocstringIndexer 88 | "chromadb>=0.5.23", # Vector database for semantic search 89 | ] 90 | all = [ 91 | "sentence-transformers>=2.2.0", 92 | "chromadb>=0.5.23", 93 | ] 94 | 95 | [tool.ruff] 96 | # Set line length to 120 characters 97 | line-length = 120 98 | # Target Python 3.10 as specified in our requires-python 99 | target-version = "py310" 100 | 101 | # Configure linting 102 | [tool.ruff.lint] 103 | # Select these rule sets (categories) 104 | select = ["E", "F", "W", "I", "RUF"] 105 | ignore = [] 106 | 107 | # Configure isort rules 108 | [tool.ruff.lint.isort] 109 | known-first-party = ["kit", "cased_kit"] 110 | 111 | # Configure formatter 112 | [tool.ruff.format] 113 | # Formatting uses line-length from the top level 114 | -------------------------------------------------------------------------------- /scripts/benchmark.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from kit.repository import Repository as Repo 4 | 5 | 6 | def main(): 7 | import argparse 8 | 9 | parser = argparse.ArgumentParser(description="Benchmark kit repo indexing.") 10 | parser.add_argument("repo", nargs="?", default=".", help="Path to repo root (default: .)") 11 | args = parser.parse_args() 12 | repo = Repo(args.repo) 13 | 14 | print(f"Indexing repo at {args.repo} ...") 15 | start = time.time() 16 | idx = repo.index() 17 | elapsed = time.time() - start 18 | num_files = len(idx["file_tree"]) 19 | num_symbols = sum(len(syms) for syms in idx["symbols"].values()) 20 | print(f"Indexed {num_files} files, {num_symbols} symbols in {elapsed:.2f} seconds.") 21 | 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /scripts/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script runs linting and formatting checks using Ruff. 3 | # Pass --fix as the first argument to automatically apply fixes. 4 | 5 | # Exit immediately if a command exits with a non-zero status. 6 | set -e 7 | 8 | # Navigate to the root of the repository relative to the script directory 9 | cd "$(dirname "$0")/.." 10 | 11 | # Check the first argument 12 | if [ "$1" == "--fix" ]; then 13 | echo "Running Ruff to apply fixes (linting and formatting)..." 14 | # Apply lint rule fixes (autofixable ones) 15 | ruff check . --fix 16 | # Apply formatting fixes 17 | ruff format . 18 | echo "Ruff fixes applied successfully!" 19 | else 20 | echo "Running Ruff linter and formatting check (no fixes applied)..." 21 | # Ruff check combines linting and format checking 22 | ruff check . 23 | echo "Ruff checks passed successfully!" 24 | fi -------------------------------------------------------------------------------- /scripts/index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | CLI: Index a repo and print the file tree and symbols as JSON 4 | Usage: 5 | python scripts/index.py /path/to/repo 6 | """ 7 | 8 | import json 9 | import sys 10 | 11 | from kit import Repository 12 | 13 | if __name__ == "__main__": 14 | repo_path = sys.argv[1] if len(sys.argv) > 1 else "." 15 | repo = Repository(repo_path) 16 | index = repo.index() 17 | print(json.dumps(index, indent=2)) 18 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Ensure all deps (including vector search) are installed, then run tests 3 | 4 | export PYTHONPATH=src 5 | python -m pytest "$@" 6 | -------------------------------------------------------------------------------- /scripts/typecheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run mypy type checks for all source and test code 3 | export PYTHONPATH=src 4 | mypy src/kit 5 | mypy tests 6 | -------------------------------------------------------------------------------- /src/kit/api/__init__.py: -------------------------------------------------------------------------------- 1 | """kit REST API package.""" 2 | 3 | from .app import app # re-export for `uvicorn kit.api:app` 4 | -------------------------------------------------------------------------------- /src/kit/dependency_analyzer/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from .dependency_analyzer import DependencyAnalyzer 4 | 5 | __all__ = ["DependencyAnalyzer"] 6 | -------------------------------------------------------------------------------- /src/kit/llm_context.py: -------------------------------------------------------------------------------- 1 | """Utilities to assemble rich prompts for LLMs. 2 | 3 | This is intentionally lightweight – it glues together repository data 4 | (diff, file bodies, search hits, etc.) into a single string that can be 5 | fed straight into a chat completion. 6 | """ 7 | 8 | from __future__ import annotations 9 | 10 | from pathlib import Path 11 | from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence 12 | 13 | if TYPE_CHECKING: 14 | from .repository import Repository 15 | 16 | 17 | class ContextAssembler: 18 | """Collects pieces of context and spits out a prompt blob. 19 | 20 | Parameters 21 | ---------- 22 | repo 23 | A :class:`kit.repository.Repository` object representing the codebase 24 | we want to reason about. The assembler uses it to fetch file content 25 | and (in the future) symbol relationships. 26 | title 27 | Optional global title prepended to the context (not used by default). 28 | """ 29 | 30 | def __init__(self, repo: Repository, *, title: Optional[str] = None) -> None: 31 | self.repo = repo 32 | self._sections: List[str] = [] 33 | if title: 34 | self._sections.append(f"# {title}\n") 35 | 36 | def add_diff(self, diff: str) -> None: 37 | """Add a raw git diff section.""" 38 | if not diff.strip(): 39 | return 40 | self._sections.append("## Diff\n```diff\n" + diff.strip() + "\n```") 41 | 42 | def add_file( 43 | self, 44 | file_path: str, 45 | *, 46 | highlight_changes: bool = False, 47 | max_lines: int | None = None, 48 | max_bytes: int | None = None, 49 | skip_if_name_in: Optional[Sequence[str]] = None, 50 | ) -> None: 51 | """Embed full file content. 52 | 53 | If *highlight_changes* is true we still just inline raw content – 54 | markup is left to the caller/LLM. 55 | """ 56 | # Guard: skip by exact filename 57 | if skip_if_name_in and Path(file_path).name in skip_if_name_in: 58 | return 59 | 60 | try: 61 | code = self.repo.get_file_content(file_path) 62 | except FileNotFoundError: 63 | return 64 | 65 | # Guards: size limits 66 | if max_bytes is not None and len(code.encode("utf-8", "ignore")) > max_bytes: 67 | return 68 | if max_lines is not None and code.count("\n") + 1 > max_lines: 69 | return 70 | 71 | lang = Path(file_path).suffix.lstrip(".") or "text" 72 | header = f"## {file_path} (full)" if not highlight_changes else f"## {file_path} (with changes highlighted)" 73 | self._sections.append(f"{header}\n```{lang}\n{code}\n```") 74 | 75 | def add_search_results(self, results: Sequence[Dict[str, Any]], *, query: str) -> None: 76 | """Append semantic search matches to the context.""" 77 | if not results: 78 | return 79 | blob = [f"## Semantic search for: {query}"] 80 | for i, res in enumerate(results, 1): 81 | code = res.get("code") or res.get("snippet") or "" 82 | file = res.get("file", f"result_{i}") 83 | blob.append(f"### {file}\n```\n{code}\n```") 84 | self._sections.append("\n".join(blob)) 85 | 86 | def format_context(self) -> str: 87 | """Return the accumulated context.""" 88 | return "\n\n".join(self._sections) 89 | -------------------------------------------------------------------------------- /src/kit/mcp/__init__.py: -------------------------------------------------------------------------------- 1 | """kit.mcp – Model Context Protocol server wrapper.""" 2 | 3 | from __future__ import annotations 4 | 5 | from .main import main as main 6 | from .server import serve as serve 7 | 8 | __all__ = ["main", "serve"] 9 | -------------------------------------------------------------------------------- /src/kit/mcp/__main__.py: -------------------------------------------------------------------------------- 1 | """Entry point for the MCP module.""" 2 | 3 | from __future__ import annotations 4 | 5 | from .main import main 6 | 7 | if __name__ == "__main__": 8 | main() 9 | -------------------------------------------------------------------------------- /src/kit/mcp/main.py: -------------------------------------------------------------------------------- 1 | """Console-script entry point for the Kit MCP server.""" 2 | 3 | from __future__ import annotations 4 | 5 | import asyncio 6 | import logging 7 | import sys 8 | 9 | from .server import serve 10 | 11 | 12 | def main() -> None: 13 | """Launch the Kit MCP server.""" 14 | try: 15 | asyncio.run(serve()) 16 | except KeyboardInterrupt: 17 | logging.info("Server stopped by user") 18 | except Exception as e: # pragma: no cover 19 | logging.error(f"Server error: {e!s}", exc_info=True) 20 | sys.exit(1) 21 | 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /src/kit/pr_review/ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Kit Roadmap 2 | 3 | This roadmap outlines planned features and improvements for kit, prioritized by user feedback and strategic value. 4 | 5 | ## 📅 Planned Features 6 | 7 | #### Per-User/Per-Organization Custom Context 8 | Store custom guidelines, coding standards, and preferences that get automatically included in reviews. 9 | 10 | ```bash 11 | # Example usage 12 | kit profile create --name "company-standards" --file coding-guidelines.md 13 | kit review --profile company-standards 14 | ``` 15 | 16 | #### Feedback Learning System 17 | Simple database to store review feedback and adapt over time. 18 | 19 | ```bash 20 | # Example feedback workflow 21 | kit review # Generates review 22 | kit feedback --helpful/--not-helpful --notes "Missed performance issue" 23 | kit insights # Show what's working well 24 | ``` 25 | 26 | #### Inline Comments & GitHub Review API 27 | Post comments directly on specific lines instead of single review comment. 28 | 29 | ```bash 30 | kit review --mode inline # Line-by-line comments 31 | ``` 32 | 33 | ### 🎯 Medium Term (Q3-Q4 2025) 34 | 35 | #### Multi-Model Consensus 36 | Route different aspects to different models and aggregate insights. 37 | 38 | ```bash 39 | kit review --consensus # Use multiple models, combine results 40 | ``` 41 | 42 | #### Repository Context Learning 43 | Learn which types of context are most valuable and adapt automatically. 44 | 45 | #### IDE Integration 46 | Real-time suggestions in VS Code and other editors while coding. 47 | 48 | --- 49 | 50 | ## 🔧 Technical Improvements 51 | 52 | - **Model Router**: Intelligent routing to optimal models based on PR complexity 53 | - **Context Optimization**: Smarter context selection to maximize LLM effectiveness 54 | - **Plugin System**: Simple plugin architecture for custom analyzers 55 | 56 | --- 57 | 58 | ## 🎯 Success Metrics 59 | 60 | ### User Experience 61 | - **Review Relevance**: >80% of suggestions rated as helpful 62 | - **Response Time**: <30 seconds for standard reviews 63 | - **Cost Efficiency**: <$0.10 per review for typical usage 64 | - **Adoption Rate**: >90% of PRs reviewed within 1 hour 65 | 66 | ### Technical Quality 67 | - **Uptime**: >99.9% availability for cloud service 68 | - **Accuracy**: <5% false positive rate on issue detection 69 | - **Performance**: Support for repositories up to 1M lines of code 70 | - **Scalability**: Handle 10,000+ reviews per day per organization 71 | 72 | ### Business Impact 73 | - **Code Quality**: Measurable improvement in code quality metrics 74 | - **Development Velocity**: Faster PR review cycles 75 | - **Bug Reduction**: Fewer bugs in production 76 | - **Developer Satisfaction**: High satisfaction scores from development teams 77 | 78 | --- 79 | 80 | ## 📞 Get Involved 81 | 82 | - **Feature Requests**: [Open an issue](https://github.com/cased/kit/issues) with your ideas 83 | - **User Feedback**: Join our [Discord community](https://discord.gg/fbAVtCeU) soon for discussions 84 | - **Contributions**: Submit PRs for features you'd like to see 85 | 86 | --- 87 | -------------------------------------------------------------------------------- /src/kit/pr_review/__init__.py: -------------------------------------------------------------------------------- 1 | """PR Review functionality for kit.""" 2 | 3 | from .cache import RepoCache 4 | from .config import ReviewConfig 5 | from .reviewer import PRReviewer 6 | 7 | __all__ = ["PRReviewer", "RepoCache", "ReviewConfig"] 8 | -------------------------------------------------------------------------------- /src/kit/pr_review/__main__.py: -------------------------------------------------------------------------------- 1 | """Entry point for running the PR review debug CLI as a module.""" 2 | 3 | from .debug import app 4 | 5 | if __name__ == "__main__": 6 | app() 7 | -------------------------------------------------------------------------------- /src/kit/pr_review/debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Debug CLI for PR review testing.""" 3 | 4 | from typing import Optional 5 | 6 | import typer 7 | 8 | app = typer.Typer(help="Debug tools for PR review testing.") 9 | 10 | 11 | @app.command("review") 12 | def review_pr( 13 | pr_url: str, 14 | config: Optional[str] = typer.Option(None, "--config", "-c", help="Path to config file"), 15 | dry_run: bool = typer.Option(True, "--dry-run/--post", help="Run analysis but do not post comment"), 16 | ): 17 | """Review a GitHub PR using kit analysis for testing.""" 18 | try: 19 | from .config import ReviewConfig 20 | from .reviewer import PRReviewer 21 | 22 | # Load configuration 23 | if config: 24 | review_config = ReviewConfig.from_file(config) 25 | else: 26 | review_config = ReviewConfig.from_file() 27 | 28 | # Override post_as_comment if dry run 29 | if dry_run: 30 | review_config.post_as_comment = False 31 | 32 | # Run review 33 | reviewer = PRReviewer(review_config) 34 | result = reviewer.review_pr(pr_url) 35 | 36 | if dry_run: 37 | typer.echo("\n" + "=" * 50) 38 | typer.echo("REVIEW RESULT (DRY RUN)") 39 | typer.echo("=" * 50) 40 | typer.echo(result) 41 | else: 42 | typer.echo("✅ Review posted to PR") 43 | 44 | except Exception as e: 45 | typer.echo(f"❌ Error during review: {e}", err=True) 46 | raise typer.Exit(1) 47 | 48 | 49 | if __name__ == "__main__": 50 | app() 51 | -------------------------------------------------------------------------------- /src/kit/pr_review/example_reviews/biopython_204_documentation_fix.md: -------------------------------------------------------------------------------- 1 | # Example Review: Biopython Documentation Fix PR #204 2 | 3 | **Repository**: [biopython/biopython.github.io](https://github.com/biopython/biopython.github.io) 4 | **PR**: [#204 - Add correct import statement to FAQ](https://github.com/biopython/biopython.github.io/pull/204) 5 | **Status**: Merged ✅ 6 | **Changed Files**: 1 7 | **Review Model**: Claude Sonnet 4 8 | **Cost**: $0.0064 9 | 10 | ## PR Summary 11 | 12 | This is a simple documentation fix that adds a correct import statement to the Biopython Structural Bioinformatics FAQ. The author experienced an error when following the documentation and fixed it by adding the proper import statement that was missing. 13 | 14 | **Problem**: `MMCIF2Dict("5SWE.Cif")` was failing with "TypeError: 'module' object is not callable" because the import statement only imported the module, not the class. 15 | 16 | **Solution**: Added the correct import statement to make the code examples work properly. 17 | 18 | --- 19 | 20 | ## Kit AI Review Output 21 | 22 | ### Priority Issues 23 | No significant issues identified. This is a straightforward documentation improvement. 24 | 25 | ### Summary 26 | This PR adds a correct import statement to the Biopython Structural Bioinformatics FAQ documentation. The change appears to be a minor documentation fix, adding 2 lines to clarify proper import usage for users following the FAQ guide. 27 | 28 | Based on the file analysis showing: 29 | - Single markdown file modified (`wiki/The_Biopython_Structural_Bioinformatics_FAQ.md`) 30 | - 2 lines added, 0 lines removed 31 | - No symbols or code dependencies affected 32 | 33 | This is a low-risk documentation enhancement that should improve user experience by providing clearer import guidance. 34 | 35 | ### Recommendations 36 | 37 | **Documentation Quality:** 38 | - Since the actual diff content isn't accessible due to the 404 error, I recommend verifying that: 39 | - The import statement follows current Biopython conventions 40 | - The syntax is correct and tested 41 | - The import aligns with the specific FAQ context where it's being added 42 | 43 | **Process Improvement:** 44 | - Consider adding a brief comment in the PR description explaining which specific import was corrected and why, to help future maintainers understand the context 45 | 46 | **Validation:** 47 | - Ensure the import statement works with the current version of Biopython being documented 48 | - Verify the import is placed in the appropriate section of the FAQ for logical flow 49 | 50 | This appears to be a beneficial change that enhances the documentation's accuracy and user-friendliness with minimal risk. 51 | 52 | --- 53 | 54 | ## Review Quality Metrics 55 | 56 | - **File References**: 1 57 | - **Line References**: 0 58 | - **Specific Issues**: 1 59 | - **Vague Statements**: 0 60 | - **GitHub Links**: 0 61 | - **Code Relevance**: 0.8 62 | - **Change Coverage**: 1.0 63 | - **Overall Score**: 1.00/1.0 64 | 65 | ## Key Insights 66 | 67 | 1. **Proportional Response**: Kit provided an appropriately scaled review for a small documentation change 68 | 2. **Risk Assessment**: Correctly identified this as a low-risk documentation enhancement 69 | 3. **Context Understanding**: Recognized the fix was about import statement corrections 70 | 4. **Practical Recommendations**: Focused on documentation quality and validation rather than complex code issues 71 | 5. **Cost Efficiency**: Minimal cost ($0.0064) for appropriate level of analysis 72 | 73 | This review demonstrates Kit's ability to scale its analysis appropriately for different types of changes, providing useful feedback without over-engineering the response for simple fixes. -------------------------------------------------------------------------------- /src/kit/pr_review/example_reviews/fastapi_11935_standard_dependencies.md: -------------------------------------------------------------------------------- 1 | # Example Review: FastAPI PR #11935 2 | 3 | **Repository**: [tiangolo/fastapi](https://github.com/tiangolo/fastapi) 4 | **PR**: [#11935 - Add support for `pip install "fastapi[standard]"` with standard dependencies](https://github.com/tiangolo/fastapi/pull/11935) 5 | **Status**: Merged ✅ 6 | **Changed Files**: 15 7 | **Review Model**: Claude Sonnet 4 8 | **Cost**: $0.0340 9 | 10 | ## PR Summary 11 | 12 | This PR introduces a significant packaging change for FastAPI, moving from a "batteries-included" default installation to an opt-in approach for standard dependencies. The change allows users to install either: 13 | - `pip install fastapi` (minimal installation) 14 | - `pip install "fastapi[standard]"` (full experience with CLI and development tools) 15 | 16 | --- 17 | 18 | ## Kit AI Review Output 19 | 20 | ### Priority Issues 21 | 22 | **Medium Priority** 23 | - [fastapi/cli.py:12](https://github.com/fastapi/fastapi/blob/main/fastapi/cli.py#L12): The error handling raises a RuntimeError after printing the message, which could be confusing in programmatic usage. Consider using a more specific exception type like `ImportError` or `ModuleNotFoundError`. 24 | 25 | **Low Priority** 26 | - [tests/test_fastapi_cli.py:14](https://github.com/fastapi/fastapi/blob/main/tests/test_fastapi_cli.py#L14): The test uses coverage run which adds complexity. Consider testing the CLI functionality more directly or documenting why coverage is needed here. 27 | 28 | ### Summary 29 | 30 | This PR introduces a significant packaging change for FastAPI, moving from a "batteries-included" default installation to an opt-in approach for standard dependencies. Key changes include: 31 | 32 | - **New installation pattern**: Users now install `pip install "fastapi[standard]"` for the full experience, while `pip install fastapi` provides a minimal installation 33 | - **CLI integration**: Adds `python -m fastapi` support through new `__main__.py` and `cli.py` modules 34 | - **Graceful degradation**: When CLI dependencies aren't available, provides clear error messages with installation instructions 35 | - **Documentation updates**: Comprehensive updates across README and docs to reflect the new installation approach 36 | 37 | The architectural change simplifies the core FastAPI package while maintaining backward compatibility through optional dependencies. 38 | 39 | ### Recommendations 40 | 41 | **Error Handling Enhancement** 42 | ```python 43 | # In fastapi/cli.py, consider using a more specific exception: 44 | def main() -> None: 45 | if not cli_main: 46 | message = 'To use the fastapi command, please install "fastapi[standard]":\n\n\tpip install "fastapi[standard]"\n' 47 | print(message) 48 | raise ImportError(message) from None # More semantically correct 49 | ``` 50 | 51 | **Test Coverage Improvement** 52 | - Consider adding a test that verifies the CLI works when `fastapi-cli` is properly installed 53 | - The current test only covers the error case and uses subprocess with coverage, which may be brittle 54 | 55 | **Documentation Consistency** 56 | - Verify that all code examples in tutorials and documentation are updated to use the new installation pattern 57 | - Consider adding a migration guide for existing users 58 | 59 | **Cross-codebase Impact** 60 | - This change will affect all downstream projects and tutorials that reference FastAPI installation 61 | - Consider coordinating with the ecosystem (tutorials, courses, etc.) for a smooth transition 62 | - The change is well-documented but represents a significant shift in user experience 63 | 64 | The implementation is solid and the graceful error handling for missing CLI dependencies is well-designed. The packaging simplification aligns with modern Python practices of minimal default installations with optional extras. 65 | 66 | --- 67 | 68 | ## Review Quality Metrics 69 | 70 | - **File References**: 3 71 | - **Line References**: 4 72 | - **Specific Issues**: 7 73 | - **Vague Statements**: 0 74 | - **GitHub Links**: 2 75 | - **Code Relevance**: 1.0 76 | - **Change Coverage**: 1.0 77 | - **Overall Score**: 1.00/1.0 78 | 79 | ## Key Insights 80 | 81 | 1. **Comprehensive Analysis**: Kit identified both the immediate code changes and broader ecosystem impact 82 | 2. **Specific Recommendations**: Provided concrete code suggestions with rationale 83 | 3. **Quality Focus**: Flagged potential issues in error handling and test design 84 | 4. **Context Awareness**: Understood this was a breaking change requiring ecosystem coordination 85 | 86 | This review demonstrates Kit's ability to understand architectural changes and their broader implications beyond just code syntax. -------------------------------------------------------------------------------- /src/kit/pr_review/example_reviews/react_dev_6986_branding_menu.md: -------------------------------------------------------------------------------- 1 | # Example Review: React.dev PR #6986 2 | 3 | **Repository**: [reactjs/react.dev](https://github.com/reactjs/react.dev) 4 | **PR**: [#6986 - Add branding context menu](https://github.com/reactjs/react.dev/pull/6986) 5 | **Status**: Merged ✅ 6 | **Changed Files**: 8 7 | **Review Model**: Claude Sonnet 4 8 | **Cost**: $0.0118 9 | 10 | ## PR Summary 11 | 12 | This PR adds a context menu to the React logo in the top navigation that provides access to React branding materials. When users right-click on the logo, they can access brand assets including logos and wordmarks in both light and dark variants. 13 | 14 | --- 15 | 16 | ## Kit AI Review Output 17 | 18 | ### Priority Issues 19 | 20 | **Medium Priority:** 21 | - [src/components/Layout/TopNav/BrandMenu.tsx:1](https://github.com/rickhanlonii/react.dev/blob/main/src/components/Layout/TopNav/BrandMenu.tsx#L1) - Missing accessibility attributes for the dropdown menu. The menu button should have `aria-expanded`, `aria-haspopup`, and proper ARIA labeling for screen readers. 22 | 23 | - [src/components/Layout/TopNav/BrandMenu.tsx:1](https://github.com/rickhanlonii/react.dev/blob/main/src/components/Layout/TopNav/BrandMenu.tsx#L1) - No keyboard navigation support. Users should be able to navigate menu items with arrow keys and close with Escape key. 24 | 25 | **Low Priority:** 26 | - [src/components/Layout/TopNav/TopNav.tsx:1](https://github.com/reactjs/react.dev/blob/main/src/components/Layout/TopNav/TopNav.tsx#L1) - The modification to existing TopNav component may affect existing functionality. Ensure backward compatibility is maintained. 27 | 28 | ### Summary 29 | 30 | This PR adds a branding context menu to the top navigation, introducing: 31 | - A new `BrandMenu` component with dropdown functionality 32 | - Brand assets (logos and wordmarks) in both light and dark variants 33 | - Integration with the existing `TopNav` component 34 | - Addition of a new dependency in package.json 35 | 36 | The implementation appears to be a self-contained feature addition that provides users with access to React branding materials through a dropdown menu in the navigation bar. 37 | 38 | ### Recommendations 39 | 40 | **Accessibility Improvements:** 41 | ```tsx 42 | // Add proper ARIA attributes to the menu button 43 |