quilchoom 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. quilchoom/__init__.py +3 -0
  2. quilchoom/application/__init__.py +1 -0
  3. quilchoom/application/apply_correction.py +75 -0
  4. quilchoom/application/capture_commit.py +68 -0
  5. quilchoom/application/capture_repository.py +53 -0
  6. quilchoom/application/check_document_staleness.py +26 -0
  7. quilchoom/application/check_knowledge_status.py +24 -0
  8. quilchoom/application/create_document_version.py +45 -0
  9. quilchoom/application/errors.py +47 -0
  10. quilchoom/application/generate_document.py +84 -0
  11. quilchoom/application/get_document_version.py +48 -0
  12. quilchoom/application/get_project_status.py +110 -0
  13. quilchoom/application/initialize_project.py +53 -0
  14. quilchoom/application/interpret_history.py +87 -0
  15. quilchoom/application/reconstruct_history.py +47 -0
  16. quilchoom/application/runtime.py +59 -0
  17. quilchoom/cli.py +1042 -0
  18. quilchoom/domain/correction.py +17 -0
  19. quilchoom/domain/document.py +33 -0
  20. quilchoom/domain/event.py +19 -0
  21. quilchoom/domain/evidence.py +19 -0
  22. quilchoom/domain/history.py +20 -0
  23. quilchoom/domain/interpretation.py +37 -0
  24. quilchoom/domain/knowledge_claim.py +35 -0
  25. quilchoom/domain/project.py +17 -0
  26. quilchoom/infrastructure/ai/__init__.py +0 -0
  27. quilchoom/infrastructure/ai/composition.py +52 -0
  28. quilchoom/infrastructure/ai/config.py +15 -0
  29. quilchoom/infrastructure/ai/credentials.py +20 -0
  30. quilchoom/infrastructure/ai/errors.py +35 -0
  31. quilchoom/infrastructure/ai/llm_document_generator.py +89 -0
  32. quilchoom/infrastructure/ai/llm_knowledge_interpreter.py +131 -0
  33. quilchoom/infrastructure/ai/models.py +119 -0
  34. quilchoom/infrastructure/ai/openai_provider.py +99 -0
  35. quilchoom/infrastructure/ai/retry.py +48 -0
  36. quilchoom/infrastructure/config.py +73 -0
  37. quilchoom/infrastructure/database/__init__.py +1 -0
  38. quilchoom/infrastructure/database/alembic/README +1 -0
  39. quilchoom/infrastructure/database/alembic/env.py +91 -0
  40. quilchoom/infrastructure/database/alembic/script.py.mako +28 -0
  41. quilchoom/infrastructure/database/alembic/versions/004b83fcafcb_create_evidence_table.py +54 -0
  42. quilchoom/infrastructure/database/alembic/versions/03b2175f033b_add_capture_uniqueness_constraints.py +50 -0
  43. quilchoom/infrastructure/database/alembic/versions/11da2eb1dbf1_create_events_table.py +54 -0
  44. quilchoom/infrastructure/database/alembic/versions/1d97255f2308_add_corrections.py +53 -0
  45. quilchoom/infrastructure/database/alembic/versions/3382e88e4db2_allow_claims_across_document_versions.py +27 -0
  46. quilchoom/infrastructure/database/alembic/versions/8b14085b8c73_add_knowledge_claim_basis.py +53 -0
  47. quilchoom/infrastructure/database/alembic/versions/9030269dd9ac_add_interpretation_runs.py +67 -0
  48. quilchoom/infrastructure/database/alembic/versions/a393cc967881_add_knowledge_claims.py +63 -0
  49. quilchoom/infrastructure/database/alembic/versions/b746cacea516_add_documents_and_document_versions.py +82 -0
  50. quilchoom/infrastructure/database/alembic/versions/d990a8acc606_create_projects_table.py +41 -0
  51. quilchoom/infrastructure/database/connection.py +17 -0
  52. quilchoom/infrastructure/database/errors.py +47 -0
  53. quilchoom/infrastructure/database/migrations.py +70 -0
  54. quilchoom/infrastructure/database/models.py +360 -0
  55. quilchoom/infrastructure/database/repositories.py +866 -0
  56. quilchoom/infrastructure/filesystem/__init__.py +1 -0
  57. quilchoom/infrastructure/filesystem/export.py +28 -0
  58. quilchoom/infrastructure/filesystem/workspace.py +26 -0
  59. quilchoom/infrastructure/git/__init__.py +1 -0
  60. quilchoom/infrastructure/git/repository.py +105 -0
  61. quilchoom/interfaces/document_generator.py +36 -0
  62. quilchoom/interfaces/knowledge_interpreter.py +11 -0
  63. quilchoom/interfaces/llm_provider.py +40 -0
  64. quilchoom/ui/__init__.py +3 -0
  65. quilchoom/ui/terminal.py +200 -0
  66. quilchoom-0.1.0.dist-info/METADATA +395 -0
  67. quilchoom-0.1.0.dist-info/RECORD +70 -0
  68. quilchoom-0.1.0.dist-info/WHEEL +4 -0
  69. quilchoom-0.1.0.dist-info/entry_points.txt +4 -0
  70. quilchoom-0.1.0.dist-info/licenses/LICENSE +201 -0
quilchoom/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """
2
+ Quilchoom development documentation and evidence system.
3
+ """
@@ -0,0 +1 @@
1
+ """Application use cases for Quilchoom."""
@@ -0,0 +1,75 @@
1
+ """
2
+ Applies corrections to existing knowledge claims.
3
+ """
4
+
5
+ from uuid import UUID
6
+
7
+ from quilchoom.application.errors import (
8
+ CorrectionProjectMismatchError,
9
+ SelfReplacementError,
10
+ )
11
+ from quilchoom.domain.correction import Correction
12
+ from quilchoom.domain.knowledge_claim import ClaimStatus
13
+ from quilchoom.infrastructure.database.errors import KnowledgeClaimNotFoundError
14
+ from quilchoom.infrastructure.database.repositories import (
15
+ CorrectionRepository,
16
+ KnowledgeClaimRepository,
17
+ )
18
+
19
+
20
+ def apply_correction(
21
+ project_id: UUID,
22
+ target_claim_id: UUID,
23
+ reason: str,
24
+ claim_repository: KnowledgeClaimRepository,
25
+ correction_repository: CorrectionRepository,
26
+ replacement_claim_id: UUID | None = None,
27
+ ) -> Correction:
28
+ target_claim = claim_repository.get_by_id(target_claim_id)
29
+
30
+ if target_claim is None:
31
+ raise KnowledgeClaimNotFoundError(
32
+ f"Target knowledge claim not found: {target_claim_id}"
33
+ )
34
+
35
+ if target_claim.project_id != project_id:
36
+ raise CorrectionProjectMismatchError(
37
+ f"Target knowledge claim belongs to a different project: {target_claim.project_id}"
38
+ )
39
+
40
+ if replacement_claim_id is not None:
41
+ if replacement_claim_id == target_claim_id:
42
+ raise SelfReplacementError(
43
+ f"Claim cannot be used as its own replacement: {replacement_claim_id}"
44
+ )
45
+
46
+ replacement_claim = claim_repository.get_by_id(replacement_claim_id)
47
+
48
+ if replacement_claim is None:
49
+ raise KnowledgeClaimNotFoundError(
50
+ f"Replacement knowledge claim not found: {replacement_claim_id}"
51
+ )
52
+
53
+ if replacement_claim.project_id != project_id:
54
+ raise CorrectionProjectMismatchError(
55
+ f"Replacement knowledge claim belongs to a different project: {replacement_claim.project_id}"
56
+ )
57
+
58
+ if replacement_claim_id is not None:
59
+ new_status = ClaimStatus.CORRECTED
60
+ else:
61
+ new_status = ClaimStatus.INVALIDATED
62
+
63
+ correction = Correction(
64
+ project_id=project_id,
65
+ target_claim_id=target_claim.id,
66
+ reason=reason,
67
+ replacement_claim_id=replacement_claim_id,
68
+ )
69
+
70
+ correction_repository.save_with_claim_status_update(
71
+ correction=correction,
72
+ status=new_status,
73
+ )
74
+
75
+ return correction
@@ -0,0 +1,68 @@
1
+ """
2
+ Captures a Git commit as an Event and its supporting Evidence.
3
+ """
4
+
5
+ from datetime import UTC, datetime
6
+ from pathlib import Path
7
+
8
+ from quilchoom.domain.event import Event
9
+ from quilchoom.domain.evidence import Evidence
10
+ from quilchoom.domain.project import Project
11
+ from quilchoom.infrastructure.database.repositories import (
12
+ EventRepository,
13
+ EvidenceRepository,
14
+ )
15
+ from quilchoom.infrastructure.git.repository import inspect_commit
16
+
17
+
18
+ def capture_commit(
19
+ project: Project,
20
+ repository_path: Path,
21
+ commit_sha: str,
22
+ event_repository: EventRepository,
23
+ evidence_repository: EvidenceRepository,
24
+ ) -> tuple[Event, Evidence]:
25
+ commit = inspect_commit(repository_path, commit_sha)
26
+
27
+ existing_event = event_repository.get_by_source_reference(
28
+ project.id,
29
+ "git",
30
+ commit.sha,
31
+ )
32
+
33
+ existing_evidence = evidence_repository.get_by_reference(
34
+ project.id,
35
+ "git",
36
+ commit.sha,
37
+ )
38
+
39
+ if existing_event is not None and existing_evidence is not None:
40
+ return existing_event, existing_evidence
41
+
42
+ event = existing_event or Event(
43
+ project_id=project.id,
44
+ type="git_commit",
45
+ timestamp=commit.timestamp,
46
+ summary=commit.message,
47
+ source="git",
48
+ source_reference=commit.sha,
49
+ )
50
+ evidence = existing_evidence or Evidence(
51
+ project_id=project.id,
52
+ type="git_diff",
53
+ content=commit.diff,
54
+ reference=commit.sha,
55
+ captured_at=datetime.now(UTC),
56
+ source="git",
57
+ )
58
+
59
+ if existing_event is None and existing_evidence is None:
60
+ event_repository.save_with_evidence(event, evidence)
61
+
62
+ elif existing_event is None:
63
+ event_repository.save(event)
64
+
65
+ elif existing_evidence is None:
66
+ evidence_repository.save(evidence)
67
+
68
+ return event, evidence
@@ -0,0 +1,53 @@
1
+ """
2
+ Captures uncaptured Git commits for a Quilchoom project.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ from quilchoom.application.capture_commit import capture_commit
9
+ from quilchoom.domain.project import Project
10
+ from quilchoom.infrastructure.database.repositories import (
11
+ EventRepository,
12
+ EvidenceRepository,
13
+ )
14
+ from quilchoom.infrastructure.git.repository import list_commit_shas
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class CaptureResult:
19
+ """Represents the result of capturing repository development activity."""
20
+
21
+ discovered_commits: int
22
+ captured_commits: int
23
+
24
+
25
+ def capture_repository(
26
+ project: Project,
27
+ repository_path: Path,
28
+ event_repository: EventRepository,
29
+ evidence_repository: EvidenceRepository,
30
+ ) -> CaptureResult:
31
+ commit_shas = list_commit_shas(repository_path)
32
+
33
+ existing_evidence = evidence_repository.list_for_project(project.id)
34
+
35
+ references = {
36
+ evidence.reference for evidence in existing_evidence if evidence.source == "git"
37
+ }
38
+
39
+ uncaptured = [sha for sha in commit_shas if sha not in references]
40
+
41
+ for sha in uncaptured:
42
+ capture_commit(
43
+ project=project,
44
+ repository_path=repository_path,
45
+ commit_sha=sha,
46
+ event_repository=event_repository,
47
+ evidence_repository=evidence_repository,
48
+ )
49
+
50
+ return CaptureResult(
51
+ discovered_commits=len(commit_shas),
52
+ captured_commits=len(uncaptured),
53
+ )
@@ -0,0 +1,26 @@
1
+ """
2
+ Determines whether a document version relies on outdated knowledge claims.
3
+ """
4
+
5
+ from quilchoom.domain.document import DocumentVersion
6
+ from quilchoom.domain.knowledge_claim import ClaimStatus
7
+ from quilchoom.infrastructure.database.errors import KnowledgeClaimNotFoundError
8
+ from quilchoom.infrastructure.database.repositories import KnowledgeClaimRepository
9
+
10
+
11
+ def is_document_version_stale(
12
+ version: DocumentVersion,
13
+ claim_repository: KnowledgeClaimRepository,
14
+ ) -> bool:
15
+ for claim_id in version.claim_ids:
16
+ claim = claim_repository.get_by_id(claim_id)
17
+
18
+ if claim is None:
19
+ raise KnowledgeClaimNotFoundError(
20
+ f"Knowledge claim referenced by document version not found: {claim_id}"
21
+ )
22
+
23
+ if claim.status != ClaimStatus.ACTIVE:
24
+ return True
25
+
26
+ return False
@@ -0,0 +1,24 @@
1
+ """
2
+ Determines whether a project has captured evidence awaiting interpretation.
3
+ """
4
+
5
+ from quilchoom.domain.project import Project
6
+ from quilchoom.infrastructure.database.repositories import (
7
+ EvidenceRepository,
8
+ InterpretationRunRepository,
9
+ )
10
+
11
+
12
+ def count_pending_evidence(
13
+ project: Project,
14
+ evidence_repository: EvidenceRepository,
15
+ run_repository: InterpretationRunRepository,
16
+ ) -> int:
17
+ """Counts captured evidence that has not yet been interpreted."""
18
+
19
+ evidence = evidence_repository.list_for_project(project.id)
20
+ interpreted_ids = run_repository.list_interpreted_evidence_ids(project.id)
21
+
22
+ pending_ids = {item.id for item in evidence if item.id not in interpreted_ids}
23
+
24
+ return len(pending_ids)
@@ -0,0 +1,45 @@
1
+ """
2
+ Creates new versions of existing documentation artifacts.
3
+ """
4
+
5
+ from uuid import UUID
6
+
7
+ from quilchoom.domain.document import DocumentVersion, DocumentVersionOrigin
8
+ from quilchoom.infrastructure.database.errors import DocumentNotFoundError
9
+ from quilchoom.infrastructure.database.repositories import (
10
+ DocumentRepository,
11
+ DocumentVersionRepository,
12
+ )
13
+
14
+
15
+ def create_document_version(
16
+ document_id: UUID,
17
+ content: str,
18
+ origin: DocumentVersionOrigin,
19
+ claim_ids: list[UUID],
20
+ document_repository: DocumentRepository,
21
+ version_repository: DocumentVersionRepository,
22
+ ) -> DocumentVersion:
23
+ target_document = document_repository.get_by_id(document_id)
24
+
25
+ if target_document is None:
26
+ raise DocumentNotFoundError(f"Target document not found: {document_id}")
27
+
28
+ latest_version = version_repository.get_latest(document_id)
29
+
30
+ if latest_version is not None:
31
+ version_number = latest_version.version_number + 1
32
+
33
+ else:
34
+ version_number = 1
35
+
36
+ version = DocumentVersion(
37
+ document_id=document_id,
38
+ version_number=version_number,
39
+ content=content,
40
+ origin=origin,
41
+ claim_ids=claim_ids,
42
+ )
43
+ version_repository.save(version)
44
+
45
+ return version
@@ -0,0 +1,47 @@
1
+ """
2
+ Defines application-level errors for Quilchoom.
3
+ """
4
+
5
+
6
+ class ProjectInitializationError(Exception):
7
+ """Raised when a Quilchoom project cannot be initialized."""
8
+
9
+
10
+ class ProjectRuntimeError(Exception):
11
+ """Raised when an existing Quilchoom project cannot be loaded."""
12
+
13
+
14
+ class HistoryReconstructionError(Exception):
15
+ """Raised when a project's development history cannot be reconstructed."""
16
+
17
+
18
+ class CorrectionProjectMismatchError(Exception):
19
+ """Raised when a correction references a claim from another project."""
20
+
21
+
22
+ class SelfReplacementError(Exception):
23
+ """Raised when a claim is used as its own replacement."""
24
+
25
+
26
+ class InterpretationProjectMismatchError(Exception):
27
+ """Raised when interpretation inputs belong to different projects."""
28
+
29
+
30
+ class InvalidCandidateEvidenceError(Exception):
31
+ """Raised when an interpretation candidate references unavailable evidence."""
32
+
33
+
34
+ class NoActiveClaimsError(Exception):
35
+ """Raised when document generation has no active knowledge claims."""
36
+
37
+
38
+ class InvalidGeneratedClaimError(Exception):
39
+ """Raised when generated document provenance references an unavailable claim."""
40
+
41
+
42
+ class DocumentUnavailableError(Exception):
43
+ """Raised when a requested project document has not been generated."""
44
+
45
+
46
+ class DocumentVersionUnavailableError(Exception):
47
+ """Raised when a requested document version does not exist."""
@@ -0,0 +1,84 @@
1
+ """
2
+ Generates documentation artifacts from active project knowledge.
3
+ """
4
+
5
+ from quilchoom.application.create_document_version import create_document_version
6
+ from quilchoom.application.errors import InvalidGeneratedClaimError, NoActiveClaimsError
7
+ from quilchoom.domain.document import Document, DocumentVersion, DocumentVersionOrigin
8
+ from quilchoom.domain.knowledge_claim import ClaimStatus
9
+ from quilchoom.domain.project import Project
10
+ from quilchoom.infrastructure.database.repositories import (
11
+ DocumentRepository,
12
+ DocumentVersionRepository,
13
+ KnowledgeClaimRepository,
14
+ )
15
+ from quilchoom.interfaces.document_generator import (
16
+ DocumentGenerationContext,
17
+ DocumentGenerator,
18
+ )
19
+
20
+
21
+ def generate_document(
22
+ project: Project,
23
+ key: str,
24
+ kind: str,
25
+ generator: DocumentGenerator,
26
+ document_repository: DocumentRepository,
27
+ version_repository: DocumentVersionRepository,
28
+ claim_repository: KnowledgeClaimRepository,
29
+ ) -> DocumentVersion:
30
+ existing_document = document_repository.get_by_key(project.id, key)
31
+
32
+ if existing_document is None:
33
+ document = Document(
34
+ project_id=project.id,
35
+ key=key,
36
+ kind=kind,
37
+ )
38
+
39
+ else:
40
+ document = existing_document
41
+
42
+ claims = claim_repository.list_for_project(project.id)
43
+ active_claims = [claim for claim in claims if claim.status == ClaimStatus.ACTIVE]
44
+
45
+ if not active_claims:
46
+ raise NoActiveClaimsError(
47
+ f"No active knowledge claims available for project: {project.id}"
48
+ )
49
+
50
+ context = DocumentGenerationContext(
51
+ project=project,
52
+ document=document,
53
+ claims=active_claims,
54
+ )
55
+ result = generator.generate(context)
56
+
57
+ allowed_claim_ids = {claim.id for claim in active_claims}
58
+
59
+ for claim_id in result.claim_ids:
60
+ if claim_id not in allowed_claim_ids:
61
+ raise InvalidGeneratedClaimError(
62
+ f"Generated document references unavailable claim: {claim_id}"
63
+ )
64
+
65
+ if existing_document is None:
66
+ version = DocumentVersion(
67
+ document_id=document.id,
68
+ version_number=1,
69
+ content=result.content,
70
+ origin=DocumentVersionOrigin.GENERATED,
71
+ claim_ids=result.claim_ids,
72
+ )
73
+ document_repository.save_with_initial_version(document, version)
74
+
75
+ return version
76
+
77
+ return create_document_version(
78
+ document_id=document.id,
79
+ content=result.content,
80
+ origin=DocumentVersionOrigin.GENERATED,
81
+ claim_ids=result.claim_ids,
82
+ document_repository=document_repository,
83
+ version_repository=version_repository,
84
+ )
@@ -0,0 +1,48 @@
1
+ """
2
+ Retrieves a requested version of a project document.
3
+ """
4
+
5
+ from quilchoom.application.errors import (
6
+ DocumentUnavailableError,
7
+ DocumentVersionUnavailableError,
8
+ )
9
+ from quilchoom.domain.document import DocumentVersion
10
+ from quilchoom.domain.project import Project
11
+ from quilchoom.infrastructure.database.repositories import (
12
+ DocumentRepository,
13
+ DocumentVersionRepository,
14
+ )
15
+
16
+
17
+ def get_document_version(
18
+ project: Project,
19
+ key: str,
20
+ version_number: int | None,
21
+ document_repository: DocumentRepository,
22
+ version_repository: DocumentVersionRepository,
23
+ ) -> DocumentVersion:
24
+ """Retrieves the latest or requested version of a project document."""
25
+
26
+ document = document_repository.get_by_key(project_id=project.id, key=key)
27
+
28
+ if document is None:
29
+ raise DocumentUnavailableError(f"Document not found: {key}")
30
+
31
+ if version_number is None:
32
+ version = version_repository.get_latest(document_id=document.id)
33
+
34
+ else:
35
+ version = version_repository.get_by_version_number(
36
+ document_id=document.id, version_number=version_number
37
+ )
38
+
39
+ if version is None:
40
+ if version_number is None:
41
+ raise DocumentVersionUnavailableError("Latest document version not found")
42
+
43
+ else:
44
+ raise DocumentVersionUnavailableError(
45
+ f"Document version not found: {version_number}"
46
+ )
47
+
48
+ return version
@@ -0,0 +1,110 @@
1
+ """
2
+ Determines the current local workflow status of a Quilchoom project.
3
+ """
4
+
5
+ from enum import Enum
6
+ from pathlib import Path
7
+
8
+ from pydantic import BaseModel
9
+
10
+ from quilchoom.application.check_document_staleness import (
11
+ is_document_version_stale,
12
+ )
13
+ from quilchoom.application.check_knowledge_status import count_pending_evidence
14
+ from quilchoom.domain.knowledge_claim import ClaimStatus
15
+ from quilchoom.domain.project import Project
16
+ from quilchoom.infrastructure.database.repositories import (
17
+ DocumentRepository,
18
+ DocumentVersionRepository,
19
+ EvidenceRepository,
20
+ InterpretationRunRepository,
21
+ KnowledgeClaimRepository,
22
+ )
23
+ from quilchoom.infrastructure.git.repository import list_commit_shas
24
+
25
+
26
+ class DocumentState(str, Enum):
27
+ """Defines the freshness state of a generated project document."""
28
+
29
+ CURRENT = "current"
30
+ STALE = "stale"
31
+
32
+
33
+ class DocumentStatus(BaseModel):
34
+ """Represents the current status of a generated project document."""
35
+
36
+ key: str
37
+ kind: str
38
+ version_number: int
39
+ state: DocumentState
40
+
41
+
42
+ class ProjectStatus(BaseModel):
43
+ """Represents the current local workflow status of a Quilchoom project."""
44
+
45
+ total_commits: int
46
+ pending_commits: int
47
+ pending_evidence: int
48
+ active_claims: int
49
+ documents: list[DocumentStatus]
50
+
51
+
52
+ def get_project_status(
53
+ project: Project,
54
+ repository_root: Path,
55
+ evidence_repository: EvidenceRepository,
56
+ run_repository: InterpretationRunRepository,
57
+ claim_repository: KnowledgeClaimRepository,
58
+ document_repository: DocumentRepository,
59
+ version_repository: DocumentVersionRepository,
60
+ ) -> ProjectStatus:
61
+ """Determines the current local workflow status of a project."""
62
+
63
+ commit_shas = list_commit_shas(repository_root)
64
+
65
+ evidence = evidence_repository.list_for_project(project.id)
66
+
67
+ captured_commit_shas = {item.reference for item in evidence if item.source == "git"}
68
+
69
+ total_commits = len(commit_shas)
70
+ pending_commits = sum(1 for sha in commit_shas if sha not in captured_commit_shas)
71
+
72
+ pending_evidence = count_pending_evidence(
73
+ project=project,
74
+ evidence_repository=evidence_repository,
75
+ run_repository=run_repository,
76
+ )
77
+
78
+ claims = claim_repository.list_for_project(project.id)
79
+ active_claims = sum(1 for claim in claims if claim.status == ClaimStatus.ACTIVE)
80
+
81
+ documents = document_repository.list_for_project(project.id)
82
+
83
+ document_statuses: list[DocumentStatus] = []
84
+
85
+ for document in documents:
86
+ latest_version = version_repository.get_latest(document.id)
87
+
88
+ if latest_version is None:
89
+ continue
90
+
91
+ stale = is_document_version_stale(
92
+ version=latest_version,
93
+ claim_repository=claim_repository,
94
+ )
95
+
96
+ document_status = DocumentStatus(
97
+ key=document.key,
98
+ kind=document.kind,
99
+ version_number=latest_version.version_number,
100
+ state=(DocumentState.STALE if stale else DocumentState.CURRENT),
101
+ )
102
+ document_statuses.append(document_status)
103
+
104
+ return ProjectStatus(
105
+ total_commits=total_commits,
106
+ pending_commits=pending_commits,
107
+ pending_evidence=pending_evidence,
108
+ active_claims=active_claims,
109
+ documents=document_statuses,
110
+ )
@@ -0,0 +1,53 @@
1
+ """
2
+ Initializes a Quilchoom project from a Git repository.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ from quilchoom.application.errors import ProjectInitializationError
8
+ from quilchoom.domain.project import Project
9
+ from quilchoom.infrastructure.config import write_default_config
10
+ from quilchoom.infrastructure.database.connection import create_database_engine
11
+ from quilchoom.infrastructure.database.migrations import upgrade_database
12
+ from quilchoom.infrastructure.database.repositories import ProjectRepository
13
+ from quilchoom.infrastructure.filesystem.workspace import initialize_workspace
14
+ from quilchoom.infrastructure.git.repository import find_repository_root
15
+
16
+
17
+ def initialize_project(repository_path: Path | None = None) -> Project:
18
+ repo_root = find_repository_root(repository_path)
19
+
20
+ if repo_root is None:
21
+ raise ProjectInitializationError()
22
+
23
+ workspace = initialize_workspace(repo_root)
24
+
25
+ config_path = workspace / "config.toml"
26
+ write_default_config(config_path)
27
+
28
+ db_path = workspace / "quilchoom.db"
29
+ upgrade_database(db_path)
30
+
31
+ db_engine = create_database_engine(db_path)
32
+
33
+ project_repository = ProjectRepository(db_engine)
34
+ existing_project = project_repository.get()
35
+
36
+ if existing_project is not None:
37
+ if existing_project.repository_path == repo_root:
38
+ return existing_project
39
+
40
+ updated_project = project_repository.update_repository_path(
41
+ existing_project.id,
42
+ repo_root,
43
+ )
44
+
45
+ return updated_project
46
+
47
+ project = Project(
48
+ name=repo_root.name,
49
+ repository_path=repo_root,
50
+ )
51
+ project_repository.save(project)
52
+
53
+ return project