kodit 0.5.4__py3-none-any.whl → 0.5.6__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (64) hide show
  1. kodit/_version.py +2 -2
  2. kodit/app.py +2 -0
  3. kodit/application/factories/server_factory.py +58 -32
  4. kodit/application/services/code_search_application_service.py +89 -12
  5. kodit/application/services/commit_indexing_application_service.py +527 -195
  6. kodit/application/services/enrichment_query_service.py +311 -43
  7. kodit/application/services/indexing_worker_service.py +1 -1
  8. kodit/application/services/queue_service.py +15 -10
  9. kodit/application/services/sync_scheduler.py +2 -1
  10. kodit/domain/enrichments/architecture/architecture.py +1 -1
  11. kodit/domain/enrichments/architecture/database_schema/__init__.py +1 -0
  12. kodit/domain/enrichments/architecture/database_schema/database_schema.py +17 -0
  13. kodit/domain/enrichments/architecture/physical/physical.py +1 -1
  14. kodit/domain/enrichments/development/development.py +1 -1
  15. kodit/domain/enrichments/development/snippet/snippet.py +12 -5
  16. kodit/domain/enrichments/enrichment.py +31 -4
  17. kodit/domain/enrichments/history/__init__.py +1 -0
  18. kodit/domain/enrichments/history/commit_description/__init__.py +1 -0
  19. kodit/domain/enrichments/history/commit_description/commit_description.py +17 -0
  20. kodit/domain/enrichments/history/history.py +18 -0
  21. kodit/domain/enrichments/usage/api_docs.py +1 -1
  22. kodit/domain/enrichments/usage/usage.py +1 -1
  23. kodit/domain/entities/git.py +30 -25
  24. kodit/domain/factories/git_repo_factory.py +20 -5
  25. kodit/domain/protocols.py +60 -125
  26. kodit/domain/services/embedding_service.py +14 -16
  27. kodit/domain/services/git_repository_service.py +60 -38
  28. kodit/domain/services/git_service.py +18 -11
  29. kodit/domain/tracking/resolution_service.py +6 -16
  30. kodit/domain/value_objects.py +6 -9
  31. kodit/infrastructure/api/v1/dependencies.py +12 -3
  32. kodit/infrastructure/api/v1/query_params.py +27 -0
  33. kodit/infrastructure/api/v1/routers/commits.py +91 -85
  34. kodit/infrastructure/api/v1/routers/repositories.py +53 -37
  35. kodit/infrastructure/api/v1/routers/search.py +1 -1
  36. kodit/infrastructure/api/v1/schemas/enrichment.py +14 -0
  37. kodit/infrastructure/api/v1/schemas/repository.py +1 -1
  38. kodit/infrastructure/cloning/git/git_python_adaptor.py +41 -0
  39. kodit/infrastructure/database_schema/__init__.py +1 -0
  40. kodit/infrastructure/database_schema/database_schema_detector.py +268 -0
  41. kodit/infrastructure/slicing/api_doc_extractor.py +0 -2
  42. kodit/infrastructure/sqlalchemy/embedding_repository.py +44 -34
  43. kodit/infrastructure/sqlalchemy/enrichment_association_repository.py +73 -0
  44. kodit/infrastructure/sqlalchemy/enrichment_v2_repository.py +145 -97
  45. kodit/infrastructure/sqlalchemy/entities.py +12 -116
  46. kodit/infrastructure/sqlalchemy/git_branch_repository.py +52 -244
  47. kodit/infrastructure/sqlalchemy/git_commit_repository.py +35 -324
  48. kodit/infrastructure/sqlalchemy/git_file_repository.py +70 -0
  49. kodit/infrastructure/sqlalchemy/git_repository.py +60 -230
  50. kodit/infrastructure/sqlalchemy/git_tag_repository.py +53 -240
  51. kodit/infrastructure/sqlalchemy/query.py +331 -0
  52. kodit/infrastructure/sqlalchemy/repository.py +203 -0
  53. kodit/infrastructure/sqlalchemy/task_repository.py +79 -58
  54. kodit/infrastructure/sqlalchemy/task_status_repository.py +45 -52
  55. kodit/migrations/versions/4b1a3b2c8fa5_refactor_git_tracking.py +190 -0
  56. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/METADATA +1 -1
  57. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/RECORD +60 -50
  58. kodit/infrastructure/mappers/enrichment_mapper.py +0 -83
  59. kodit/infrastructure/mappers/git_mapper.py +0 -193
  60. kodit/infrastructure/mappers/snippet_mapper.py +0 -104
  61. kodit/infrastructure/sqlalchemy/snippet_v2_repository.py +0 -479
  62. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/WHEEL +0 -0
  63. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/entry_points.txt +0 -0
  64. {kodit-0.5.4.dist-info → kodit-0.5.6.dist-info}/licenses/LICENSE +0 -0
@@ -1,16 +1,17 @@
1
1
  """Task repository for the task queue."""
2
2
 
3
3
  from collections.abc import Callable
4
+ from typing import Any
4
5
 
5
6
  import structlog
6
7
  from sqlalchemy import select
7
8
  from sqlalchemy.ext.asyncio import AsyncSession
8
9
 
9
- from kodit.domain.entities import Task
10
+ import kodit.domain.entities as domain_entities
10
11
  from kodit.domain.protocols import TaskRepository
11
- from kodit.domain.value_objects import TaskOperation
12
12
  from kodit.infrastructure.mappers.task_mapper import TaskMapper
13
13
  from kodit.infrastructure.sqlalchemy import entities as db_entities
14
+ from kodit.infrastructure.sqlalchemy.repository import SqlAlchemyRepository
14
15
  from kodit.infrastructure.sqlalchemy.unit_of_work import SqlAlchemyUnitOfWork
15
16
 
16
17
 
@@ -21,7 +22,12 @@ def create_task_repository(
21
22
  return SqlAlchemyTaskRepository(session_factory=session_factory)
22
23
 
23
24
 
24
- class SqlAlchemyTaskRepository(TaskRepository):
25
+ # TODO(Phil): Stop using dedup_key as the primary key. Add some DDD to this instead.
26
+
27
+
28
+ class SqlAlchemyTaskRepository(
29
+ SqlAlchemyRepository[domain_entities.Task, db_entities.Task], TaskRepository
30
+ ):
25
31
  """Repository for task persistence using the existing Task entity."""
26
32
 
27
33
  def __init__(self, session_factory: Callable[[], AsyncSession]) -> None:
@@ -29,25 +35,81 @@ class SqlAlchemyTaskRepository(TaskRepository):
29
35
  self.session_factory = session_factory
30
36
  self.log = structlog.get_logger(__name__)
31
37
 
32
- async def add(
33
- self,
34
- task: Task,
35
- ) -> None:
36
- """Create a new task in the database."""
38
+ @property
39
+ def db_entity_type(self) -> type[db_entities.Task]:
40
+ """The SQLAlchemy model type."""
41
+ return db_entities.Task
42
+
43
+ def _get_id(self, entity: domain_entities.Task) -> Any:
44
+ """Extract ID from domain entity."""
45
+ return entity.id
46
+
47
+ @staticmethod
48
+ def to_domain(db_entity: db_entities.Task) -> domain_entities.Task:
49
+ """Map database entity to domain entity."""
50
+ return TaskMapper.to_domain_task(db_entity)
51
+
52
+ @staticmethod
53
+ def to_db(domain_entity: domain_entities.Task) -> db_entities.Task:
54
+ """Map domain entity to database entity."""
55
+ return TaskMapper.from_domain_task(domain_entity)
56
+
57
+ async def get(self, entity_id: Any) -> domain_entities.Task:
58
+ """Get entity by dedup_key."""
37
59
  async with SqlAlchemyUnitOfWork(self.session_factory) as session:
38
- session.add(TaskMapper.from_domain_task(task))
60
+ stmt = select(db_entities.Task).where(
61
+ db_entities.Task.dedup_key == entity_id
62
+ )
63
+ result = await session.execute(stmt)
64
+ db_entity = result.scalar_one_or_none()
65
+ if not db_entity:
66
+ raise ValueError(f"Entity with id {entity_id} not found")
67
+ return self.to_domain(db_entity)
39
68
 
40
- async def get(self, task_id: str) -> Task | None:
41
- """Get a task by ID."""
69
+ async def exists(self, entity_id: Any) -> bool:
70
+ """Check if entity exists by dedup_key."""
42
71
  async with SqlAlchemyUnitOfWork(self.session_factory) as session:
43
- stmt = select(db_entities.Task).where(db_entities.Task.dedup_key == task_id)
72
+ stmt = select(db_entities.Task).where(
73
+ db_entities.Task.dedup_key == entity_id
74
+ )
44
75
  result = await session.execute(stmt)
45
- db_task = result.scalar_one_or_none()
46
- if not db_task:
47
- return None
48
- return TaskMapper.to_domain_task(db_task)
76
+ return result.scalar_one_or_none() is not None
77
+
78
+ async def save(self, entity: domain_entities.Task) -> domain_entities.Task:
79
+ """Save entity (create new or update existing)."""
80
+ async with SqlAlchemyUnitOfWork(self.session_factory) as session:
81
+ # Query by dedup_key instead of primary key
82
+ stmt = select(db_entities.Task).where(
83
+ db_entities.Task.dedup_key == entity.id
84
+ )
85
+ result = await session.execute(stmt)
86
+ existing_db_entity = result.scalar_one_or_none()
87
+
88
+ if existing_db_entity:
89
+ # Update existing entity
90
+ new_db_entity = self.to_db(entity)
91
+ self._update_db_entity(existing_db_entity, new_db_entity)
92
+ db_entity = existing_db_entity
93
+ else:
94
+ # Create new entity
95
+ db_entity = self.to_db(entity)
96
+ session.add(db_entity)
97
+
98
+ await session.flush()
99
+ return self.to_domain(db_entity)
100
+
101
+ async def delete(self, entity: domain_entities.Task) -> None:
102
+ """Remove entity by dedup_key."""
103
+ async with SqlAlchemyUnitOfWork(self.session_factory) as session:
104
+ stmt = select(db_entities.Task).where(
105
+ db_entities.Task.dedup_key == entity.id
106
+ )
107
+ result = await session.execute(stmt)
108
+ db_entity = result.scalar_one_or_none()
109
+ if db_entity:
110
+ await session.delete(db_entity)
49
111
 
50
- async def next(self) -> Task | None:
112
+ async def next(self) -> domain_entities.Task | None:
51
113
  """Take a task for processing and remove it from the database."""
52
114
  async with SqlAlchemyUnitOfWork(self.session_factory) as session:
53
115
  stmt = (
@@ -60,44 +122,3 @@ class SqlAlchemyTaskRepository(TaskRepository):
60
122
  if not db_task:
61
123
  return None
62
124
  return TaskMapper.to_domain_task(db_task)
63
-
64
- async def remove(self, task: Task) -> None:
65
- """Remove a task from the database."""
66
- async with SqlAlchemyUnitOfWork(self.session_factory) as session:
67
- db_task = await session.scalar(
68
- select(db_entities.Task).where(db_entities.Task.dedup_key == task.id)
69
- )
70
- if not db_task:
71
- raise ValueError(f"Task not found: {task.id}")
72
- await session.delete(db_task)
73
-
74
- async def update(self, task: Task) -> None:
75
- """Update a task in the database."""
76
- async with SqlAlchemyUnitOfWork(self.session_factory) as session:
77
- stmt = select(db_entities.Task).where(db_entities.Task.dedup_key == task.id)
78
- result = await session.execute(stmt)
79
- db_task = result.scalar_one_or_none()
80
-
81
- if not db_task:
82
- raise ValueError(f"Task not found: {task.id}")
83
-
84
- db_task.priority = task.priority
85
- db_task.payload = task.payload
86
-
87
- async def list(self, task_operation: TaskOperation | None = None) -> list[Task]:
88
- """List tasks with optional status filter."""
89
- async with SqlAlchemyUnitOfWork(self.session_factory) as session:
90
- stmt = select(db_entities.Task)
91
-
92
- if task_operation:
93
- stmt = stmt.where(db_entities.Task.type == task_operation.value)
94
-
95
- stmt = stmt.order_by(
96
- db_entities.Task.priority.desc(), db_entities.Task.created_at
97
- )
98
-
99
- result = await session.execute(stmt)
100
- records = result.scalars().all()
101
-
102
- # Convert to domain entities
103
- return [TaskMapper.to_domain_task(record) for record in records]
@@ -1,15 +1,16 @@
1
1
  """Task repository for the task queue."""
2
2
 
3
3
  from collections.abc import Callable
4
+ from typing import Any, override
4
5
 
5
- import structlog
6
- from sqlalchemy import delete, select
6
+ from sqlalchemy import select
7
7
  from sqlalchemy.ext.asyncio import AsyncSession
8
8
 
9
9
  from kodit.domain import entities as domain_entities
10
10
  from kodit.domain.protocols import TaskStatusRepository
11
11
  from kodit.infrastructure.mappers.task_status_mapper import TaskStatusMapper
12
12
  from kodit.infrastructure.sqlalchemy import entities as db_entities
13
+ from kodit.infrastructure.sqlalchemy.repository import SqlAlchemyRepository
13
14
  from kodit.infrastructure.sqlalchemy.unit_of_work import SqlAlchemyUnitOfWork
14
15
 
15
16
 
@@ -20,52 +21,52 @@ def create_task_status_repository(
20
21
  return SqlAlchemyTaskStatusRepository(session_factory=session_factory)
21
22
 
22
23
 
23
- class SqlAlchemyTaskStatusRepository(TaskStatusRepository):
24
+ class SqlAlchemyTaskStatusRepository(
25
+ SqlAlchemyRepository[domain_entities.TaskStatus, db_entities.TaskStatus],
26
+ TaskStatusRepository,
27
+ ):
24
28
  """Repository for persisting TaskStatus entities."""
25
29
 
26
- def __init__(self, session_factory: Callable[[], AsyncSession]) -> None:
27
- """Initialize the repository."""
28
- self.session_factory = session_factory
29
- self.mapper = TaskStatusMapper()
30
- self.log = structlog.get_logger(__name__)
30
+ @property
31
+ def db_entity_type(self) -> type[db_entities.TaskStatus]:
32
+ """The SQLAlchemy model type."""
33
+ return db_entities.TaskStatus
31
34
 
32
- async def save(self, status: domain_entities.TaskStatus) -> None:
35
+ def _get_id(self, entity: domain_entities.TaskStatus) -> Any:
36
+ """Extract ID from domain entity."""
37
+ return entity.id
38
+
39
+ @staticmethod
40
+ def to_domain(
41
+ db_entity: db_entities.TaskStatus,
42
+ ) -> domain_entities.TaskStatus:
43
+ """Map database entity to domain entity."""
44
+ return TaskStatusMapper.to_domain_task_status(db_entity)
45
+
46
+ @staticmethod
47
+ def to_db(
48
+ domain_entity: domain_entities.TaskStatus,
49
+ ) -> db_entities.TaskStatus:
50
+ """Map domain entity to database entity."""
51
+ return TaskStatusMapper.from_domain_task_status(domain_entity)
52
+
53
+ @override
54
+ async def save(
55
+ self, entity: domain_entities.TaskStatus
56
+ ) -> domain_entities.TaskStatus:
33
57
  """Save a TaskStatus to database."""
34
- # If this task has a parent, ensure the parent exists in the database first
35
- if status.parent is not None:
36
- async with SqlAlchemyUnitOfWork(self.session_factory) as session:
37
- parent_stmt = select(db_entities.TaskStatus).where(
38
- db_entities.TaskStatus.id == status.parent.id,
39
- )
40
- parent_result = await session.execute(parent_stmt)
41
- existing_parent = parent_result.scalar_one_or_none()
42
-
43
- if not existing_parent:
44
- # Recursively save the parent first
45
- await self.save(status.parent)
58
+ # Recursively convert parents to a list of domain entities, parents first
59
+ parents: list[domain_entities.TaskStatus] = []
60
+ current = entity
61
+ while current.parent is not None:
62
+ parents.insert(0, current.parent)
63
+ current = current.parent
46
64
 
47
- async with SqlAlchemyUnitOfWork(self.session_factory) as session:
48
- # Convert domain entity to database entity
49
- db_status = self.mapper.from_domain_task_status(status)
50
- stmt = select(db_entities.TaskStatus).where(
51
- db_entities.TaskStatus.id == db_status.id,
52
- )
53
- result = await session.execute(stmt)
54
- existing = result.scalar_one_or_none()
55
-
56
- if not existing:
57
- session.add(db_status)
58
- else:
59
- # Update existing record with new values
60
- existing.operation = db_status.operation
61
- existing.state = db_status.state
62
- existing.error = db_status.error
63
- existing.total = db_status.total
64
- existing.current = db_status.current
65
- existing.updated_at = db_status.updated_at
66
- existing.parent = db_status.parent
67
- existing.trackable_id = db_status.trackable_id
68
- existing.trackable_type = db_status.trackable_type
65
+ # Add current entity to the end of the list
66
+ parents.append(entity)
67
+
68
+ await self.save_bulk(parents)
69
+ return entity
69
70
 
70
71
  async def load_with_hierarchy(
71
72
  self, trackable_type: str, trackable_id: int
@@ -80,12 +81,4 @@ class SqlAlchemyTaskStatusRepository(TaskStatusRepository):
80
81
  db_statuses = list(result.scalars().all())
81
82
 
82
83
  # Use mapper to convert and reconstruct hierarchy
83
- return self.mapper.to_domain_task_status_with_hierarchy(db_statuses)
84
-
85
- async def delete(self, status: domain_entities.TaskStatus) -> None:
86
- """Delete a TaskStatus."""
87
- async with SqlAlchemyUnitOfWork(self.session_factory) as session:
88
- stmt = delete(db_entities.TaskStatus).where(
89
- db_entities.TaskStatus.id == status.id,
90
- )
91
- await session.execute(stmt)
84
+ return TaskStatusMapper.to_domain_task_status_with_hierarchy(db_statuses)
@@ -0,0 +1,190 @@
1
+ # ruff: noqa
2
+ """refactorings
3
+
4
+ Revision ID: 4b1a3b2c8fa5
5
+ Revises: 19f8c7faf8b9
6
+ Create Date: 2025-10-29 13:38:10.737704
7
+
8
+ """
9
+
10
+ from typing import Sequence, Union
11
+
12
+ from alembic import op
13
+ import sqlalchemy as sa
14
+
15
+ from kodit.domain.tracking.trackable import TrackableReferenceType
16
+
17
+
18
+ # revision identifiers, used by Alembic.
19
+ revision: str = "4b1a3b2c8fa5"
20
+ down_revision: Union[str, None] = "19f8c7faf8b9"
21
+ branch_labels: Union[str, Sequence[str], None] = None
22
+ depends_on: Union[str, Sequence[str], None] = None
23
+
24
+
25
+ def upgrade() -> None:
26
+ """Upgrade schema."""
27
+ op.drop_index("ix_commit_snippets_v2_commit_sha", table_name="commit_snippets_v2")
28
+ op.drop_index("ix_commit_snippets_v2_snippet_sha", table_name="commit_snippets_v2")
29
+ op.drop_table("commit_snippets_v2")
30
+ op.drop_index("ix_snippet_v2_files_blob_sha", table_name="snippet_v2_files")
31
+ op.drop_index("ix_snippet_v2_files_commit_sha", table_name="snippet_v2_files")
32
+ op.drop_index("ix_snippet_v2_files_file_path", table_name="snippet_v2_files")
33
+ op.drop_index("ix_snippet_v2_files_snippet_sha", table_name="snippet_v2_files")
34
+ op.drop_table("snippet_v2_files")
35
+ op.drop_index("ix_snippets_v2_extension", table_name="snippets_v2")
36
+ op.drop_table("snippets_v2")
37
+ op.drop_index("ix_git_tracking_branches_name", table_name="git_tracking_branches")
38
+ op.drop_index(
39
+ "ix_git_tracking_branches_repo_id", table_name="git_tracking_branches"
40
+ )
41
+ op.drop_table("git_tracking_branches")
42
+
43
+ # Use batch_alter_table for SQLite compatibility
44
+ with op.batch_alter_table("git_repos", schema=None) as batch_op:
45
+ # Add columns as nullable first
46
+ batch_op.add_column(
47
+ sa.Column("tracking_type", sa.String(length=255), nullable=True)
48
+ )
49
+ batch_op.add_column(
50
+ sa.Column("tracking_name", sa.String(length=255), nullable=True)
51
+ )
52
+
53
+ # Set default values for existing rows
54
+ op.execute(
55
+ f"UPDATE git_repos SET tracking_type = '{TrackableReferenceType.BRANCH}' WHERE tracking_type IS NULL"
56
+ )
57
+ op.execute(
58
+ f"UPDATE git_repos SET tracking_name = 'main' WHERE tracking_name IS NULL"
59
+ )
60
+
61
+ # Make columns non-nullable using batch_alter_table for SQLite compatibility
62
+ with op.batch_alter_table("git_repos", schema=None) as batch_op:
63
+ batch_op.alter_column("tracking_type", nullable=False)
64
+ batch_op.alter_column("tracking_name", nullable=False)
65
+ batch_op.create_index(
66
+ op.f("ix_git_repos_tracking_name"),
67
+ ["tracking_name"],
68
+ unique=False,
69
+ )
70
+ batch_op.create_index(
71
+ op.f("ix_git_repos_tracking_type"),
72
+ ["tracking_type"],
73
+ unique=False,
74
+ )
75
+
76
+
77
+ def downgrade() -> None:
78
+ """Downgrade schema."""
79
+ op.create_table(
80
+ "snippets_v2",
81
+ sa.Column("sha", sa.VARCHAR(length=64), nullable=False),
82
+ sa.Column("created_at", sa.DATETIME(), nullable=False),
83
+ sa.Column("updated_at", sa.DATETIME(), nullable=False),
84
+ sa.Column("content", sa.TEXT(), nullable=False),
85
+ sa.Column("extension", sa.VARCHAR(length=255), nullable=False),
86
+ sa.PrimaryKeyConstraint("sha"),
87
+ )
88
+ op.create_index(
89
+ "ix_snippets_v2_extension", "snippets_v2", ["extension"], unique=False
90
+ )
91
+ op.create_table(
92
+ "snippet_v2_files",
93
+ sa.Column("id", sa.INTEGER(), nullable=False),
94
+ sa.Column("snippet_sha", sa.VARCHAR(length=64), nullable=False),
95
+ sa.Column("blob_sha", sa.VARCHAR(length=64), nullable=False),
96
+ sa.Column("commit_sha", sa.VARCHAR(length=64), nullable=False),
97
+ sa.Column("file_path", sa.VARCHAR(length=1024), nullable=False),
98
+ sa.ForeignKeyConstraint(
99
+ ["commit_sha", "file_path"],
100
+ ["git_commit_files.commit_sha", "git_commit_files.path"],
101
+ ),
102
+ sa.ForeignKeyConstraint(
103
+ ["snippet_sha"],
104
+ ["snippets_v2.sha"],
105
+ ),
106
+ sa.PrimaryKeyConstraint("id"),
107
+ sa.UniqueConstraint(
108
+ "snippet_sha",
109
+ "blob_sha",
110
+ "commit_sha",
111
+ "file_path",
112
+ name="uix_snippet_file",
113
+ ),
114
+ )
115
+ op.create_index(
116
+ "ix_snippet_v2_files_snippet_sha",
117
+ "snippet_v2_files",
118
+ ["snippet_sha"],
119
+ unique=False,
120
+ )
121
+ op.create_index(
122
+ "ix_snippet_v2_files_file_path", "snippet_v2_files", ["file_path"], unique=False
123
+ )
124
+ op.create_index(
125
+ "ix_snippet_v2_files_commit_sha",
126
+ "snippet_v2_files",
127
+ ["commit_sha"],
128
+ unique=False,
129
+ )
130
+ op.create_index(
131
+ "ix_snippet_v2_files_blob_sha", "snippet_v2_files", ["blob_sha"], unique=False
132
+ )
133
+ op.create_table(
134
+ "commit_snippets_v2",
135
+ sa.Column("id", sa.INTEGER(), nullable=False),
136
+ sa.Column("commit_sha", sa.VARCHAR(length=64), nullable=False),
137
+ sa.Column("snippet_sha", sa.VARCHAR(length=64), nullable=False),
138
+ sa.ForeignKeyConstraint(
139
+ ["commit_sha"],
140
+ ["git_commits.commit_sha"],
141
+ ),
142
+ sa.ForeignKeyConstraint(
143
+ ["snippet_sha"],
144
+ ["snippets_v2.sha"],
145
+ ),
146
+ sa.PrimaryKeyConstraint("id"),
147
+ sa.UniqueConstraint("commit_sha", "snippet_sha", name="uix_commit_snippet"),
148
+ )
149
+ op.create_index(
150
+ "ix_commit_snippets_v2_snippet_sha",
151
+ "commit_snippets_v2",
152
+ ["snippet_sha"],
153
+ unique=False,
154
+ )
155
+ op.create_index(
156
+ "ix_commit_snippets_v2_commit_sha",
157
+ "commit_snippets_v2",
158
+ ["commit_sha"],
159
+ unique=False,
160
+ )
161
+ # Use batch_alter_table for SQLite compatibility
162
+ with op.batch_alter_table("git_repos", schema=None) as batch_op:
163
+ batch_op.drop_index(op.f("ix_git_repos_tracking_type"))
164
+ batch_op.drop_index(op.f("ix_git_repos_tracking_name"))
165
+ batch_op.drop_column("tracking_name")
166
+ batch_op.drop_column("tracking_type")
167
+
168
+ op.create_table(
169
+ "git_tracking_branches",
170
+ sa.Column("repo_id", sa.INTEGER(), nullable=False),
171
+ sa.Column("name", sa.VARCHAR(length=255), nullable=False),
172
+ sa.Column("created_at", sa.DATETIME(), nullable=False),
173
+ sa.Column("updated_at", sa.DATETIME(), nullable=False),
174
+ sa.ForeignKeyConstraint(
175
+ ["repo_id"], ["git_repos.id"], name="fk_tracking_branch_repo"
176
+ ),
177
+ sa.PrimaryKeyConstraint("repo_id", "name", name="pk_git_tracking_branches"),
178
+ )
179
+ op.create_index(
180
+ "ix_git_tracking_branches_repo_id",
181
+ "git_tracking_branches",
182
+ ["repo_id"],
183
+ unique=False,
184
+ )
185
+ op.create_index(
186
+ "ix_git_tracking_branches_name",
187
+ "git_tracking_branches",
188
+ ["name"],
189
+ unique=False,
190
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kodit
3
- Version: 0.5.4
3
+ Version: 0.5.6
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/