devtime-ei 0.1.0__tar.gz → 0.1.1__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 (75) hide show
  1. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/PKG-INFO +60 -24
  2. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/README.md +58 -22
  3. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/pyproject.toml +10 -3
  4. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/__init__.py +1 -1
  5. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/cli.py +35 -0
  6. devtime_ei-0.1.1/src/devtime/demo.py +75 -0
  7. devtime_ei-0.1.1/src/devtime/resources/demo-saas/.devtimeignore +23 -0
  8. devtime_ei-0.1.1/src/devtime/resources/demo-saas/README.md +5 -0
  9. devtime_ei-0.1.1/src/devtime/resources/demo-saas/docs/decisions/0001-use-jwt.md +15 -0
  10. devtime_ei-0.1.1/src/devtime/resources/demo-saas/package.json +15 -0
  11. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/admin/permissions.ts +9 -0
  12. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/auth/login.ts +21 -0
  13. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/auth/middleware.ts +16 -0
  14. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/auth/tokens.ts +11 -0
  15. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/billing/stripe-webhook.ts +25 -0
  16. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/billing/subscription-service.ts +11 -0
  17. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/export/export-csv.ts +14 -0
  18. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/jobs/email-worker.ts +16 -0
  19. devtime_ei-0.1.1/src/devtime/resources/demo-saas/src/jobs/queues.ts +6 -0
  20. devtime_ei-0.1.1/src/devtime/resources/demo-saas/tests/auth-login.test.ts +10 -0
  21. devtime_ei-0.1.1/src/devtime/resources/demo-saas/tests/stripe-signature.test.ts +8 -0
  22. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime_ei.egg-info/PKG-INFO +60 -24
  23. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime_ei.egg-info/SOURCES.txt +16 -0
  24. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/LICENSE +0 -0
  25. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/setup.cfg +0 -0
  26. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/ai/__init__.py +0 -0
  27. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/ai/local.py +0 -0
  28. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/ai/prompts.py +0 -0
  29. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/ai/providers.py +0 -0
  30. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/assets/devtimeignore.starter +0 -0
  31. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/config.py +0 -0
  32. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/db/__init__.py +0 -0
  33. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/db/connection.py +0 -0
  34. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/db/migrations.py +0 -0
  35. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/db/repository.py +0 -0
  36. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/db/schema.sql +0 -0
  37. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/fixtures/__init__.py +0 -0
  38. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/fixtures/assertions.py +0 -0
  39. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/fixtures/loader.py +0 -0
  40. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/fixtures/runner.py +0 -0
  41. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/__init__.py +0 -0
  42. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/claims.py +0 -0
  43. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/concepts.py +0 -0
  44. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/context_pack.py +0 -0
  45. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/evidence.py +0 -0
  46. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/lineage.py +0 -0
  47. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/risk.py +0 -0
  48. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/intelligence/scoring.py +0 -0
  49. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/mcp/__init__.py +0 -0
  50. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/mcp/schemas.py +0 -0
  51. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/mcp/server.py +0 -0
  52. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/mcp/tools.py +0 -0
  53. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/output/__init__.py +0 -0
  54. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/output/json_export.py +0 -0
  55. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/output/markdown.py +0 -0
  56. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/output/terminal.py +0 -0
  57. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/paths.py +0 -0
  58. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/privacy.py +0 -0
  59. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/__init__.py +0 -0
  60. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/__init__.py +0 -0
  61. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/base.py +0 -0
  62. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/config_files.py +0 -0
  63. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/docs.py +0 -0
  64. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/nextjs.py +0 -0
  65. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/python.py +0 -0
  66. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/tests.py +0 -0
  67. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/extractors/typescript.py +0 -0
  68. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/file_walker.py +0 -0
  69. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/ignore.py +0 -0
  70. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/language.py +0 -0
  71. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime/scanner/signals.py +0 -0
  72. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime_ei.egg-info/dependency_links.txt +0 -0
  73. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime_ei.egg-info/entry_points.txt +0 -0
  74. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime_ei.egg-info/requires.txt +0 -0
  75. {devtime_ei-0.1.0 → devtime_ei-0.1.1}/src/devtime_ei.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devtime-ei
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Local-first Engineering Intelligence for software repositories
5
5
  Author-email: Aviad Shakargi <aviad94@gmail.com>
6
6
  Maintainer-email: Aviad Shakargi <aviad94@gmail.com>
@@ -8,7 +8,7 @@ License: Apache-2.0
8
8
  Project-URL: Homepage, https://github.com/Shakargy/devtime
9
9
  Project-URL: Repository, https://github.com/Shakargy/devtime
10
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
11
+ Project-URL: Release Notes, https://github.com/Shakargy/devtime/releases/tag/v0.1.1
12
12
  Project-URL: Demo, https://youtu.be/1Hiu3Y9J_SI
13
13
  Keywords: devtools,cli,static-analysis,repository-analysis,engineering-intelligence,local-first
14
14
  Classifier: Development Status :: 3 - Alpha
@@ -56,6 +56,22 @@ decision improves understanding.
56
56
 
57
57
  ## Try DevTime in 60 seconds
58
58
 
59
+ ```bash
60
+ pipx install devtime-ei
61
+ dtc demo init
62
+ cd devtime-demo-saas
63
+ dtc init
64
+ dtc scan
65
+ dtc concepts
66
+ dtc explain "Billing Webhooks"
67
+ ```
68
+
69
+ The PyPI distribution is `devtime-ei`. The Python package remains `devtime`, and the
70
+ CLI command remains `dtc`. `dtc demo init` copies a small static example repo into
71
+ `./devtime-demo-saas` so you can try DevTime without cloning this repository.
72
+
73
+ ### From source
74
+
59
75
  ```bash
60
76
  git clone https://github.com/Shakargy/devtime.git
61
77
  cd devtime
@@ -98,27 +114,36 @@ steps) is in **[DEMO_SCRIPT.md](DEMO_SCRIPT.md)**.
98
114
 
99
115
  ## Why this exists
100
116
 
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.
117
+ Git records what changed, but it does not preserve the reasoning behind those
118
+ changes. When you return to a repository - or review one you did not write - you often
119
+ have to reconstruct why a behavior exists, what evidence supports it, and what is
120
+ still uncertain.
105
121
 
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.
122
+ DevTime builds evidence-backed repository memory: a local layer that helps a
123
+ codebase explain itself from code, tests, configs, routes, and recorded decisions.
124
+ It shows what the repository can support with evidence - and, just as importantly,
125
+ what it cannot support yet.
108
126
 
109
127
  ## Who it is for
110
128
 
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.
129
+ DevTime is for people who need to understand a repository from evidence rather than
130
+ memory.
114
131
 
115
- It is useful when you want to ask:
132
+ It is especially useful if you:
116
133
 
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?
134
+ - are onboarding to an unfamiliar codebase and need to understand how a feature is implemented;
135
+ - are reviewing a pull request and want to see what evidence supports a behavior;
136
+ - are returning to a project after weeks or months and cannot remember why something exists;
137
+ - maintain a long-lived project where design decisions are easily lost;
138
+ - want repository understanding to be backed by code and recorded decisions instead of generated summaries.
139
+
140
+ Questions DevTime helps answer include:
141
+
142
+ - Where is authentication actually implemented?
143
+ - What files prove that Billing Webhooks exist?
144
+ - What is still uncertain?
145
+ - Did this diff touch a risky concept?
146
+ - Is there a decision explaining this behavior?
122
147
 
123
148
  ## What DevTime does
124
149
 
@@ -185,7 +210,24 @@ step-by-step first run and troubleshooting.
185
210
 
186
211
  ## Installation
187
212
 
188
- Recommended source install for now:
213
+ Recommended: install from PyPI with [pipx](https://pipx.pypa.io/) so the `dtc`
214
+ command is available on your PATH in an isolated environment:
215
+
216
+ ```bash
217
+ pipx install devtime-ei
218
+ ```
219
+
220
+ Or with pip:
221
+
222
+ ```bash
223
+ pip install devtime-ei
224
+ ```
225
+
226
+ The PyPI distribution is `devtime-ei`. The Python package remains `devtime`, and the
227
+ CLI command remains `dtc`. After installing, run `dtc demo init` to create a local
228
+ example repo to try it on.
229
+
230
+ ### From source
189
231
 
190
232
  ```bash
191
233
  git clone https://github.com/Shakargy/devtime.git
@@ -205,12 +247,6 @@ python -m venv .venv
205
247
  pip install -e ".[dev]"
206
248
  ```
207
249
 
208
- Planned PyPI install (after the package is published and verified):
209
-
210
- ```bash
211
- pipx install devtime
212
- ```
213
-
214
250
  ## Example output
215
251
 
216
252
  ```
@@ -20,6 +20,22 @@ decision improves understanding.
20
20
 
21
21
  ## Try DevTime in 60 seconds
22
22
 
23
+ ```bash
24
+ pipx install devtime-ei
25
+ dtc demo init
26
+ cd devtime-demo-saas
27
+ dtc init
28
+ dtc scan
29
+ dtc concepts
30
+ dtc explain "Billing Webhooks"
31
+ ```
32
+
33
+ The PyPI distribution is `devtime-ei`. The Python package remains `devtime`, and the
34
+ CLI command remains `dtc`. `dtc demo init` copies a small static example repo into
35
+ `./devtime-demo-saas` so you can try DevTime without cloning this repository.
36
+
37
+ ### From source
38
+
23
39
  ```bash
24
40
  git clone https://github.com/Shakargy/devtime.git
25
41
  cd devtime
@@ -62,27 +78,36 @@ steps) is in **[DEMO_SCRIPT.md](DEMO_SCRIPT.md)**.
62
78
 
63
79
  ## Why this exists
64
80
 
65
- Git remembers code. It does not remember understanding. It does not tell you why a
66
- behavior exists, what evidence supports it, or what nobody has decided yet. As AI
67
- tools generate code faster than teams can review it, that missing understanding
68
- becomes the bottleneck.
81
+ Git records what changed, but it does not preserve the reasoning behind those
82
+ changes. When you return to a repository - or review one you did not write - you often
83
+ have to reconstruct why a behavior exists, what evidence supports it, and what is
84
+ still uncertain.
69
85
 
70
- DevTime builds evidence-backed repository memory: a local layer that says what a
71
- repository can prove, and - just as importantly - what it cannot prove yet.
86
+ DevTime builds evidence-backed repository memory: a local layer that helps a
87
+ codebase explain itself from code, tests, configs, routes, and recorded decisions.
88
+ It shows what the repository can support with evidence - and, just as importantly,
89
+ what it cannot support yet.
72
90
 
73
91
  ## Who it is for
74
92
 
75
- DevTime is for developers reviewing unfamiliar code, teams using AI coding tools, and
76
- maintainers who want repository understanding to be backed by evidence instead of
77
- generated summaries.
93
+ DevTime is for people who need to understand a repository from evidence rather than
94
+ memory.
78
95
 
79
- It is useful when you want to ask:
96
+ It is especially useful if you:
80
97
 
81
- - where is authentication actually implemented?
82
- - what files prove that billing webhooks exist?
83
- - what is still uncertain?
84
- - did this diff touch a risky concept?
85
- - is there a decision explaining this behavior?
98
+ - are onboarding to an unfamiliar codebase and need to understand how a feature is implemented;
99
+ - are reviewing a pull request and want to see what evidence supports a behavior;
100
+ - are returning to a project after weeks or months and cannot remember why something exists;
101
+ - maintain a long-lived project where design decisions are easily lost;
102
+ - want repository understanding to be backed by code and recorded decisions instead of generated summaries.
103
+
104
+ Questions DevTime helps answer include:
105
+
106
+ - Where is authentication actually implemented?
107
+ - What files prove that Billing Webhooks exist?
108
+ - What is still uncertain?
109
+ - Did this diff touch a risky concept?
110
+ - Is there a decision explaining this behavior?
86
111
 
87
112
  ## What DevTime does
88
113
 
@@ -149,7 +174,24 @@ step-by-step first run and troubleshooting.
149
174
 
150
175
  ## Installation
151
176
 
152
- Recommended source install for now:
177
+ Recommended: install from PyPI with [pipx](https://pipx.pypa.io/) so the `dtc`
178
+ command is available on your PATH in an isolated environment:
179
+
180
+ ```bash
181
+ pipx install devtime-ei
182
+ ```
183
+
184
+ Or with pip:
185
+
186
+ ```bash
187
+ pip install devtime-ei
188
+ ```
189
+
190
+ The PyPI distribution is `devtime-ei`. The Python package remains `devtime`, and the
191
+ CLI command remains `dtc`. After installing, run `dtc demo init` to create a local
192
+ example repo to try it on.
193
+
194
+ ### From source
153
195
 
154
196
  ```bash
155
197
  git clone https://github.com/Shakargy/devtime.git
@@ -169,12 +211,6 @@ python -m venv .venv
169
211
  pip install -e ".[dev]"
170
212
  ```
171
213
 
172
- Planned PyPI install (after the package is published and verified):
173
-
174
- ```bash
175
- pipx install devtime
176
- ```
177
-
178
214
  ## Example output
179
215
 
180
216
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devtime-ei"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "Local-first Engineering Intelligence for software repositories"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -45,7 +45,7 @@ dev = [
45
45
  Homepage = "https://github.com/Shakargy/devtime"
46
46
  Repository = "https://github.com/Shakargy/devtime"
47
47
  Issues = "https://github.com/Shakargy/devtime/issues"
48
- "Release Notes" = "https://github.com/Shakargy/devtime/releases/tag/v0.1.0"
48
+ "Release Notes" = "https://github.com/Shakargy/devtime/releases/tag/v0.1.1"
49
49
  Demo = "https://youtu.be/1Hiu3Y9J_SI"
50
50
 
51
51
  [project.scripts]
@@ -59,8 +59,15 @@ build-backend = "setuptools.build_meta"
59
59
  where = ["src"]
60
60
 
61
61
  # Non-Python runtime files that ship inside the package (read at runtime).
62
+ # The demo-saas resource is copied out by `dtc demo init`; the leading-dot
63
+ # .devtimeignore is listed explicitly because glob `*` does not match dotfiles.
62
64
  [tool.setuptools.package-data]
63
- devtime = ["db/*.sql", "assets/*"]
65
+ devtime = [
66
+ "db/*.sql",
67
+ "assets/*",
68
+ "resources/demo-saas/**/*",
69
+ "resources/demo-saas/.devtimeignore",
70
+ ]
64
71
 
65
72
  [tool.pytest.ini_options]
66
73
  testpaths = ["tests"]
@@ -1,6 +1,6 @@
1
1
  """DevTime - local-first Engineering Intelligence for repository memory."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.1"
4
4
 
5
5
  # Version metadata (Builder Edition, Chapter 20).
6
6
  EVIDENCE_MODEL = "2026.06.1"
@@ -24,9 +24,13 @@ console = Console()
24
24
  claim_app = typer.Typer(help="Inspect and govern claims.")
25
25
  decision_app = typer.Typer(help="Record human decisions.")
26
26
  mcp_app = typer.Typer(help="Local read-only MCP server.")
27
+ demo_app = typer.Typer(
28
+ help="Create a local copy of the bundled demo repository.", no_args_is_help=True
29
+ )
27
30
  app.add_typer(claim_app, name="claim")
28
31
  app.add_typer(decision_app, name="decision")
29
32
  app.add_typer(mcp_app, name="mcp")
33
+ app.add_typer(demo_app, name="demo")
30
34
 
31
35
 
32
36
  # --------------------------------------------------------------------------- #
@@ -43,6 +47,37 @@ def init() -> None:
43
47
  console.print("AI disabled. Cloud disabled. Telemetry off. MCP read-only.")
44
48
 
45
49
 
50
+ @demo_app.command("init")
51
+ def demo_init(
52
+ force: bool = typer.Option(
53
+ False, "--force", help="Replace devtime-demo-saas if it already exists."
54
+ ),
55
+ ) -> None:
56
+ """Copy the bundled demo repository into ./devtime-demo-saas (static files only)."""
57
+ from devtime.demo import DEMO_DIR_NAME, DemoExistsError, create_demo
58
+
59
+ try:
60
+ create_demo(Path.cwd(), force=force)
61
+ except DemoExistsError as exc:
62
+ console.print(
63
+ f"[yellow]{DEMO_DIR_NAME}/ already exists[/yellow] at {exc.path}."
64
+ )
65
+ console.print(
66
+ "Use [bold]dtc demo init --force[/bold] to replace it, "
67
+ "or remove the directory first."
68
+ )
69
+ raise typer.Exit(code=1)
70
+
71
+ console.print(f"[green]Demo repository created[/green] at ./{DEMO_DIR_NAME}")
72
+ console.print("")
73
+ console.print("Next:")
74
+ console.print(f" cd {DEMO_DIR_NAME}")
75
+ console.print(" dtc init")
76
+ console.print(" dtc scan")
77
+ console.print(" dtc concepts")
78
+ console.print(' dtc explain "Billing Webhooks"')
79
+
80
+
46
81
  @app.command()
47
82
  def status() -> None:
48
83
  """Show local storage, AI, cloud, telemetry, MCP, and scan status."""
@@ -0,0 +1,75 @@
1
+ """Bundled demo repository support for `dtc demo init`.
2
+
3
+ This lets people who installed DevTime from PyPI (`pipx install devtime-ei`) try it
4
+ without cloning the source repository. It copies a small, static example repo that
5
+ ships inside the installed package into the current working directory.
6
+
7
+ It only copies static files. It never executes code, never installs anything, never
8
+ runs tests or migrations, and never touches the network. Nothing leaves the machine.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import shutil
14
+ from importlib import resources
15
+ from pathlib import Path
16
+
17
+ # Directory name created in the current working directory.
18
+ DEMO_DIR_NAME = "devtime-demo-saas"
19
+
20
+ # The packaged demo lives at src/devtime/resources/demo-saas/ and ships in the wheel
21
+ # via [tool.setuptools.package-data].
22
+ _RESOURCE_SUBPATH = ("resources", "demo-saas")
23
+
24
+ # Artifacts that must never end up in a copied demo, stripped defensively after copy.
25
+ _FORBIDDEN_DIRS = {".devtime", ".git", "node_modules", "__pycache__", ".cache"}
26
+ _FORBIDDEN_FILE_SUFFIXES = (".sqlite", ".sqlite3", ".db")
27
+
28
+
29
+ class DemoExistsError(Exception):
30
+ """Raised when the demo directory already exists and force was not requested."""
31
+
32
+ def __init__(self, path: Path) -> None:
33
+ self.path = path
34
+ super().__init__(str(path))
35
+
36
+
37
+ def packaged_demo_source() -> Path:
38
+ """Return a filesystem path to the demo directory bundled in the package."""
39
+ root = resources.files("devtime")
40
+ source = root.joinpath(*_RESOURCE_SUBPATH)
41
+ # In a normal wheel/editable install this is already a real path on disk.
42
+ return Path(str(source))
43
+
44
+
45
+ def _strip_forbidden(dest: Path) -> None:
46
+ """Remove any artifacts that must never be part of a shared demo."""
47
+ for path in sorted(dest.rglob("*"), reverse=True):
48
+ if path.is_dir() and path.name in _FORBIDDEN_DIRS:
49
+ shutil.rmtree(path, ignore_errors=True)
50
+ elif path.is_file() and path.suffix.lower() in _FORBIDDEN_FILE_SUFFIXES:
51
+ path.unlink(missing_ok=True)
52
+
53
+
54
+ def create_demo(dest_parent: Path, *, force: bool = False) -> Path:
55
+ """Copy the bundled demo into ``dest_parent/devtime-demo-saas``.
56
+
57
+ Writes only inside ``dest_parent``. Returns the created directory path.
58
+ Raises :class:`DemoExistsError` if the destination exists and ``force`` is False.
59
+ """
60
+ dest = dest_parent / DEMO_DIR_NAME
61
+ if dest.exists():
62
+ if not force:
63
+ raise DemoExistsError(dest)
64
+ shutil.rmtree(dest)
65
+
66
+ source = packaged_demo_source()
67
+ if not source.is_dir():
68
+ raise FileNotFoundError(f"Bundled demo not found at {source}")
69
+
70
+ # as_file guarantees a real filesystem path even if the package were zipped.
71
+ with resources.as_file(resources.files("devtime").joinpath(*_RESOURCE_SUBPATH)) as src_path:
72
+ shutil.copytree(src_path, dest)
73
+
74
+ _strip_forbidden(dest)
75
+ return dest
@@ -0,0 +1,23 @@
1
+ # Secrets
2
+ .env
3
+ .env.*
4
+ *.pem
5
+ *.key
6
+ *.p12
7
+ *.pfx
8
+ id_rsa
9
+ id_ed25519
10
+ secrets.*
11
+ credentials.*
12
+ service-account*.json
13
+
14
+ # Generated
15
+ node_modules/
16
+ dist/
17
+ build/
18
+ coverage/
19
+ .next/
20
+ .cache/
21
+
22
+ # VCS
23
+ .git/
@@ -0,0 +1,5 @@
1
+ # Demo SaaS
2
+
3
+ A small SaaS app used as a DevTime demo and test target. It includes
4
+ Authentication, Billing Webhooks, Background Jobs, Admin Permissions, and
5
+ Data Export - with one intentionally missing duplicate-delivery test.
@@ -0,0 +1,15 @@
1
+ # 0001 - Use JWT for authentication
2
+
3
+ ## Status
4
+ Accepted
5
+
6
+ ## Context
7
+ We needed stateless authentication that works across multiple API instances
8
+ without shared session storage.
9
+
10
+ ## Decision
11
+ Use JWT access tokens signed with a server secret, with a 1 hour expiry.
12
+
13
+ ## Consequences
14
+ - No server-side session store required.
15
+ - Token revocation before expiry is not yet solved.
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "demo-saas",
3
+ "version": "1.0.0",
4
+ "description": "Demo SaaS app for DevTime (Builder Edition, Appendix E).",
5
+ "dependencies": {
6
+ "express": "^4.19.0",
7
+ "stripe": "^15.0.0",
8
+ "jsonwebtoken": "^9.0.0",
9
+ "bullmq": "^5.0.0",
10
+ "ioredis": "^5.4.0"
11
+ },
12
+ "devDependencies": {
13
+ "jest": "^29.7.0"
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ export function requireAdmin(req: Request, res: Response, next: NextFunction) {
4
+ const role = (req as any).role;
5
+ if (role !== "admin") {
6
+ return res.status(403).json({ error: "forbidden" });
7
+ }
8
+ return next();
9
+ }
@@ -0,0 +1,21 @@
1
+ import { Router } from "express";
2
+ import jwt from "jsonwebtoken";
3
+
4
+ export const authRouter = Router();
5
+
6
+ authRouter.post("/auth/login", async (req, res) => {
7
+ const { email, password } = req.body;
8
+ const user = await findUser(email, password);
9
+ if (!user) {
10
+ return res.status(401).json({ error: "invalid_credentials" });
11
+ }
12
+ const token = jwt.sign({ sub: user.id }, process.env.JWT_SECRET as string, {
13
+ expiresIn: "1h",
14
+ });
15
+ return res.json({ token });
16
+ });
17
+
18
+ async function findUser(email: string, password: string) {
19
+ // Lookup omitted for the demo.
20
+ return { id: "u_1", email };
21
+ }
@@ -0,0 +1,16 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { verifyAccessToken } from "./tokens";
3
+
4
+ export function requireAuth(req: Request, res: Response, next: NextFunction) {
5
+ const header = req.headers.authorization;
6
+ if (!header) {
7
+ return res.status(401).json({ error: "missing_token" });
8
+ }
9
+ try {
10
+ const payload = verifyAccessToken(header.replace("Bearer ", ""));
11
+ (req as any).userId = payload.sub;
12
+ return next();
13
+ } catch {
14
+ return res.status(401).json({ error: "invalid_token" });
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ import jwt from "jsonwebtoken";
2
+
3
+ export function issueAccessToken(userId: string): string {
4
+ return jwt.sign({ sub: userId }, process.env.JWT_SECRET as string, {
5
+ expiresIn: "1h",
6
+ });
7
+ }
8
+
9
+ export function verifyAccessToken(token: string): { sub: string } {
10
+ return jwt.verify(token, process.env.JWT_SECRET as string) as { sub: string };
11
+ }
@@ -0,0 +1,25 @@
1
+ import { Router } from "express";
2
+ import Stripe from "stripe";
3
+ import { updateSubscriptionState } from "./subscription-service";
4
+
5
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string);
6
+ export const billingRouter = Router();
7
+
8
+ billingRouter.post("/api/stripe/webhook", async (req, res) => {
9
+ const sig = req.headers["stripe-signature"] as string;
10
+ let event;
11
+ try {
12
+ event = stripe.webhooks.constructEvent(
13
+ (req as any).rawBody,
14
+ sig,
15
+ process.env.STRIPE_WEBHOOK_SECRET as string
16
+ );
17
+ } catch (err) {
18
+ return res.status(400).send("invalid_signature");
19
+ }
20
+
21
+ if (event.type === "customer.subscription.updated") {
22
+ await updateSubscriptionState(event.data.object);
23
+ }
24
+ return res.json({ received: true });
25
+ });
@@ -0,0 +1,11 @@
1
+ export async function updateSubscriptionState(subscription: any): Promise<void> {
2
+ // Maps a Stripe subscription onto local subscription state.
3
+ const status = subscription.status;
4
+ const customerId = subscription.customer;
5
+ await saveSubscription(customerId, status);
6
+ }
7
+
8
+ async function saveSubscription(customerId: string, status: string) {
9
+ // Persistence omitted for the demo.
10
+ return { customerId, status };
11
+ }
@@ -0,0 +1,14 @@
1
+ import { Router } from "express";
2
+
3
+ export const exportRouter = Router();
4
+
5
+ exportRouter.get("/api/export/users.csv", async (req, res) => {
6
+ const rows = await loadUsers();
7
+ const csv = rows.map((r) => `${r.id},${r.email}`).join("\n");
8
+ res.setHeader("Content-Type", "text/csv");
9
+ return res.send(csv);
10
+ });
11
+
12
+ async function loadUsers() {
13
+ return [{ id: "u_1", email: "demo@example.com" }];
14
+ }
@@ -0,0 +1,16 @@
1
+ import { Worker } from "bullmq";
2
+ import IORedis from "ioredis";
3
+
4
+ const connection = new IORedis(process.env.REDIS_URL as string);
5
+
6
+ export const emailWorker = new Worker(
7
+ "emails",
8
+ async (job) => {
9
+ await sendEmail(job.data.to, job.data.template);
10
+ },
11
+ { connection }
12
+ );
13
+
14
+ async function sendEmail(to: string, template: string) {
15
+ return { to, template };
16
+ }
@@ -0,0 +1,6 @@
1
+ import { Queue } from "bullmq";
2
+ import IORedis from "ioredis";
3
+
4
+ const connection = new IORedis(process.env.REDIS_URL as string);
5
+
6
+ export const emailQueue = new Queue("emails", { connection });
@@ -0,0 +1,10 @@
1
+ import { issueAccessToken, verifyAccessToken } from "../src/auth/tokens";
2
+
3
+ describe("auth login", () => {
4
+ it("issues and verifies a jwt access token", () => {
5
+ process.env.JWT_SECRET = "test-secret";
6
+ const token = issueAccessToken("u_1");
7
+ const payload = verifyAccessToken(token);
8
+ expect(payload.sub).toBe("u_1");
9
+ });
10
+ });
@@ -0,0 +1,8 @@
1
+ describe("stripe webhook signature", () => {
2
+ it("rejects an invalid stripe signature", async () => {
3
+ // Verifies the signature-check branch returns 400 on bad signatures.
4
+ expect(true).toBe(true);
5
+ });
6
+ });
7
+
8
+ // NOTE: intentionally missing a duplicate-delivery / retry test.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devtime-ei
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Local-first Engineering Intelligence for software repositories
5
5
  Author-email: Aviad Shakargi <aviad94@gmail.com>
6
6
  Maintainer-email: Aviad Shakargi <aviad94@gmail.com>
@@ -8,7 +8,7 @@ License: Apache-2.0
8
8
  Project-URL: Homepage, https://github.com/Shakargy/devtime
9
9
  Project-URL: Repository, https://github.com/Shakargy/devtime
10
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
11
+ Project-URL: Release Notes, https://github.com/Shakargy/devtime/releases/tag/v0.1.1
12
12
  Project-URL: Demo, https://youtu.be/1Hiu3Y9J_SI
13
13
  Keywords: devtools,cli,static-analysis,repository-analysis,engineering-intelligence,local-first
14
14
  Classifier: Development Status :: 3 - Alpha
@@ -56,6 +56,22 @@ decision improves understanding.
56
56
 
57
57
  ## Try DevTime in 60 seconds
58
58
 
59
+ ```bash
60
+ pipx install devtime-ei
61
+ dtc demo init
62
+ cd devtime-demo-saas
63
+ dtc init
64
+ dtc scan
65
+ dtc concepts
66
+ dtc explain "Billing Webhooks"
67
+ ```
68
+
69
+ The PyPI distribution is `devtime-ei`. The Python package remains `devtime`, and the
70
+ CLI command remains `dtc`. `dtc demo init` copies a small static example repo into
71
+ `./devtime-demo-saas` so you can try DevTime without cloning this repository.
72
+
73
+ ### From source
74
+
59
75
  ```bash
60
76
  git clone https://github.com/Shakargy/devtime.git
61
77
  cd devtime
@@ -98,27 +114,36 @@ steps) is in **[DEMO_SCRIPT.md](DEMO_SCRIPT.md)**.
98
114
 
99
115
  ## Why this exists
100
116
 
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.
117
+ Git records what changed, but it does not preserve the reasoning behind those
118
+ changes. When you return to a repository - or review one you did not write - you often
119
+ have to reconstruct why a behavior exists, what evidence supports it, and what is
120
+ still uncertain.
105
121
 
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.
122
+ DevTime builds evidence-backed repository memory: a local layer that helps a
123
+ codebase explain itself from code, tests, configs, routes, and recorded decisions.
124
+ It shows what the repository can support with evidence - and, just as importantly,
125
+ what it cannot support yet.
108
126
 
109
127
  ## Who it is for
110
128
 
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.
129
+ DevTime is for people who need to understand a repository from evidence rather than
130
+ memory.
114
131
 
115
- It is useful when you want to ask:
132
+ It is especially useful if you:
116
133
 
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?
134
+ - are onboarding to an unfamiliar codebase and need to understand how a feature is implemented;
135
+ - are reviewing a pull request and want to see what evidence supports a behavior;
136
+ - are returning to a project after weeks or months and cannot remember why something exists;
137
+ - maintain a long-lived project where design decisions are easily lost;
138
+ - want repository understanding to be backed by code and recorded decisions instead of generated summaries.
139
+
140
+ Questions DevTime helps answer include:
141
+
142
+ - Where is authentication actually implemented?
143
+ - What files prove that Billing Webhooks exist?
144
+ - What is still uncertain?
145
+ - Did this diff touch a risky concept?
146
+ - Is there a decision explaining this behavior?
122
147
 
123
148
  ## What DevTime does
124
149
 
@@ -185,7 +210,24 @@ step-by-step first run and troubleshooting.
185
210
 
186
211
  ## Installation
187
212
 
188
- Recommended source install for now:
213
+ Recommended: install from PyPI with [pipx](https://pipx.pypa.io/) so the `dtc`
214
+ command is available on your PATH in an isolated environment:
215
+
216
+ ```bash
217
+ pipx install devtime-ei
218
+ ```
219
+
220
+ Or with pip:
221
+
222
+ ```bash
223
+ pip install devtime-ei
224
+ ```
225
+
226
+ The PyPI distribution is `devtime-ei`. The Python package remains `devtime`, and the
227
+ CLI command remains `dtc`. After installing, run `dtc demo init` to create a local
228
+ example repo to try it on.
229
+
230
+ ### From source
189
231
 
190
232
  ```bash
191
233
  git clone https://github.com/Shakargy/devtime.git
@@ -205,12 +247,6 @@ python -m venv .venv
205
247
  pip install -e ".[dev]"
206
248
  ```
207
249
 
208
- Planned PyPI install (after the package is published and verified):
209
-
210
- ```bash
211
- pipx install devtime
212
- ```
213
-
214
250
  ## Example output
215
251
 
216
252
  ```
@@ -4,6 +4,7 @@ pyproject.toml
4
4
  src/devtime/__init__.py
5
5
  src/devtime/cli.py
6
6
  src/devtime/config.py
7
+ src/devtime/demo.py
7
8
  src/devtime/paths.py
8
9
  src/devtime/privacy.py
9
10
  src/devtime/ai/__init__.py
@@ -36,6 +37,21 @@ src/devtime/output/__init__.py
36
37
  src/devtime/output/json_export.py
37
38
  src/devtime/output/markdown.py
38
39
  src/devtime/output/terminal.py
40
+ src/devtime/resources/demo-saas/.devtimeignore
41
+ src/devtime/resources/demo-saas/README.md
42
+ src/devtime/resources/demo-saas/package.json
43
+ src/devtime/resources/demo-saas/docs/decisions/0001-use-jwt.md
44
+ src/devtime/resources/demo-saas/src/admin/permissions.ts
45
+ src/devtime/resources/demo-saas/src/auth/login.ts
46
+ src/devtime/resources/demo-saas/src/auth/middleware.ts
47
+ src/devtime/resources/demo-saas/src/auth/tokens.ts
48
+ src/devtime/resources/demo-saas/src/billing/stripe-webhook.ts
49
+ src/devtime/resources/demo-saas/src/billing/subscription-service.ts
50
+ src/devtime/resources/demo-saas/src/export/export-csv.ts
51
+ src/devtime/resources/demo-saas/src/jobs/email-worker.ts
52
+ src/devtime/resources/demo-saas/src/jobs/queues.ts
53
+ src/devtime/resources/demo-saas/tests/auth-login.test.ts
54
+ src/devtime/resources/demo-saas/tests/stripe-signature.test.ts
39
55
  src/devtime/scanner/__init__.py
40
56
  src/devtime/scanner/file_walker.py
41
57
  src/devtime/scanner/ignore.py
File without changes
File without changes