devtime-ei 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 (54) hide show
  1. devtime/__init__.py +9 -0
  2. devtime/ai/__init__.py +0 -0
  3. devtime/ai/local.py +11 -0
  4. devtime/ai/prompts.py +24 -0
  5. devtime/ai/providers.py +41 -0
  6. devtime/assets/devtimeignore.starter +23 -0
  7. devtime/cli.py +374 -0
  8. devtime/config.py +67 -0
  9. devtime/db/__init__.py +0 -0
  10. devtime/db/connection.py +16 -0
  11. devtime/db/migrations.py +114 -0
  12. devtime/db/repository.py +351 -0
  13. devtime/db/schema.sql +145 -0
  14. devtime/fixtures/__init__.py +0 -0
  15. devtime/fixtures/assertions.py +51 -0
  16. devtime/fixtures/loader.py +52 -0
  17. devtime/fixtures/runner.py +73 -0
  18. devtime/intelligence/__init__.py +0 -0
  19. devtime/intelligence/claims.py +235 -0
  20. devtime/intelligence/concepts.py +483 -0
  21. devtime/intelligence/context_pack.py +276 -0
  22. devtime/intelligence/evidence.py +127 -0
  23. devtime/intelligence/lineage.py +21 -0
  24. devtime/intelligence/risk.py +267 -0
  25. devtime/intelligence/scoring.py +99 -0
  26. devtime/mcp/__init__.py +0 -0
  27. devtime/mcp/schemas.py +39 -0
  28. devtime/mcp/server.py +35 -0
  29. devtime/mcp/tools.py +90 -0
  30. devtime/output/__init__.py +0 -0
  31. devtime/output/json_export.py +50 -0
  32. devtime/output/markdown.py +50 -0
  33. devtime/output/terminal.py +208 -0
  34. devtime/paths.py +40 -0
  35. devtime/privacy.py +96 -0
  36. devtime/scanner/__init__.py +0 -0
  37. devtime/scanner/extractors/__init__.py +0 -0
  38. devtime/scanner/extractors/base.py +83 -0
  39. devtime/scanner/extractors/config_files.py +41 -0
  40. devtime/scanner/extractors/docs.py +35 -0
  41. devtime/scanner/extractors/nextjs.py +82 -0
  42. devtime/scanner/extractors/python.py +81 -0
  43. devtime/scanner/extractors/tests.py +61 -0
  44. devtime/scanner/extractors/typescript.py +99 -0
  45. devtime/scanner/file_walker.py +96 -0
  46. devtime/scanner/ignore.py +96 -0
  47. devtime/scanner/language.py +36 -0
  48. devtime/scanner/signals.py +252 -0
  49. devtime_ei-0.1.0.dist-info/METADATA +289 -0
  50. devtime_ei-0.1.0.dist-info/RECORD +54 -0
  51. devtime_ei-0.1.0.dist-info/WHEEL +5 -0
  52. devtime_ei-0.1.0.dist-info/entry_points.txt +2 -0
  53. devtime_ei-0.1.0.dist-info/licenses/LICENSE +201 -0
  54. devtime_ei-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,96 @@
1
+ """Ignore-rule matching (Builder Edition, Chapter 7).
2
+
3
+ Respects .gitignore and .devtimeignore, and always refuses secrets, VCS internals,
4
+ and common generated directories even if the ignore files are missing.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+
11
+ import pathspec
12
+
13
+ # Always-on safety net. These must never become evidence (Chapter 7 safety rules).
14
+ HARD_DENY = [
15
+ ".git/",
16
+ ".env",
17
+ ".env.*",
18
+ "*.pem",
19
+ "*.key",
20
+ "*.p12",
21
+ "*.pfx",
22
+ "id_rsa",
23
+ "id_ed25519",
24
+ "secrets.*",
25
+ "credentials.*",
26
+ "service-account*.json",
27
+ ".devtime/",
28
+ ]
29
+
30
+ # Directories pruned before traversal regardless of ignore files (Reality
31
+ # Hardening): never descend into these, so large trees are skipped, not filtered.
32
+ PRUNE_DIRS = {
33
+ "node_modules", ".git", "dist", "build", "coverage", ".next", "out",
34
+ "__pycache__", ".pytest_cache", ".venv", "venv", ".cache", ".devtime",
35
+ ".turbo", ".sst", ".open-next", ".svelte-kit", ".nuxt",
36
+ }
37
+
38
+
39
+ def is_pruned_dirname(name: str) -> bool:
40
+ return name in PRUNE_DIRS or name.endswith(".egg-info")
41
+
42
+
43
+ BINARY_EXTENSIONS = {
44
+ ".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".svg",
45
+ ".pdf", ".zip", ".gz", ".tar", ".7z", ".rar",
46
+ ".woff", ".woff2", ".ttf", ".eot", ".otf",
47
+ ".mp3", ".mp4", ".mov", ".avi", ".wav",
48
+ ".exe", ".dll", ".so", ".dylib", ".bin", ".class", ".o", ".a",
49
+ ".lock", ".sqlite", ".db",
50
+ }
51
+
52
+
53
+ class IgnoreMatcher:
54
+ def __init__(self, patterns: list[str]):
55
+ self._spec = pathspec.PathSpec.from_lines("gitignore", patterns)
56
+
57
+ def match(self, rel_path: str) -> bool:
58
+ return self._spec.match_file(rel_path)
59
+
60
+ def match_dir(self, rel_dir: str) -> bool:
61
+ """True if a directory is ignored (so it can be pruned before traversal).
62
+
63
+ gitignore patterns may target a directory with or without a trailing
64
+ slash, so both forms are checked.
65
+ """
66
+ rel_dir = rel_dir.rstrip("/")
67
+ return self._spec.match_file(rel_dir) or self._spec.match_file(rel_dir + "/")
68
+
69
+
70
+ def _read_patterns(path: Path) -> list[str]:
71
+ if not path.exists():
72
+ return []
73
+ out: list[str] = []
74
+ for line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
75
+ line = line.strip()
76
+ if line and not line.startswith("#"):
77
+ out.append(line)
78
+ return out
79
+
80
+
81
+ def build_matcher(
82
+ root: Path,
83
+ *,
84
+ respect_gitignore: bool = True,
85
+ respect_devtimeignore: bool = True,
86
+ ) -> IgnoreMatcher:
87
+ patterns = list(HARD_DENY)
88
+ if respect_gitignore:
89
+ patterns += _read_patterns(root / ".gitignore")
90
+ if respect_devtimeignore:
91
+ patterns += _read_patterns(root / ".devtimeignore")
92
+ return IgnoreMatcher(patterns)
93
+
94
+
95
+ def is_binary_extension(extension: str) -> bool:
96
+ return extension.lower() in BINARY_EXTENSIONS
@@ -0,0 +1,36 @@
1
+ """Language and file-role classification (Builder Edition, Chapter 7)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ TEST_HINTS = (".test.", ".spec.", "_test.", "test_")
6
+ TEST_DIRS = ("/tests/", "/test/", "/__tests__/", "tests/", "test/")
7
+ DOC_DIRS = ("/docs/", "docs/")
8
+ DOC_EXTS = (".md", ".mdx", ".rst")
9
+
10
+
11
+ def classify_language(path: str) -> str | None:
12
+ if path.endswith((".ts", ".tsx", ".js", ".jsx")):
13
+ return "typescript"
14
+ if path.endswith(".py"):
15
+ return "python"
16
+ if path.endswith((".md", ".mdx")):
17
+ return "markdown"
18
+ if path.endswith((".yml", ".yaml")):
19
+ return "yaml"
20
+ if path.endswith(".json"):
21
+ return "json"
22
+ return None
23
+
24
+
25
+ def is_test_path(rel_path: str) -> bool:
26
+ lower = rel_path.lower()
27
+ if any(hint in lower for hint in TEST_HINTS):
28
+ return True
29
+ return any(d in lower for d in TEST_DIRS)
30
+
31
+
32
+ def is_doc_path(rel_path: str) -> bool:
33
+ lower = rel_path.lower()
34
+ if lower.endswith(DOC_EXTS):
35
+ return True
36
+ return any(d in lower for d in DOC_DIRS)
@@ -0,0 +1,252 @@
1
+ """Scan orchestration (Builder Edition, Chapter 7 pipeline).
2
+
3
+ scan repository -> load config -> load ignore rules -> walk files safely ->
4
+ classify -> hash -> extract signals -> persist files and signals -> detect
5
+ concepts -> build evidence -> generate claims -> compute scores -> write summary.
6
+
7
+ Never executes repository code, never makes network calls.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import sqlite3
14
+ import uuid
15
+ from dataclasses import dataclass, field
16
+ from datetime import datetime, timezone
17
+ from pathlib import Path
18
+
19
+ from devtime import __version__, config, paths
20
+ from devtime.db import connection, migrations, repository
21
+ from devtime.intelligence import claims as claims_mod
22
+ from devtime.intelligence import concepts as concepts_mod
23
+ from devtime.intelligence import evidence as evidence_mod
24
+ from devtime.scanner import ignore as ignore_mod
25
+ from devtime.scanner.extractors import (
26
+ config_files,
27
+ docs,
28
+ nextjs,
29
+ python,
30
+ tests,
31
+ typescript,
32
+ )
33
+ from devtime.scanner.extractors.base import Signal
34
+ from devtime.scanner.file_walker import WalkedFile, walk_repository
35
+ from devtime.scanner.language import classify_language
36
+
37
+
38
+ SUPPORTED_CONCEPT_FAMILIES = (
39
+ "Authentication", "Billing Webhooks", "Background Jobs",
40
+ "Data Export", "Admin Permissions", "File Uploads",
41
+ )
42
+
43
+ # Frameworks whose routes/controllers V0 does not parse, keyed by a dependency token.
44
+ UNSUPPORTED_FRAMEWORKS = {
45
+ "django": "Django routes are not parsed in V0; route coverage may be incomplete.",
46
+ "@nestjs/core": "NestJS controller parsing is not supported in V0; backend route coverage may be incomplete.",
47
+ "@nestjs/common": "NestJS controller parsing is not supported in V0; backend route coverage may be incomplete.",
48
+ "rails": "Rails routes are not parsed in V0; route coverage may be incomplete.",
49
+ "laravel/framework": "Laravel routes are not parsed in V0; route coverage may be incomplete.",
50
+ }
51
+
52
+
53
+ @dataclass
54
+ class ScanResult:
55
+ file_count: int
56
+ signal_count: int
57
+ concept_count: int
58
+ intelligence: list[claims_mod.ConceptIntelligence]
59
+ duration_seconds: float = 0.0
60
+ pruned_dirs: int = 0
61
+ skipped_files: int = 0
62
+ framework_warnings: list[str] = field(default_factory=list)
63
+
64
+
65
+ def detect_framework_warnings(signals: list[Signal]) -> list[str]:
66
+ """Surface coverage gaps for frameworks V0 cannot parse (Trust Repair v0.0.6)."""
67
+ deps = {
68
+ (s.name or "").lower()
69
+ for s in signals
70
+ if s.kind == "dependency" and s.name
71
+ }
72
+ warnings: list[str] = []
73
+ seen: set[str] = set()
74
+ for token, message in UNSUPPORTED_FRAMEWORKS.items():
75
+ if token in deps and message not in seen:
76
+ warnings.append(f"Framework coverage warning: {message}")
77
+ seen.add(message)
78
+ return warnings
79
+
80
+
81
+ def _now() -> str:
82
+ return datetime.now(timezone.utc).isoformat()
83
+
84
+
85
+ def _hash_file(path: Path) -> str:
86
+ h = hashlib.sha256()
87
+ try:
88
+ with path.open("rb") as fh:
89
+ for chunk in iter(lambda: fh.read(65536), b""):
90
+ h.update(chunk)
91
+ except OSError:
92
+ return ""
93
+ return h.hexdigest()
94
+
95
+
96
+ def _is_migration_path(rel_path: str) -> bool:
97
+ """DB migrations describe schema history, not live behavior or workers.
98
+
99
+ Reality Validation finding: an Alembic migration named *_add_jobs.py was being
100
+ used as Background Jobs evidence. Migration files must not produce
101
+ concept-defining signals.
102
+ """
103
+ low = rel_path.lower()
104
+ return (
105
+ "/migrations/" in low
106
+ or low.startswith("migrations/")
107
+ or "/alembic/versions/" in low
108
+ or "alembic/versions/" in low
109
+ )
110
+
111
+
112
+ def _extract(file: WalkedFile, language: str | None) -> list[Signal]:
113
+ # Migrations are scanned/hashed but never become concept evidence.
114
+ if _is_migration_path(file.rel_path):
115
+ return []
116
+
117
+ out: list[Signal] = []
118
+ if file.is_test:
119
+ out += tests.extract_test_signals(file)
120
+ if file.is_doc or language == "markdown":
121
+ out += docs.extract_doc_signals(file)
122
+ if language == "typescript":
123
+ out += typescript.extract_typescript_signals(file)
124
+ out += nextjs.extract_nextjs_signals(file)
125
+ elif language == "python":
126
+ out += python.extract_python_signals(file)
127
+ # Config/manifest extraction by filename, regardless of language.
128
+ out += config_files.extract_config_signals(file)
129
+ return out
130
+
131
+
132
+ def run_scan(
133
+ root: Path | None = None, *, refresh: bool = False, progress: bool = False
134
+ ) -> ScanResult:
135
+ import sys
136
+ import time
137
+
138
+ started = time.perf_counter()
139
+ root = root or paths.repo_root()
140
+ if not paths.is_initialized(root):
141
+ migrations.init_repo(root)
142
+
143
+ cfg = config.load_config(root)
144
+ scanner_cfg = cfg["scanner"]
145
+ max_bytes = int(scanner_cfg["max_file_size_kb"]) * 1024
146
+
147
+ matcher = ignore_mod.build_matcher(
148
+ root,
149
+ respect_gitignore=bool(scanner_cfg["respect_gitignore"]),
150
+ respect_devtimeignore=bool(scanner_cfg["respect_devtimeignore"]),
151
+ )
152
+
153
+ conn = connection.connect(root)
154
+ try:
155
+ repo_id = repository.add_repository_if_missing(conn, root)
156
+ scan_id = f"scan-{uuid.uuid4().hex[:10]}"
157
+ conn.execute(
158
+ "INSERT INTO scans(id, repository_id, started_at, status, devtime_version) "
159
+ "VALUES (?,?,?,?,?)",
160
+ (scan_id, repo_id, _now(), "running", __version__),
161
+ )
162
+ conn.commit()
163
+
164
+ all_signals: list[Signal] = []
165
+ file_count = 0
166
+ walk_stats: dict = {"pruned_dirs": 0, "skipped_files": 0}
167
+
168
+ if progress:
169
+ print("Scanning locally. No code execution. No network.", file=sys.stderr)
170
+
171
+ for wf in walk_repository(
172
+ root, matcher, max_bytes,
173
+ follow_symlinks=bool(scanner_cfg["follow_symlinks"]),
174
+ stats=walk_stats,
175
+ ):
176
+ if progress and file_count and file_count % 1000 == 0:
177
+ print(f"Progress: {file_count} files scanned...", file=sys.stderr)
178
+ language = classify_language(wf.rel_path)
179
+ file_id = f"file-{uuid.uuid4().hex[:10]}"
180
+ conn.execute(
181
+ "INSERT OR REPLACE INTO files"
182
+ "(id, repository_id, path, extension, language, sha256, size_bytes, "
183
+ " is_test, is_doc, ignored, last_seen_scan_id) "
184
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?)",
185
+ (
186
+ file_id,
187
+ repo_id,
188
+ wf.rel_path,
189
+ wf.extension,
190
+ language,
191
+ _hash_file(wf.path),
192
+ wf.size_bytes,
193
+ 1 if wf.is_test else 0,
194
+ 1 if wf.is_doc else 0,
195
+ 0,
196
+ scan_id,
197
+ ),
198
+ )
199
+ file_count += 1
200
+
201
+ for s in _extract(wf, language):
202
+ all_signals.append(s)
203
+ conn.execute(
204
+ "INSERT INTO signals"
205
+ "(id, scan_id, file_id, kind, name, value, start_line, end_line, "
206
+ " confidence, metadata_json) VALUES (?,?,?,?,?,?,?,?,?,?)",
207
+ (
208
+ f"sig-{uuid.uuid4().hex[:10]}",
209
+ scan_id,
210
+ file_id,
211
+ s.kind,
212
+ s.name,
213
+ s.value,
214
+ s.start_line,
215
+ s.end_line,
216
+ s.confidence,
217
+ "{}",
218
+ ),
219
+ )
220
+ conn.commit()
221
+
222
+ # Detect concepts -> build evidence -> generate claims + uncertainty.
223
+ candidates = concepts_mod.detect_concepts(all_signals)
224
+ intelligence: list[claims_mod.ConceptIntelligence] = []
225
+ for cand in candidates:
226
+ ev = evidence_mod.build_evidence(cand)
227
+ ci = claims_mod.build_concept_intelligence(cand, ev)
228
+ intelligence.append(ci)
229
+
230
+ # Persist derived memory (replace machine-derived rows; keep human decisions).
231
+ repository.clear_derived_memory(conn)
232
+ repository.save_intelligence(conn, repo_id, intelligence)
233
+
234
+ conn.execute(
235
+ "UPDATE scans SET finished_at=?, status=?, file_count=?, signal_count=? "
236
+ "WHERE id=?",
237
+ (_now(), "completed", file_count, len(all_signals), scan_id),
238
+ )
239
+ conn.commit()
240
+
241
+ return ScanResult(
242
+ file_count=file_count,
243
+ signal_count=len(all_signals),
244
+ concept_count=len(intelligence),
245
+ intelligence=intelligence,
246
+ duration_seconds=round(time.perf_counter() - started, 2),
247
+ pruned_dirs=walk_stats.get("pruned_dirs", 0),
248
+ skipped_files=walk_stats.get("skipped_files", 0),
249
+ framework_warnings=detect_framework_warnings(all_signals),
250
+ )
251
+ finally:
252
+ conn.close()
@@ -0,0 +1,289 @@
1
+ Metadata-Version: 2.4
2
+ Name: devtime-ei
3
+ Version: 0.1.0
4
+ Summary: Local-first Engineering Intelligence for software repositories
5
+ Author-email: Aviad Shakargi <aviad94@gmail.com>
6
+ Maintainer-email: Aviad Shakargi <aviad94@gmail.com>
7
+ License: Apache-2.0
8
+ Project-URL: Homepage, https://github.com/Shakargy/devtime
9
+ Project-URL: Repository, https://github.com/Shakargy/devtime
10
+ Project-URL: Issues, https://github.com/Shakargy/devtime/issues
11
+ Project-URL: Release Notes, https://github.com/Shakargy/devtime/releases/tag/v0.1.0
12
+ Project-URL: Demo, https://youtu.be/1Hiu3Y9J_SI
13
+ Keywords: devtools,cli,static-analysis,repository-analysis,engineering-intelligence,local-first
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: Apache Software License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.11
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: typer>=0.12
28
+ Requires-Dist: rich>=13.7
29
+ Requires-Dist: pydantic>=2.7
30
+ Requires-Dist: sqlalchemy>=2.0
31
+ Requires-Dist: pyyaml>=6.0
32
+ Requires-Dist: pathspec>=0.12
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest>=8.0; extra == "dev"
35
+ Dynamic: license-file
36
+
37
+ # DevTime
38
+
39
+ **Local-first Engineering Intelligence for software repositories.**
40
+
41
+ DevTime helps a codebase explain itself from evidence.
42
+
43
+ It scans code, tests, configs, routes, and decisions to identify supported software
44
+ concepts, link claims to files, surface uncertainty, and warn about a narrow set of
45
+ risky changes.
46
+
47
+ > No cloud. No telemetry. No code execution. No AI required.
48
+
49
+ [![DevTime demo - Repository memory from evidence](assets/devtime-demo-thumbnail-v0.1.0.png)](https://youtu.be/1Hiu3Y9J_SI)
50
+
51
+ Watch the 2-minute demo: DevTime scans a repo locally, explains concepts from
52
+ evidence, surfaces uncertainty, catches a risky diff, and shows how a corroborated
53
+ decision improves understanding.
54
+
55
+ ---
56
+
57
+ ## Try DevTime in 60 seconds
58
+
59
+ ```bash
60
+ git clone https://github.com/Shakargy/devtime.git
61
+ cd devtime
62
+ python -m venv .venv
63
+ source .venv/bin/activate
64
+ pip install -e ".[dev]"
65
+ cd examples/demo-saas
66
+ dtc init
67
+ dtc scan
68
+ dtc concepts
69
+ dtc explain "Billing Webhooks"
70
+ ```
71
+
72
+ On Windows PowerShell:
73
+
74
+ ```powershell
75
+ git clone https://github.com/Shakargy/devtime.git
76
+ cd devtime
77
+ python -m venv .venv
78
+ .venv\Scripts\Activate.ps1
79
+ pip install -e ".[dev]"
80
+ cd examples/demo-saas
81
+ dtc init
82
+ dtc scan
83
+ dtc concepts
84
+ dtc explain "Billing Webhooks"
85
+ ```
86
+
87
+ You should see Billing Webhooks explained from evidence, including supported claims,
88
+ file references, uncertainty, Understanding Score, and Understanding Debt.
89
+
90
+ To test risk review, make a local change first, then run:
91
+
92
+ ```bash
93
+ dtc risk --diff
94
+ ```
95
+
96
+ A full, copy-pasteable walkthrough (including the risk-diff and corroborated-decision
97
+ steps) is in **[DEMO_SCRIPT.md](DEMO_SCRIPT.md)**.
98
+
99
+ ## Why this exists
100
+
101
+ Git remembers code. It does not remember understanding. It does not tell you why a
102
+ behavior exists, what evidence supports it, or what nobody has decided yet. As AI
103
+ tools generate code faster than teams can review it, that missing understanding
104
+ becomes the bottleneck.
105
+
106
+ DevTime builds evidence-backed repository memory: a local layer that says what a
107
+ repository can prove, and - just as importantly - what it cannot prove yet.
108
+
109
+ ## Who it is for
110
+
111
+ DevTime is for developers reviewing unfamiliar code, teams using AI coding tools, and
112
+ maintainers who want repository understanding to be backed by evidence instead of
113
+ generated summaries.
114
+
115
+ It is useful when you want to ask:
116
+
117
+ - where is authentication actually implemented?
118
+ - what files prove that billing webhooks exist?
119
+ - what is still uncertain?
120
+ - did this diff touch a risky concept?
121
+ - is there a decision explaining this behavior?
122
+
123
+ ## What DevTime does
124
+
125
+ - Detects concepts from routes, tests, configs, dependencies, and docs.
126
+ - Explains from evidence by linking claims to files and signals.
127
+ - Surfaces uncertainty when evidence is missing or weak.
128
+ - Scores understanding with an Understanding Score and Understanding Debt label.
129
+ - Reviews narrow risky diffs with advisory findings from local memory.
130
+ - Records decisions locally so rationale can reduce uncertainty when corroborated by code.
131
+
132
+ ## Supported concepts
133
+
134
+ V0 detects six supported concept families. It does not discover arbitrary domain
135
+ concepts yet:
136
+
137
+ - Authentication
138
+ - Billing Webhooks
139
+ - Background Jobs
140
+ - Data Export
141
+ - Admin Permissions
142
+ - File Uploads
143
+
144
+ Anything outside these six is out of scope for V0. See [LIMITATIONS.md](LIMITATIONS.md).
145
+
146
+ ## What DevTime does not do
147
+
148
+ - It does not execute your code.
149
+ - It does not send code or data over the network.
150
+ - It does not require or call an AI model.
151
+ - It does not guarantee correctness or safe changes.
152
+ - It does not replace code review or architecture decisions.
153
+ - It is **not** a documentation generator, a static analyzer, an observability tool,
154
+ a productivity tracker, or an AI coding agent.
155
+
156
+ ## Trust model
157
+
158
+ - DevTime stores local repository memory in `.devtime/` (a local SQLite database).
159
+ - **No network access** during a scan.
160
+ - **No code execution** during a scan.
161
+ - Ignored directories are pruned *before* scanning; ignored files and secrets must
162
+ never become evidence or claims.
163
+ - Every claim must link to evidence - *no claim without evidence*.
164
+ - Weak evidence produces **uncertainty**, not confidence.
165
+ - *Usage is not decision*: that a dependency is used does not mean someone decided why.
166
+ - Risk review is **advisory** by default - it does not block PRs.
167
+
168
+ ## Commands
169
+
170
+ | Command | Purpose |
171
+ |---------|---------|
172
+ | `dtc init` | Create local `.devtime` memory. |
173
+ | `dtc scan` | Scan the current repository and extract evidence-backed signals. |
174
+ | `dtc concepts` | List detected concepts with confidence and Understanding Debt. |
175
+ | `dtc explain <concept>` | Explain a concept: claims, evidence, confidence, uncertainty, Understanding Debt. |
176
+ | `dtc context <concept>` | Create a governed Context Pack for agents or humans. |
177
+ | `dtc risk --diff` | Review a git diff for risky changes using local evidence (advisory). |
178
+ | `dtc decision add` | Add a local decision record that can reduce uncertainty. |
179
+
180
+ (Also available: `dtc evidence`, `dtc debt`, `dtc status`, `dtc doctor --privacy`,
181
+ `dtc export`, `dtc reset`.)
182
+
183
+ Requires **Python >= 3.11** and git. See **[QUICKSTART.md](QUICKSTART.md)** for a
184
+ step-by-step first run and troubleshooting.
185
+
186
+ ## Installation
187
+
188
+ Recommended source install for now:
189
+
190
+ ```bash
191
+ git clone https://github.com/Shakargy/devtime.git
192
+ cd devtime
193
+ python -m venv .venv
194
+ source .venv/bin/activate
195
+ pip install -e ".[dev]"
196
+ ```
197
+
198
+ Windows PowerShell:
199
+
200
+ ```powershell
201
+ git clone https://github.com/Shakargy/devtime.git
202
+ cd devtime
203
+ python -m venv .venv
204
+ .venv\Scripts\Activate.ps1
205
+ pip install -e ".[dev]"
206
+ ```
207
+
208
+ Planned PyPI install (after the package is published and verified):
209
+
210
+ ```bash
211
+ pipx install devtime
212
+ ```
213
+
214
+ ## Example output
215
+
216
+ ```
217
+ $ dtc explain "Billing Webhooks"
218
+ Concept: Billing Webhooks
219
+
220
+ Supported claims:
221
+ - Billing Webhooks is present and supported by behavior evidence.
222
+ type: concept confidence: 0.86 evidence: src/billing/stripe-webhook.ts, tests/stripe-signature.test.ts
223
+ - Billing Webhooks has active route handling.
224
+ type: behavior confidence: 0.82 evidence: src/billing/stripe-webhook.ts
225
+ - Billing Webhooks verifies webhook signatures.
226
+ type: behavior confidence: 0.85 evidence: src/billing/stripe-webhook.ts
227
+
228
+ Uncertainty:
229
+ - No decision was found explaining key choices for Billing Webhooks.
230
+
231
+ Understanding Score: 58 / 100
232
+ Understanding Debt: medium
233
+ causes:
234
+ - missing or uncorroborated decision evidence
235
+ - no confirmed owner
236
+ ```
237
+
238
+ > Understanding Score is higher = better understanding; Understanding Debt is a
239
+ > label (low/medium/high), not the same number.
240
+
241
+ ## Proof
242
+
243
+ DevTime runs on `examples/demo-saas` and on real repositories. During Reality
244
+ Validation it detected - and then learned from - real failures (Next.js App Router
245
+ blindness, a false Billing Webhooks detection on a generic webhook system, a DB
246
+ migration mis-counted as Background Jobs evidence, and more). Each failure became a
247
+ fixture so it cannot silently regress.
248
+
249
+ - Tests grew from 13 to 88 as real failures became fixtures.
250
+ - Scan time on a 355-file real repo dropped from ~27.3s to ~0.48s after ignored-
251
+ directory pruning.
252
+
253
+ Full evidence, before/after examples, and the validation reports are in
254
+ **[PROOF.md](PROOF.md)** and `reports/reality-validation/`.
255
+
256
+ ## Privacy and safety
257
+
258
+ - Runs entirely locally; nothing leaves your machine during a scan.
259
+ - No code execution and no network calls during scanning.
260
+ - Secrets and ignored files are excluded from evidence by design (`dtc doctor
261
+ --privacy` reports the boundaries).
262
+ - `dtc reset` deletes local memory; your source code is never modified.
263
+
264
+ ## Known limitations
265
+
266
+ DevTime is a **heuristic scanner**, not a full compiler or semantic analyzer. It is
267
+ currently strongest on TypeScript / Next.js / Express / FastAPI-style repositories
268
+ that resemble its fixtures. False positives and false negatives are possible.
269
+ Understanding Debt is a product signal, not an objective universal truth.
270
+
271
+ Read the full list - including framework coverage, risk-review scope, and what is
272
+ intentionally not built yet - in **[LIMITATIONS.md](LIMITATIONS.md)**.
273
+
274
+ ## Roadmap
275
+
276
+ This is an early, local-first V0 focused on being trustworthy before being large.
277
+ Not yet built (intentionally): git-history signals, wired MCP transport, an AI
278
+ provider, a UI, and any cloud/team/enterprise features. See **[ROADMAP.md](ROADMAP.md)**.
279
+
280
+ ## Contributing
281
+
282
+ The most valuable contribution is a **fixture**: a small repository pattern plus the
283
+ expected concepts, allowed claims, forbidden claims, and required uncertainty. If
284
+ DevTime gets something wrong on your code, that wrong output can become a fixture so
285
+ it never regresses. See **[CONTRIBUTING.md](CONTRIBUTING.md)**.
286
+
287
+ ## License
288
+
289
+ Licensed under the **Apache License 2.0**. See [LICENSE](LICENSE).