chronicle-devkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. chronicle_devkit-0.1.0/PKG-INFO +233 -0
  2. chronicle_devkit-0.1.0/README.md +217 -0
  3. chronicle_devkit-0.1.0/pyproject.toml +30 -0
  4. chronicle_devkit-0.1.0/setup.cfg +4 -0
  5. chronicle_devkit-0.1.0/src/chronicle/__init__.py +1 -0
  6. chronicle_devkit-0.1.0/src/chronicle/ai/__init__.py +0 -0
  7. chronicle_devkit-0.1.0/src/chronicle/ai/base.py +7 -0
  8. chronicle_devkit-0.1.0/src/chronicle/ai/factory.py +23 -0
  9. chronicle_devkit-0.1.0/src/chronicle/ai/google_ai.py +14 -0
  10. chronicle_devkit-0.1.0/src/chronicle/ai/openai.py +14 -0
  11. chronicle_devkit-0.1.0/src/chronicle/analysis/__init__.py +0 -0
  12. chronicle_devkit-0.1.0/src/chronicle/analysis/analyzers/__init__.py +0 -0
  13. chronicle_devkit-0.1.0/src/chronicle/analysis/analyzers/base.py +15 -0
  14. chronicle_devkit-0.1.0/src/chronicle/analysis/analyzers/django_migrations.py +74 -0
  15. chronicle_devkit-0.1.0/src/chronicle/analysis/analyzers/git.py +37 -0
  16. chronicle_devkit-0.1.0/src/chronicle/analysis/context.py +12 -0
  17. chronicle_devkit-0.1.0/src/chronicle/analysis/engine.py +18 -0
  18. chronicle_devkit-0.1.0/src/chronicle/cli/__init__.py +1 -0
  19. chronicle_devkit-0.1.0/src/chronicle/cli/commands/__init__.py +0 -0
  20. chronicle_devkit-0.1.0/src/chronicle/cli/commands/analyze.py +73 -0
  21. chronicle_devkit-0.1.0/src/chronicle/cli/commands/config.py +79 -0
  22. chronicle_devkit-0.1.0/src/chronicle/cli/commands/init.py +47 -0
  23. chronicle_devkit-0.1.0/src/chronicle/cli/commands/interpret.py +48 -0
  24. chronicle_devkit-0.1.0/src/chronicle/cli/commands/scan.py +87 -0
  25. chronicle_devkit-0.1.0/src/chronicle/cli/commands/show.py +106 -0
  26. chronicle_devkit-0.1.0/src/chronicle/cli/commands/status.py +26 -0
  27. chronicle_devkit-0.1.0/src/chronicle/cli/commands/version.py +6 -0
  28. chronicle_devkit-0.1.0/src/chronicle/cli/main.py +30 -0
  29. chronicle_devkit-0.1.0/src/chronicle/config/__init__.py +0 -0
  30. chronicle_devkit-0.1.0/src/chronicle/config/credentials.py +17 -0
  31. chronicle_devkit-0.1.0/src/chronicle/config/loader.py +21 -0
  32. chronicle_devkit-0.1.0/src/chronicle/config/manager.py +52 -0
  33. chronicle_devkit-0.1.0/src/chronicle/integrations/__init__.py +0 -0
  34. chronicle_devkit-0.1.0/src/chronicle/integrations/git.py +16 -0
  35. chronicle_devkit-0.1.0/src/chronicle/interpretation/__init__.py +0 -0
  36. chronicle_devkit-0.1.0/src/chronicle/interpretation/context.py +8 -0
  37. chronicle_devkit-0.1.0/src/chronicle/interpretation/context_builder.py +147 -0
  38. chronicle_devkit-0.1.0/src/chronicle/interpretation/interpreter.py +12 -0
  39. chronicle_devkit-0.1.0/src/chronicle/interpretation/prompts.py +51 -0
  40. chronicle_devkit-0.1.0/src/chronicle/project/__init__.py +0 -0
  41. chronicle_devkit-0.1.0/src/chronicle/project/discovery.py +9 -0
  42. chronicle_devkit-0.1.0/src/chronicle/project/initializer.py +45 -0
  43. chronicle_devkit-0.1.0/src/chronicle/project/status.py +25 -0
  44. chronicle_devkit-0.1.0/src/chronicle/scanning/__init__.py +0 -0
  45. chronicle_devkit-0.1.0/src/chronicle/scanning/context.py +10 -0
  46. chronicle_devkit-0.1.0/src/chronicle/scanning/engine.py +16 -0
  47. chronicle_devkit-0.1.0/src/chronicle/scanning/scanners/__init__.py +0 -0
  48. chronicle_devkit-0.1.0/src/chronicle/scanning/scanners/base.py +17 -0
  49. chronicle_devkit-0.1.0/src/chronicle/scanning/scanners/django_migrations.py +156 -0
  50. chronicle_devkit-0.1.0/src/chronicle/scanning/scanners/django_model.py +12 -0
  51. chronicle_devkit-0.1.0/src/chronicle/scanning/scanners/git.py +94 -0
  52. chronicle_devkit-0.1.0/src/chronicle/scanning/scanners/git_models.py +6 -0
  53. chronicle_devkit-0.1.0/src/chronicle/storage/__init__.py +0 -0
  54. chronicle_devkit-0.1.0/src/chronicle/storage/analysis_state.py +44 -0
  55. chronicle_devkit-0.1.0/src/chronicle/storage/database.py +9 -0
  56. chronicle_devkit-0.1.0/src/chronicle/storage/findings.py +53 -0
  57. chronicle_devkit-0.1.0/src/chronicle/storage/models.py +20 -0
  58. chronicle_devkit-0.1.0/src/chronicle/storage/observations.py +93 -0
  59. chronicle_devkit-0.1.0/src/chronicle/storage/scan_state.py +44 -0
  60. chronicle_devkit-0.1.0/src/chronicle/storage/schema.py +47 -0
  61. chronicle_devkit-0.1.0/src/chronicle_devkit.egg-info/PKG-INFO +233 -0
  62. chronicle_devkit-0.1.0/src/chronicle_devkit.egg-info/SOURCES.txt +64 -0
  63. chronicle_devkit-0.1.0/src/chronicle_devkit.egg-info/dependency_links.txt +1 -0
  64. chronicle_devkit-0.1.0/src/chronicle_devkit.egg-info/entry_points.txt +2 -0
  65. chronicle_devkit-0.1.0/src/chronicle_devkit.egg-info/requires.txt +12 -0
  66. chronicle_devkit-0.1.0/src/chronicle_devkit.egg-info/top_level.txt +1 -0
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: chronicle-devkit
3
+ Version: 0.1.0
4
+ Summary: A local first developer intelligence tool
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: typer>=0.16
8
+ Requires-Dist: tomli-w>=1.2.0
9
+ Requires-Dist: openai>=3.6.0
10
+ Requires-Dist: nltk>=3.10.3
11
+ Requires-Dist: google-genai>=2.20.0
12
+ Requires-Dist: keyring>=25.7.0
13
+ Requires-Dist: tomli>=2.0.0; python_version < "3.11"
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest>=8.0; extra == "dev"
16
+
17
+ # Chronicle
18
+
19
+ **Local-first developer intelligence.** Chronicle records how a software project changes over time, turns that history into signals, and lets you ask questions about it — all stored and analyzed on your own machine.
20
+
21
+ Chronicle fits naturally into your existing workflow. Point it at a Git project, and it:
22
+
23
+ 1. **Scans** your project's history into structured *observations* (git commits, Django migrations).
24
+ 2. **Analyzes** those observations into actionable *findings* (new files, changed schema fields …).
25
+ 3. **Interprets** the collected history with AI, answering questions in plain language.
26
+
27
+ ## How it works
28
+
29
+ ```
30
+ git history ──► scan ──► observations ──► analyze ──► findings ──► interpret (AI) ──► answers
31
+ Django migrations ─┘ local SQLite ▲
32
+ keywords + related commits
33
+ ```
34
+
35
+ - **Local-first** — everything lives in a `.chronicle/` folder inside your project. No accounts, no cloud sync, no telemetry. Your `chronicle.db` stays yours.
36
+ - **Incremental** — Chronicle remembers how far it got (`scan_state` for scanners, `analysis_state` for analyzers) so repeated runs only process what's new.
37
+ - **Extensible** — scanners (`Scanner`) and analyzers (`BaseAnalyzer`) are pluggable; providers are pluggable too (OpenAI / Gemini).
38
+
39
+ ## Requirements
40
+
41
+ - **Python 3.10+**
42
+ - A **Git** repository (the project root is detected by walking up to the nearest `.git` directory)
43
+ - `pip` via the `dev` extra for running tests
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ # clone and enter the repository
49
+ git clone https://github.com/Sakshyam-Xtha/Chronicle.git
50
+ cd Chronicle
51
+
52
+ # create a virtual environment (optional but recommended)
53
+ python -m venv .venv
54
+ source .venv/bin/activate
55
+
56
+ # install in editable mode (includes the `chronicle` command)
57
+ pip install -e ".[dev]"
58
+ ```
59
+
60
+ Verify the CLI is on your path:
61
+
62
+ ```bash
63
+ chronicle version # -> Chronicle 0.1.0
64
+ ```
65
+
66
+ ## Quick start
67
+
68
+ ```bash
69
+ # 1. Initialize Chronicle in a project (creates .chronicle/ + local DB)
70
+ cd /path/to/your/project
71
+ chronicle init
72
+
73
+ # 2. Scan the project history into observations
74
+ chronicle scan
75
+
76
+ # 3. Browse what was recorded
77
+ chronicle show
78
+ chronicle show --id 1
79
+
80
+ # 4. Analyze observations into findings
81
+ chronicle analyze
82
+
83
+ # 5. Ask questions about your history (requires an AI provider/key)
84
+ chronicle interpret --question "When did we add the User model?"
85
+ ```
86
+
87
+ > `init`, `scan`, and `show` need no configuration. Only `interpret` requires an AI provider.
88
+
89
+ ## Command reference
90
+
91
+ | Command | Description |
92
+ | --- | --- |
93
+ | `chronicle init` | Create the `.chronicle/` directory and initialize the local database |
94
+ | `chronicle scan` | Scan project history into observations (git commits, Django migrations) |
95
+ | `chronicle show` | List all observations; `--id <n>` shows one observation in detail |
96
+ | `chronicle analyze` | Turn observations into findings via the installed analyzers |
97
+ | `chronicle interpret --question "<text>"` | Ask an AI-backed question about the project history |
98
+ | `chronicle config` | View current configuration; `set provider` / `set model` to configure AI |
99
+ | `chronicle status` | Show whether the project is initialized and detected |
100
+ | `chronicle version` | Print the Chronicle version |
101
+
102
+ ### `chronicle status`
103
+
104
+ ```bash
105
+ $ chronicle status
106
+ Project: my-project
107
+ Root: /path/to/my-project
108
+ Git: detected
109
+ Chronicle: initialized
110
+ Configuration: found
111
+ ```
112
+
113
+ ### `chronicle show`
114
+
115
+ Without arguments it prints a compact table of all observations. With `--id <n>` it renders a detailed view:
116
+
117
+ ```text
118
+ Observation #1
119
+ ────────────────────────────────────────────
120
+ Source: git
121
+ Type: commit
122
+ External ID: 9f8d3a1
123
+ Timestamp: 2026-08-21 12:00:00 UTC
124
+
125
+ Data
126
+ ────────────────────────────────────────────
127
+ ```
128
+ Migration observations additionally show **App**, **Migration**, **Dependencies**, and **Operations** (model / field per operation).
129
+
130
+ ## Configuring AI (for `interpret`)
131
+
132
+ Chronicle reads its configuration from `.chronicle/config.toml`:
133
+
134
+ ```toml
135
+ [chronicle]
136
+ version = 1
137
+
138
+ [ai]
139
+ provider = ""
140
+ model = ""
141
+ ```
142
+
143
+ Set the provider and model:
144
+
145
+ ```bash
146
+ chronicle config set provider openai
147
+ chronicle config set model gpt-4o
148
+ ```
149
+
150
+ Then export your API key as an environment variable:
151
+
152
+ ```bash
153
+ export OPENAI_API_KEY="sk-..." # provider: openai
154
+ export GEMINI_API_KEY="..." # provider: gemini
155
+ ```
156
+
157
+ Check everything is wired up:
158
+
159
+ ```bash
160
+ chronicle config
161
+ ```
162
+
163
+ ## What gets recorded
164
+
165
+ ### Git commits (`scan`)
166
+
167
+ Each commit becomes an observation with:
168
+
169
+ - `hash`, `message`, `author`
170
+ - `parents` (empty for the root commit, one or more for merges)
171
+ - `changes` — per-file **status** (`A` added, `M` modified, `D` deleted) and **path**
172
+
173
+ ### Django migrations (`scan`)
174
+
175
+ Every `migrations/*.py` file (ignoring `.git`, `.venv`, `venv`, `env`, `node_modules`, `__pycache__`) is parsed with `ast` into an observation with:
176
+
177
+ - `app` (application label), `name` (migration name)
178
+ - `dependencies`
179
+ - `operations` — e.g. `AddField`, `RemoveField` with model / field details
180
+
181
+ ### Findings (`analyze`)
182
+
183
+ Analyzers turn observations into findings:
184
+
185
+ - **GitAnalyzer** — flags newly created files (`A`) with `severity: info`
186
+ - **DjangoMigrationAnalyzer** — flags `RemoveField` (`warning`) and `AddField` (`info`) operations
187
+
188
+ ## How interpretation works
189
+
190
+ `chronicle interpret` does **not** dump everything at the model. It:
191
+
192
+ 1. Tokenizes your question and drops stop words to extract keywords.
193
+ 2. Scores every finding by how well it matches those keywords (title, message, data).
194
+ 3. Picks the top 10 findings and their related git commits (e.g. the commit that introduced a migration).
195
+ 4. Builds a prompt with only that focused context and asks the configured provider.
196
+
197
+ ## Project layout
198
+
199
+ ```
200
+ src/chronicle/
201
+ ├── ai/ # AI provider abstraction (OpenAI, Gemini, factory)
202
+ ├── analysis/ # analyzers + engine that turn observations into findings
203
+ ├── cli/ # Typer CLI commands (`scan`, `show`, `analyze`, …)
204
+ ├── config/ # config.toml management + API key resolution
205
+ ├── integrations/ # thin wrappers (e.g. git)
206
+ ├── interpretation/ # question -> context -> prompt -> AI response
207
+ ├── project/ # discovery, initialization, status
208
+ ├── scanning/ # scanners + engine that collect observations
209
+ │ └── scanners/ # git, django_migrations and their models
210
+ └── storage/ # SQLite repositories + schema
211
+ ```
212
+
213
+ ## Development
214
+
215
+ Run the test suite:
216
+
217
+ ```bash
218
+ pip install -e ".[dev]"
219
+ pytest test/
220
+ ```
221
+
222
+ The suite is split into:
223
+
224
+ - `test/unit/` — fast, mocked tests (parsing, storage, contexts, models)
225
+ - `test/integration/` — real-git and end-to-end CLI tests (incremental scan checkpoints, migration parsing, merge parents)
226
+
227
+ ## Roadmap / status
228
+
229
+ Chronicle is an early-stage local-first tool (`0.1.0`). Current scanners cover **git** and **Django migrations**; analyzers cover **git file additions** and **Django schema changes**; AI interpretation supports **OpenAI** and **Gemini**.
230
+
231
+ ## License
232
+
233
+ Not yet specified.
@@ -0,0 +1,217 @@
1
+ # Chronicle
2
+
3
+ **Local-first developer intelligence.** Chronicle records how a software project changes over time, turns that history into signals, and lets you ask questions about it — all stored and analyzed on your own machine.
4
+
5
+ Chronicle fits naturally into your existing workflow. Point it at a Git project, and it:
6
+
7
+ 1. **Scans** your project's history into structured *observations* (git commits, Django migrations).
8
+ 2. **Analyzes** those observations into actionable *findings* (new files, changed schema fields …).
9
+ 3. **Interprets** the collected history with AI, answering questions in plain language.
10
+
11
+ ## How it works
12
+
13
+ ```
14
+ git history ──► scan ──► observations ──► analyze ──► findings ──► interpret (AI) ──► answers
15
+ Django migrations ─┘ local SQLite ▲
16
+ keywords + related commits
17
+ ```
18
+
19
+ - **Local-first** — everything lives in a `.chronicle/` folder inside your project. No accounts, no cloud sync, no telemetry. Your `chronicle.db` stays yours.
20
+ - **Incremental** — Chronicle remembers how far it got (`scan_state` for scanners, `analysis_state` for analyzers) so repeated runs only process what's new.
21
+ - **Extensible** — scanners (`Scanner`) and analyzers (`BaseAnalyzer`) are pluggable; providers are pluggable too (OpenAI / Gemini).
22
+
23
+ ## Requirements
24
+
25
+ - **Python 3.10+**
26
+ - A **Git** repository (the project root is detected by walking up to the nearest `.git` directory)
27
+ - `pip` via the `dev` extra for running tests
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ # clone and enter the repository
33
+ git clone https://github.com/Sakshyam-Xtha/Chronicle.git
34
+ cd Chronicle
35
+
36
+ # create a virtual environment (optional but recommended)
37
+ python -m venv .venv
38
+ source .venv/bin/activate
39
+
40
+ # install in editable mode (includes the `chronicle` command)
41
+ pip install -e ".[dev]"
42
+ ```
43
+
44
+ Verify the CLI is on your path:
45
+
46
+ ```bash
47
+ chronicle version # -> Chronicle 0.1.0
48
+ ```
49
+
50
+ ## Quick start
51
+
52
+ ```bash
53
+ # 1. Initialize Chronicle in a project (creates .chronicle/ + local DB)
54
+ cd /path/to/your/project
55
+ chronicle init
56
+
57
+ # 2. Scan the project history into observations
58
+ chronicle scan
59
+
60
+ # 3. Browse what was recorded
61
+ chronicle show
62
+ chronicle show --id 1
63
+
64
+ # 4. Analyze observations into findings
65
+ chronicle analyze
66
+
67
+ # 5. Ask questions about your history (requires an AI provider/key)
68
+ chronicle interpret --question "When did we add the User model?"
69
+ ```
70
+
71
+ > `init`, `scan`, and `show` need no configuration. Only `interpret` requires an AI provider.
72
+
73
+ ## Command reference
74
+
75
+ | Command | Description |
76
+ | --- | --- |
77
+ | `chronicle init` | Create the `.chronicle/` directory and initialize the local database |
78
+ | `chronicle scan` | Scan project history into observations (git commits, Django migrations) |
79
+ | `chronicle show` | List all observations; `--id <n>` shows one observation in detail |
80
+ | `chronicle analyze` | Turn observations into findings via the installed analyzers |
81
+ | `chronicle interpret --question "<text>"` | Ask an AI-backed question about the project history |
82
+ | `chronicle config` | View current configuration; `set provider` / `set model` to configure AI |
83
+ | `chronicle status` | Show whether the project is initialized and detected |
84
+ | `chronicle version` | Print the Chronicle version |
85
+
86
+ ### `chronicle status`
87
+
88
+ ```bash
89
+ $ chronicle status
90
+ Project: my-project
91
+ Root: /path/to/my-project
92
+ Git: detected
93
+ Chronicle: initialized
94
+ Configuration: found
95
+ ```
96
+
97
+ ### `chronicle show`
98
+
99
+ Without arguments it prints a compact table of all observations. With `--id <n>` it renders a detailed view:
100
+
101
+ ```text
102
+ Observation #1
103
+ ────────────────────────────────────────────
104
+ Source: git
105
+ Type: commit
106
+ External ID: 9f8d3a1
107
+ Timestamp: 2026-08-21 12:00:00 UTC
108
+
109
+ Data
110
+ ────────────────────────────────────────────
111
+ ```
112
+ Migration observations additionally show **App**, **Migration**, **Dependencies**, and **Operations** (model / field per operation).
113
+
114
+ ## Configuring AI (for `interpret`)
115
+
116
+ Chronicle reads its configuration from `.chronicle/config.toml`:
117
+
118
+ ```toml
119
+ [chronicle]
120
+ version = 1
121
+
122
+ [ai]
123
+ provider = ""
124
+ model = ""
125
+ ```
126
+
127
+ Set the provider and model:
128
+
129
+ ```bash
130
+ chronicle config set provider openai
131
+ chronicle config set model gpt-4o
132
+ ```
133
+
134
+ Then export your API key as an environment variable:
135
+
136
+ ```bash
137
+ export OPENAI_API_KEY="sk-..." # provider: openai
138
+ export GEMINI_API_KEY="..." # provider: gemini
139
+ ```
140
+
141
+ Check everything is wired up:
142
+
143
+ ```bash
144
+ chronicle config
145
+ ```
146
+
147
+ ## What gets recorded
148
+
149
+ ### Git commits (`scan`)
150
+
151
+ Each commit becomes an observation with:
152
+
153
+ - `hash`, `message`, `author`
154
+ - `parents` (empty for the root commit, one or more for merges)
155
+ - `changes` — per-file **status** (`A` added, `M` modified, `D` deleted) and **path**
156
+
157
+ ### Django migrations (`scan`)
158
+
159
+ Every `migrations/*.py` file (ignoring `.git`, `.venv`, `venv`, `env`, `node_modules`, `__pycache__`) is parsed with `ast` into an observation with:
160
+
161
+ - `app` (application label), `name` (migration name)
162
+ - `dependencies`
163
+ - `operations` — e.g. `AddField`, `RemoveField` with model / field details
164
+
165
+ ### Findings (`analyze`)
166
+
167
+ Analyzers turn observations into findings:
168
+
169
+ - **GitAnalyzer** — flags newly created files (`A`) with `severity: info`
170
+ - **DjangoMigrationAnalyzer** — flags `RemoveField` (`warning`) and `AddField` (`info`) operations
171
+
172
+ ## How interpretation works
173
+
174
+ `chronicle interpret` does **not** dump everything at the model. It:
175
+
176
+ 1. Tokenizes your question and drops stop words to extract keywords.
177
+ 2. Scores every finding by how well it matches those keywords (title, message, data).
178
+ 3. Picks the top 10 findings and their related git commits (e.g. the commit that introduced a migration).
179
+ 4. Builds a prompt with only that focused context and asks the configured provider.
180
+
181
+ ## Project layout
182
+
183
+ ```
184
+ src/chronicle/
185
+ ├── ai/ # AI provider abstraction (OpenAI, Gemini, factory)
186
+ ├── analysis/ # analyzers + engine that turn observations into findings
187
+ ├── cli/ # Typer CLI commands (`scan`, `show`, `analyze`, …)
188
+ ├── config/ # config.toml management + API key resolution
189
+ ├── integrations/ # thin wrappers (e.g. git)
190
+ ├── interpretation/ # question -> context -> prompt -> AI response
191
+ ├── project/ # discovery, initialization, status
192
+ ├── scanning/ # scanners + engine that collect observations
193
+ │ └── scanners/ # git, django_migrations and their models
194
+ └── storage/ # SQLite repositories + schema
195
+ ```
196
+
197
+ ## Development
198
+
199
+ Run the test suite:
200
+
201
+ ```bash
202
+ pip install -e ".[dev]"
203
+ pytest test/
204
+ ```
205
+
206
+ The suite is split into:
207
+
208
+ - `test/unit/` — fast, mocked tests (parsing, storage, contexts, models)
209
+ - `test/integration/` — real-git and end-to-end CLI tests (incremental scan checkpoints, migration parsing, merge parents)
210
+
211
+ ## Roadmap / status
212
+
213
+ Chronicle is an early-stage local-first tool (`0.1.0`). Current scanners cover **git** and **Django migrations**; analyzers cover **git file additions** and **Django schema changes**; AI interpretation supports **OpenAI** and **Gemini**.
214
+
215
+ ## License
216
+
217
+ Not yet specified.
@@ -0,0 +1,30 @@
1
+ [project]
2
+ name = "chronicle-devkit"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.10"
5
+ description = "A local first developer intelligence tool"
6
+ dependencies = [
7
+ "typer>=0.16",
8
+ "tomli-w>=1.2.0",
9
+ "openai>=3.6.0",
10
+ "nltk>=3.10.3",
11
+ "google-genai>=2.20.0",
12
+ "keyring>=25.7.0",
13
+ "tomli>=2.0.0; python_version < '3.11'",
14
+ ]
15
+ readme = "README.md"
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=8.0",
20
+ ]
21
+
22
+ [build-system]
23
+ requires = ["setuptools>=68"]
24
+ build-backend = "setuptools.build_meta"
25
+
26
+ [project.scripts]
27
+ chronicle = "chronicle.cli.main:main"
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,7 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ class AIProvider(ABC):
4
+ @abstractmethod
5
+ def generate(self,prompt:str) -> str:
6
+ """Generate a response from the AI model."""
7
+ raise NotImplementedError
@@ -0,0 +1,23 @@
1
+ from chronicle.ai.base import AIProvider
2
+ from chronicle.ai.openai import OpenAIProvider
3
+ from chronicle.ai.google_ai import GenAIProvider
4
+
5
+ def create_provider(
6
+ provider: str,
7
+ model:str,
8
+ api_key:str
9
+ ) -> AIProvider:
10
+ if provider == "openai":
11
+ return OpenAIProvider(
12
+ api_key=api_key,
13
+ model=model,
14
+ )
15
+ elif provider == "gemini":
16
+ return GenAIProvider(
17
+ api_key=api_key,
18
+ model=model,
19
+ )
20
+
21
+ raise ValueError(
22
+ f"Unsupported AI provider: {provider}"
23
+ )
@@ -0,0 +1,14 @@
1
+ from google import genai
2
+ from chronicle.ai.base import AIProvider
3
+
4
+ class GenAIProvider(AIProvider):
5
+ def __init__(self,model:str,api_key:str) -> None:
6
+ self.client = genai.Client(api_key=api_key)
7
+ self.model = model
8
+
9
+ def generate(self, prompt: str) -> str:
10
+ response = self.client.models.generate_content(
11
+ model=self.model,
12
+ contents = prompt,
13
+ )
14
+ return response.text if response.text else "Error: Model did not respond"
@@ -0,0 +1,14 @@
1
+ from openai import OpenAI
2
+ from chronicle.ai.base import AIProvider
3
+
4
+ class OpenAIProvider(AIProvider):
5
+ def __init__(self,api_key:str,model:str) -> None:
6
+ self.client = OpenAI(api_key=api_key)
7
+ self.model = model
8
+
9
+ def generate(self, prompt: str) -> str:
10
+ response = self.client.responses.create(
11
+ model=self.model,
12
+ input=prompt,
13
+ )
14
+ return response.output_text
@@ -0,0 +1,15 @@
1
+ from abc import ABC,abstractmethod
2
+ from pathlib import Path
3
+ from chronicle.storage.models import Observation,Findings
4
+ from chronicle.analysis.context import AnalysisContext
5
+
6
+ class BaseAnalyzer(ABC):
7
+ """Base class for all chronicle analyzers."""
8
+
9
+ def __init__(self,project_root:Path) -> None:
10
+ self.project_root = project_root
11
+
12
+ @abstractmethod
13
+ def analyze(self, context:AnalysisContext) -> list[Findings]:
14
+ """Analyze the project and return findings."""
15
+ pass
@@ -0,0 +1,74 @@
1
+ from chronicle.analysis.analyzers.base import BaseAnalyzer
2
+ from chronicle.storage.models import Findings
3
+ import typer
4
+ from chronicle.storage.observations import Observation
5
+ from chronicle.analysis.context import AnalysisContext
6
+
7
+ class DjangoMigrationAnalyzer(BaseAnalyzer):
8
+
9
+ def analyze(
10
+ self,
11
+ context: AnalysisContext
12
+ ) -> list[Findings]:
13
+
14
+ findings = []
15
+
16
+ for observation in context.observations:
17
+
18
+ if observation.source != "django":
19
+ continue
20
+
21
+ if observation.type != "migration":
22
+ continue
23
+
24
+ operations = observation.data.get(
25
+ "operations",
26
+ []
27
+ )
28
+
29
+ for operation in operations:
30
+
31
+ operation_name = operation.get(
32
+ "operation"
33
+ )
34
+
35
+ if operation_name == "RemoveField":
36
+ typer.echo(
37
+ f"Generating finding: "
38
+ f"observation={observation.id}, "
39
+ f"data={operation}"
40
+ )
41
+ findings.append(
42
+ Findings(
43
+ analyzer="django-migrations",
44
+ severity="warning",
45
+ title="Field removed",
46
+ message=(
47
+ "A database field is being "
48
+ "removed by this migration."
49
+ ),
50
+ observation_id= observation.id, #type: ignore
51
+ data=operation,
52
+ )
53
+ )
54
+ if operation_name == "AddField":
55
+ typer.echo(
56
+ f"Generating finding: "
57
+ f"observation={observation.id}, "
58
+ f"data={operation}"
59
+ )
60
+ findings.append(
61
+ Findings(
62
+ analyzer="django-migrations",
63
+ severity="info",
64
+ title="Field added",
65
+ message=(
66
+ "A database field is being "
67
+ "added by this migration."
68
+ ),
69
+ observation_id= observation.id, #type: ignore
70
+ data=operation,
71
+ )
72
+ )
73
+
74
+ return findings
@@ -0,0 +1,37 @@
1
+ from pathlib import Path
2
+
3
+ from chronicle.storage.models import Observation,Findings
4
+ from .base import BaseAnalyzer
5
+ from chronicle.analysis.context import AnalysisContext
6
+
7
+ class GitAnalyzer(BaseAnalyzer):
8
+ def __init__(self, project_root: Path) -> None:
9
+ super().__init__(project_root)
10
+
11
+ def analyze(self,context:AnalysisContext) -> list[Findings]:
12
+ findings = []
13
+ for observation in context.observations:
14
+ if observation.type != "commit":
15
+ continue
16
+ if observation.source != "git":
17
+ continue
18
+
19
+ message = observation.data.get("message","Not given")
20
+ author = observation.data.get("author","unknown")
21
+ parsed_message = author + ":" + message
22
+ changes = observation.data.get("changes",[])
23
+
24
+ for change in changes:
25
+ file_status = change.get("status")
26
+ if file_status == "A":
27
+ findings.append(
28
+ Findings(
29
+ analyzer="git_analyzer",
30
+ severity="info",
31
+ title="New File created",
32
+ message=parsed_message,
33
+ observation_id=observation.id, #type: ignore
34
+ data=change
35
+ ))
36
+
37
+ return findings