kodit 0.1.6__tar.gz → 0.1.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kodit might be problematic. Click here for more details.

Files changed (77) hide show
  1. {kodit-0.1.6 → kodit-0.1.7}/PKG-INFO +1 -1
  2. {kodit-0.1.6 → kodit-0.1.7}/docs/_index.md +3 -4
  3. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/_version.py +2 -2
  4. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/cli.py +45 -96
  5. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/indexing/repository.py +5 -3
  6. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/indexing/service.py +3 -1
  7. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/cli_test.py +4 -8
  8. {kodit-0.1.6 → kodit-0.1.7}/tests/smoke.sh +3 -8
  9. {kodit-0.1.6 → kodit-0.1.7}/.cursor/rules/kodit.mdc +0 -0
  10. {kodit-0.1.6 → kodit-0.1.7}/.github/CODE_OF_CONDUCT.md +0 -0
  11. {kodit-0.1.6 → kodit-0.1.7}/.github/CONTRIBUTING.md +0 -0
  12. {kodit-0.1.6 → kodit-0.1.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  13. {kodit-0.1.6 → kodit-0.1.7}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  14. {kodit-0.1.6 → kodit-0.1.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {kodit-0.1.6 → kodit-0.1.7}/.github/workflows/docker.yaml +0 -0
  16. {kodit-0.1.6 → kodit-0.1.7}/.github/workflows/docs.yaml +0 -0
  17. {kodit-0.1.6 → kodit-0.1.7}/.github/workflows/pypi-test.yaml +0 -0
  18. {kodit-0.1.6 → kodit-0.1.7}/.github/workflows/pypi.yaml +0 -0
  19. {kodit-0.1.6 → kodit-0.1.7}/.github/workflows/test.yaml +0 -0
  20. {kodit-0.1.6 → kodit-0.1.7}/.gitignore +0 -0
  21. {kodit-0.1.6 → kodit-0.1.7}/.python-version +0 -0
  22. {kodit-0.1.6 → kodit-0.1.7}/.vscode/launch.json +0 -0
  23. {kodit-0.1.6 → kodit-0.1.7}/.vscode/settings.json +0 -0
  24. {kodit-0.1.6 → kodit-0.1.7}/Dockerfile +0 -0
  25. {kodit-0.1.6 → kodit-0.1.7}/LICENSE +0 -0
  26. {kodit-0.1.6 → kodit-0.1.7}/README.md +0 -0
  27. {kodit-0.1.6 → kodit-0.1.7}/alembic.ini +0 -0
  28. {kodit-0.1.6 → kodit-0.1.7}/docs/developer/index.md +0 -0
  29. {kodit-0.1.6 → kodit-0.1.7}/pyproject.toml +0 -0
  30. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/.gitignore +0 -0
  31. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/__init__.py +0 -0
  32. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/alembic/README +0 -0
  33. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/alembic/__init__.py +0 -0
  34. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/alembic/env.py +0 -0
  35. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/alembic/script.py.mako +0 -0
  36. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/alembic/versions/85155663351e_initial.py +0 -0
  37. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/alembic/versions/__init__.py +0 -0
  38. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/app.py +0 -0
  39. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/bm25/__init__.py +0 -0
  40. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/bm25/bm25.py +0 -0
  41. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/config.py +0 -0
  42. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/database.py +0 -0
  43. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/indexing/__init__.py +0 -0
  44. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/indexing/models.py +0 -0
  45. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/logging.py +0 -0
  46. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/mcp.py +0 -0
  47. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/middleware.py +0 -0
  48. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/retreival/__init__.py +0 -0
  49. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/retreival/repository.py +0 -0
  50. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/retreival/service.py +0 -0
  51. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/snippets/__init__.py +0 -0
  52. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/snippets/languages/__init__.py +0 -0
  53. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/snippets/languages/csharp.scm +0 -0
  54. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/snippets/languages/python.scm +0 -0
  55. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/snippets/method_snippets.py +0 -0
  56. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/snippets/snippets.py +0 -0
  57. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/sources/__init__.py +0 -0
  58. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/sources/models.py +0 -0
  59. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/sources/repository.py +0 -0
  60. {kodit-0.1.6 → kodit-0.1.7}/src/kodit/sources/service.py +0 -0
  61. {kodit-0.1.6 → kodit-0.1.7}/tests/__init__.py +0 -0
  62. {kodit-0.1.6 → kodit-0.1.7}/tests/conftest.py +0 -0
  63. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/__init__.py +0 -0
  64. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/e2e.py +0 -0
  65. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/indexing/__init__.py +0 -0
  66. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/indexing/test_service.py +0 -0
  67. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/mcp_test.py +0 -0
  68. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/retreival/__init__.py +0 -0
  69. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/retreival/test_service.py +0 -0
  70. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/snippets/__init__.py +0 -0
  71. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/snippets/csharp.cs +0 -0
  72. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/snippets/detect_language_test.py +0 -0
  73. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/snippets/method_extraction_test.py +0 -0
  74. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/snippets/python.py +0 -0
  75. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/sources/__init__.py +0 -0
  76. {kodit-0.1.6 → kodit-0.1.7}/tests/kodit/sources/test_service.py +0 -0
  77. {kodit-0.1.6 → kodit-0.1.7}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kodit
3
- Version: 0.1.6
3
+ Version: 0.1.7
4
4
  Summary: Code indexing for better AI code generation
5
5
  Project-URL: Homepage, https://docs.helixml.tech/kodit/
6
6
  Project-URL: Documentation, https://docs.helixml.tech/kodit/
@@ -57,10 +57,9 @@ pip install kodit
57
57
  Kodit has two key parts. A configuration CLI to manage what gets indexed and an MCP
58
58
  server to expose your code to an AI coding assistant.
59
59
 
60
- 1. Add a source: `kodit sources create /path/to/your/code`
61
- 2. Create an index on that source: `kodit indexes create 1`
62
- 3. Test retrieval on your index: `kodit retrieve "test"`
63
- 4. Start an MCP server: `kodit serve`
60
+ 1. Add an index: `kodit index /path/to/your/code`
61
+ 2. Test retrieval on your index: `kodit retrieve "test"`
62
+ 3. Start an MCP server: `kodit serve`
64
63
 
65
64
  Now browse to your AI coding assistant and add the MCP server. You will also need to
66
65
  tell your assistant to use this server in coding tasks, otherwise it won't get called!
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.6'
21
- __version_tuple__ = version_tuple = (0, 1, 6)
20
+ __version__ = version = '0.1.7'
21
+ __version_tuple__ = version_tuple = (0, 1, 7)
@@ -8,7 +8,7 @@ from typing import Any
8
8
  import click
9
9
  import structlog
10
10
  import uvicorn
11
- from pytable_formatter import Table
11
+ from pytable_formatter import Cell, Table
12
12
  from sqlalchemy.ext.asyncio import AsyncSession
13
13
 
14
14
  from kodit.config import (
@@ -84,110 +84,59 @@ def cli( # noqa: PLR0913
84
84
  ctx.obj = config
85
85
 
86
86
 
87
- @cli.group()
88
- def sources() -> None:
89
- """Manage code sources."""
90
-
91
-
92
- @sources.command(name="list")
93
- @with_app_context
94
- @with_session
95
- async def list_sources(session: AsyncSession, app_context: AppContext) -> None:
96
- """List all code sources."""
97
- repository = SourceRepository(session)
98
- service = SourceService(app_context.get_clone_dir(), repository)
99
- sources = await service.list_sources()
100
-
101
- # Define headers and data
102
- headers = ["ID", "Created At", "URI"]
103
- data = [[source.id, source.created_at, source.uri] for source in sources]
104
-
105
- # Create and display the table
106
- table = Table(headers=headers, data=data)
107
- click.echo(table)
108
-
109
-
110
- @sources.command(name="create")
111
- @click.argument("uri")
112
- @with_app_context
113
- @with_session
114
- async def create_source(
115
- session: AsyncSession, app_context: AppContext, uri: str
116
- ) -> None:
117
- """Add a new code source."""
118
- repository = SourceRepository(session)
119
- service = SourceService(app_context.get_clone_dir(), repository)
120
- source = await service.create(uri)
121
- click.echo(f"Source created: {source.id}")
122
-
123
-
124
- @cli.group()
125
- def indexes() -> None:
126
- """Manage indexes."""
127
-
128
-
129
- @indexes.command(name="create")
130
- @click.argument("source_id")
87
+ @cli.command()
88
+ @click.argument("sources", nargs=-1)
131
89
  @with_app_context
132
90
  @with_session
133
- async def create_index(
134
- session: AsyncSession, app_context: AppContext, source_id: int
91
+ async def index(
92
+ session: AsyncSession,
93
+ app_context: AppContext,
94
+ sources: list[str],
135
95
  ) -> None:
136
- """Create an index for a source."""
96
+ """List indexes, or index data sources."""
137
97
  source_repository = SourceRepository(session)
138
98
  source_service = SourceService(app_context.get_clone_dir(), source_repository)
139
99
  repository = IndexRepository(session)
140
100
  service = IndexService(repository, source_service, app_context.get_data_dir())
141
- index = await service.create(source_id)
142
- click.echo(f"Index created: {index.id}")
143
101
 
144
-
145
- @indexes.command(name="list")
146
- @with_app_context
147
- @with_session
148
- async def list_indexes(session: AsyncSession, app_context: AppContext) -> None:
149
- """List all indexes."""
150
- source_repository = SourceRepository(session)
151
- source_service = SourceService(app_context.get_clone_dir(), source_repository)
152
- repository = IndexRepository(session)
153
- service = IndexService(repository, source_service, app_context.get_data_dir())
154
- indexes = await service.list_indexes()
155
-
156
- # Define headers and data
157
- headers = [
158
- "ID",
159
- "Created At",
160
- "Updated At",
161
- "Num Snippets",
162
- ]
163
- data = [
164
- [
165
- index.id,
166
- index.created_at,
167
- index.updated_at,
168
- index.num_snippets,
102
+ if not sources:
103
+ # No source specified, list all indexes
104
+ indexes = await service.list_indexes()
105
+ headers: list[str | Cell] = [
106
+ "ID",
107
+ "Created At",
108
+ "Updated At",
109
+ "Source",
110
+ "Num Snippets",
169
111
  ]
170
- for index in indexes
171
- ]
172
-
173
- # Create and display the table
174
- table = Table(headers=headers, data=data)
175
- click.echo(table)
176
-
177
-
178
- @indexes.command(name="run")
179
- @click.argument("index_id")
180
- @with_app_context
181
- @with_session
182
- async def run_index(
183
- session: AsyncSession, app_context: AppContext, index_id: int
184
- ) -> None:
185
- """Run an index."""
186
- source_repository = SourceRepository(session)
187
- source_service = SourceService(app_context.get_clone_dir(), source_repository)
188
- repository = IndexRepository(session)
189
- service = IndexService(repository, source_service, app_context.get_data_dir())
190
- await service.run(index_id)
112
+ data = [
113
+ [
114
+ index.id,
115
+ index.created_at,
116
+ index.updated_at,
117
+ index.source,
118
+ index.num_snippets,
119
+ ]
120
+ for index in indexes
121
+ ]
122
+ click.echo(Table(headers=headers, data=data))
123
+ return
124
+ # Handle source indexing
125
+ for source in sources:
126
+ if source.startswith("https://"):
127
+ msg = "Web or git indexing is not implemented yet"
128
+ raise click.UsageError(msg)
129
+ if source.startswith("git"):
130
+ msg = "Git indexing is not implemented yet"
131
+ raise click.UsageError(msg)
132
+ if Path(source).is_file():
133
+ msg = "File indexing is not implemented yet"
134
+ raise click.UsageError(msg)
135
+
136
+ # Index directory
137
+ s = await source_service.create(source)
138
+ index = await service.create(s.id)
139
+ await service.run(index.id)
191
140
 
192
141
 
193
142
  @cli.command()
@@ -82,7 +82,7 @@ class IndexRepository:
82
82
  result = await self.session.execute(query)
83
83
  return list(result.scalars())
84
84
 
85
- async def list_indexes(self) -> list[Index]:
85
+ async def list_indexes(self) -> list[tuple[Index, Source]]:
86
86
  """List all indexes.
87
87
 
88
88
  Returns:
@@ -90,9 +90,11 @@ class IndexRepository:
90
90
  and counts of files and snippets.
91
91
 
92
92
  """
93
- query = select(Index).limit(10)
93
+ query = select(Index, Source).join(
94
+ Source, Index.source_id == Source.id, full=True
95
+ )
94
96
  result = await self.session.execute(query)
95
- return list(result.scalars())
97
+ return list(result.tuples())
96
98
 
97
99
  async def num_snippets_for_index(self, index_id: int) -> int:
98
100
  """Get the number of snippets for an index."""
@@ -33,6 +33,7 @@ class IndexView(pydantic.BaseModel):
33
33
  id: int
34
34
  created_at: datetime
35
35
  updated_at: datetime | None = None
36
+ source: str | None = None
36
37
  num_snippets: int | None = None
37
38
 
38
39
 
@@ -105,8 +106,9 @@ class IndexService:
105
106
  created_at=index.created_at,
106
107
  updated_at=index.updated_at,
107
108
  num_snippets=await self.repository.num_snippets_for_index(index.id),
109
+ source=source.uri,
108
110
  )
109
- for index in indexes
111
+ for index, source in indexes
110
112
  ]
111
113
 
112
114
  async def run(self, index_id: int) -> None:
@@ -37,9 +37,7 @@ def test_version_command(runner: CliRunner, default_cli_args: list[str]) -> None
37
37
  def test_cli_vars_work(runner: CliRunner, default_cli_args: list[str]) -> None:
38
38
  """Test that cli args override env vars."""
39
39
  runner.env = {"LOG_LEVEL": "INFO"}
40
- result = runner.invoke(
41
- cli, [*default_cli_args, "--log-level", "DEBUG", "sources", "list"]
42
- )
40
+ result = runner.invoke(cli, [*default_cli_args, "--log-level", "DEBUG", "index"])
43
41
  assert result.exit_code == 0
44
42
  assert result.output.count("debug") > 10 # The db spits out lots of debug messages
45
43
 
@@ -47,7 +45,7 @@ def test_cli_vars_work(runner: CliRunner, default_cli_args: list[str]) -> None:
47
45
  def test_env_vars_work(runner: CliRunner, default_cli_args: list[str]) -> None:
48
46
  """Test that env vars work."""
49
47
  runner.env = {"LOG_LEVEL": "DEBUG"}
50
- result = runner.invoke(cli, [*default_cli_args, "sources", "list"])
48
+ result = runner.invoke(cli, [*default_cli_args, "index"])
51
49
  assert result.exit_code == 0
52
50
  assert result.output.count("debug") > 10 # The db spits out lots of debug messages
53
51
 
@@ -57,9 +55,7 @@ def test_dotenv_file_works(runner: CliRunner, default_cli_args: list[str]) -> No
57
55
  with tempfile.NamedTemporaryFile(delete=False) as f:
58
56
  f.write(b"LOG_LEVEL=DEBUG")
59
57
  f.flush()
60
- result = runner.invoke(
61
- cli, [*default_cli_args, "--env-file", f.name, "sources", "list"]
62
- )
58
+ result = runner.invoke(cli, [*default_cli_args, "--env-file", f.name, "index"])
63
59
  assert result.exit_code == 0
64
60
  assert (
65
61
  result.output.count("debug") > 10
@@ -69,7 +65,7 @@ def test_dotenv_file_works(runner: CliRunner, default_cli_args: list[str]) -> No
69
65
  def test_dotenv_file_not_found(runner: CliRunner, default_cli_args: list[str]) -> None:
70
66
  """Test that the .env file not found error is raised."""
71
67
  result = runner.invoke(
72
- cli, [*default_cli_args, "--env-file", "nonexistent.env", "sources", "list"]
68
+ cli, [*default_cli_args, "--env-file", "nonexistent.env", "index"]
73
69
  )
74
70
  assert result.exit_code == 2
75
71
  assert "does not exist" in result.output
@@ -24,14 +24,9 @@ echo "print('Hello, world!')" > $tmp_dir/test.py
24
24
  # Test version command
25
25
  $prefix kodit version
26
26
 
27
- # Test sources commands
28
- $prefix kodit sources list
29
- $prefix kodit sources create $tmp_dir
30
-
31
- # Test indexes commands
32
- $prefix kodit indexes list
33
- $prefix kodit indexes create 1
34
- $prefix kodit indexes run 1
27
+ # Test index command
28
+ $prefix kodit index $tmp_dir
29
+ $prefix kodit index
35
30
 
36
31
  # Test retrieve command
37
32
  $prefix kodit retrieve "Hello"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes