codd-dev 0.2.0a5__tar.gz → 0.3.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 (33) hide show
  1. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/.gitignore +1 -1
  2. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/PKG-INFO +38 -5
  3. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/README.md +27 -4
  4. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/cli.py +27 -39
  5. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/config.py +24 -1
  6. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/extractor.py +221 -252
  7. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/hooks.py +12 -6
  8. codd_dev-0.3.0/codd/parsing.py +2136 -0
  9. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/planner.py +6 -2
  10. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/scanner.py +55 -43
  11. codd_dev-0.3.0/codd/synth.py +782 -0
  12. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/templates/codd.yaml.tmpl +13 -13
  13. codd_dev-0.3.0/codd/templates/extracted/api-contract.md.j2 +76 -0
  14. codd_dev-0.3.0/codd/templates/extracted/architecture-overview.md.j2 +125 -0
  15. codd_dev-0.3.0/codd/templates/extracted/module-detail.md.j2 +102 -0
  16. codd_dev-0.3.0/codd/templates/extracted/schema-design.md.j2 +72 -0
  17. codd_dev-0.3.0/codd/templates/extracted/system-context.md.j2 +114 -0
  18. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/templates/gitignore.tmpl +2 -4
  19. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/verifier.py +307 -54
  20. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/pyproject.toml +15 -1
  21. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/LICENSE +0 -0
  22. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/__init__.py +0 -0
  23. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/defaults.yaml +0 -0
  24. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/generator.py +0 -0
  25. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/graph.py +0 -0
  26. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/implementer.py +0 -0
  27. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/propagate.py +0 -0
  28. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/templates/conventions.yaml.tmpl +0 -0
  29. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/templates/data_dependencies.yaml.tmpl +0 -0
  30. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/templates/doc_links.yaml.tmpl +0 -0
  31. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/templates/overrides.yaml.tmpl +0 -0
  32. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/codd/validator.py +0 -0
  33. {codd_dev-0.2.0a5 → codd_dev-0.3.0}/hooks/pre-commit +0 -0
@@ -22,5 +22,5 @@ htmlcov/
22
22
  .pytest_cache/
23
23
 
24
24
  # CoDD generated (for development/testing)
25
- codd/graph.db
25
+ codd/scan/
26
26
  codd/reports/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codd-dev
3
- Version: 0.2.0a5
3
+ Version: 0.3.0
4
4
  Summary: CoDD: Coherence-Driven Development — cross-artifact change impact analysis
5
5
  Project-URL: Homepage, https://github.com/yohey-w/codd-dev
6
6
  Project-URL: Repository, https://github.com/yohey-w/codd-dev
@@ -16,13 +16,23 @@ Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Topic :: Software Development :: Quality Assurance
17
17
  Requires-Python: >=3.10
18
18
  Requires-Dist: click>=8.0
19
+ Requires-Dist: jinja2>=3.1.0
19
20
  Requires-Dist: pyyaml>=6.0
21
+ Requires-Dist: tomli>=2.0.1; python_version < '3.11'
22
+ Provides-Extra: api-parsers
23
+ Requires-Dist: graphql-core>=3.2.0; extra == 'api-parsers'
24
+ Provides-Extra: infra
25
+ Requires-Dist: python-hcl2>=7.0.0; extra == 'infra'
20
26
  Provides-Extra: mcp
21
27
  Provides-Extra: scan
22
28
  Requires-Dist: tree-sitter-java>=0.22; extra == 'scan'
23
29
  Requires-Dist: tree-sitter-python>=0.22; extra == 'scan'
24
30
  Requires-Dist: tree-sitter-typescript>=0.22; extra == 'scan'
25
31
  Requires-Dist: tree-sitter>=0.22; extra == 'scan'
32
+ Provides-Extra: tree-sitter
33
+ Requires-Dist: tree-sitter-python<0.26.0,>=0.25.0; extra == 'tree-sitter'
34
+ Requires-Dist: tree-sitter-typescript<0.24.0,>=0.23.0; extra == 'tree-sitter'
35
+ Requires-Dist: tree-sitter<0.26.0,>=0.25.0; extra == 'tree-sitter'
26
36
  Description-Content-Type: text/markdown
27
37
 
28
38
  <p align="center">
@@ -49,7 +59,7 @@ Description-Content-Type: text/markdown
49
59
  pip install codd-dev
50
60
  ```
51
61
 
52
- **Public Alpha** — `init` / `scan` / `impact` / `validate` are stable today.
62
+ **Public Alpha** — `init` / `scan` / `impact` are stable; `validate` is alpha.
53
63
 
54
64
  ---
55
65
 
@@ -256,7 +266,17 @@ codd:
256
266
  ---
257
267
  ```
258
268
 
259
- `graph.db` is a cache — regenerated on every `codd scan`.
269
+ `codd/scan/` is a cache — regenerated on every `codd scan`.
270
+
271
+ ## Config Directory Discovery
272
+
273
+ By default, `codd init` creates a `codd/` directory. If your project already has a `codd/` directory (e.g., it's your source code package), use `--config-dir`:
274
+
275
+ ```bash
276
+ codd init --config-dir .codd --project-name "my-project" --language "python"
277
+ ```
278
+
279
+ All other commands (`scan`, `impact`, `generate`, etc.) automatically discover whichever config directory exists — `codd/` first, then `.codd/`. No extra flags needed.
260
280
 
261
281
  ## Brownfield? Start Here
262
282
 
@@ -291,7 +311,7 @@ codd impact
291
311
 
292
312
  | Command | Status | Description |
293
313
  |---------|--------|-------------|
294
- | `codd init` | **Stable** | Initialize CoDD in any project |
314
+ | `codd init` | **Stable** | Initialize CoDD in any project (`--config-dir .codd` for projects where `codd/` exists) |
295
315
  | `codd scan` | **Stable** | Build dependency graph from frontmatter |
296
316
  | `codd impact` | **Stable** | Change impact analysis (Green / Amber / Gray) |
297
317
  | `codd validate` | **Alpha** | Frontmatter integrity & graph consistency check |
@@ -395,11 +415,24 @@ docs/
395
415
  └── infra/ # Infrastructure design
396
416
  ```
397
417
 
418
+ ### CoDD Manages Its Own Development
419
+
420
+ CoDD dogfoods itself. The `.codd/` directory contains CoDD's own config, and `codd extract` reverse-engineers design docs from its own source code. The full V-Model lifecycle runs on itself:
421
+
422
+ ```bash
423
+ codd init --config-dir .codd --project-name "codd-dev" --language "python"
424
+ codd extract # 15 modules → design docs with dependency frontmatter
425
+ codd scan # 49 nodes, 83 edges
426
+ codd verify # mypy + pytest (127/127 tests pass)
427
+ ```
428
+
429
+ If CoDD can't manage itself, it shouldn't manage your project.
430
+
398
431
  ## Roadmap
399
432
 
400
433
  - [ ] Semantic dependency types (`requires`, `affects`, `verifies`, `implements`)
401
434
  - [x] `codd extract` — reverse-generate design docs from existing codebases (brownfield support)
402
- - [ ] `codd verify` — full docs-code-tests coherence check
435
+ - [x] `codd verify` — language-agnostic verification (Python: mypy + pytest, TypeScript: tsc + jest)
403
436
  - [ ] Multi-harness integration examples (Claude Code, Copilot, Cursor)
404
437
  - [ ] VS Code extension for impact visualization
405
438
 
@@ -22,7 +22,7 @@
22
22
  pip install codd-dev
23
23
  ```
24
24
 
25
- **Public Alpha** — `init` / `scan` / `impact` / `validate` are stable today.
25
+ **Public Alpha** — `init` / `scan` / `impact` are stable; `validate` is alpha.
26
26
 
27
27
  ---
28
28
 
@@ -229,7 +229,17 @@ codd:
229
229
  ---
230
230
  ```
231
231
 
232
- `graph.db` is a cache — regenerated on every `codd scan`.
232
+ `codd/scan/` is a cache — regenerated on every `codd scan`.
233
+
234
+ ## Config Directory Discovery
235
+
236
+ By default, `codd init` creates a `codd/` directory. If your project already has a `codd/` directory (e.g., it's your source code package), use `--config-dir`:
237
+
238
+ ```bash
239
+ codd init --config-dir .codd --project-name "my-project" --language "python"
240
+ ```
241
+
242
+ All other commands (`scan`, `impact`, `generate`, etc.) automatically discover whichever config directory exists — `codd/` first, then `.codd/`. No extra flags needed.
233
243
 
234
244
  ## Brownfield? Start Here
235
245
 
@@ -264,7 +274,7 @@ codd impact
264
274
 
265
275
  | Command | Status | Description |
266
276
  |---------|--------|-------------|
267
- | `codd init` | **Stable** | Initialize CoDD in any project |
277
+ | `codd init` | **Stable** | Initialize CoDD in any project (`--config-dir .codd` for projects where `codd/` exists) |
268
278
  | `codd scan` | **Stable** | Build dependency graph from frontmatter |
269
279
  | `codd impact` | **Stable** | Change impact analysis (Green / Amber / Gray) |
270
280
  | `codd validate` | **Alpha** | Frontmatter integrity & graph consistency check |
@@ -368,11 +378,24 @@ docs/
368
378
  └── infra/ # Infrastructure design
369
379
  ```
370
380
 
381
+ ### CoDD Manages Its Own Development
382
+
383
+ CoDD dogfoods itself. The `.codd/` directory contains CoDD's own config, and `codd extract` reverse-engineers design docs from its own source code. The full V-Model lifecycle runs on itself:
384
+
385
+ ```bash
386
+ codd init --config-dir .codd --project-name "codd-dev" --language "python"
387
+ codd extract # 15 modules → design docs with dependency frontmatter
388
+ codd scan # 49 nodes, 83 edges
389
+ codd verify # mypy + pytest (127/127 tests pass)
390
+ ```
391
+
392
+ If CoDD can't manage itself, it shouldn't manage your project.
393
+
371
394
  ## Roadmap
372
395
 
373
396
  - [ ] Semantic dependency types (`requires`, `affects`, `verifies`, `implements`)
374
397
  - [x] `codd extract` — reverse-generate design docs from existing codebases (brownfield support)
375
- - [ ] `codd verify` — full docs-code-tests coherence check
398
+ - [x] `codd verify` — language-agnostic verification (Python: mypy + pytest, TypeScript: tsc + jest)
376
399
  - [ ] Multi-harness integration examples (Claude Code, Copilot, Cursor)
377
400
  - [ ] VS Code extension for impact visualization
378
401
 
@@ -6,9 +6,20 @@ import os
6
6
  import shutil
7
7
  from pathlib import Path
8
8
 
9
+ from codd.config import find_codd_dir
10
+
9
11
  TEMPLATES_DIR = Path(__file__).parent / "templates"
10
12
 
11
13
 
14
+ def _require_codd_dir(project_root: Path) -> Path:
15
+ """Return the CoDD config dir or exit with a helpful message."""
16
+ codd_dir = find_codd_dir(project_root)
17
+ if codd_dir is None:
18
+ click.echo("Error: CoDD config dir not found (looked for codd/ and .codd/). Run 'codd init' first.")
19
+ raise SystemExit(1)
20
+ return codd_dir
21
+
22
+
12
23
  @click.group()
13
24
  @click.version_option(package_name="codd-dev")
14
25
  def main():
@@ -18,7 +29,7 @@ def main():
18
29
 
19
30
  @main.command()
20
31
  @click.option("--project-name", prompt="Project name", help="Name of the project")
21
- @click.option("--language", prompt="Primary language", help="Primary language (python/typescript/java/go)")
32
+ @click.option("--language", prompt="Primary language", help="Primary language (python/typescript/javascript/go — full support; java — symbols only)")
22
33
  @click.option("--dest", default=".", help="Destination directory (default: current dir)")
23
34
  @click.option(
24
35
  "--requirements",
@@ -26,10 +37,15 @@ def main():
26
37
  type=click.Path(exists=True),
27
38
  help="Import a requirements file (any format: .md, .txt, .doc). CoDD adds frontmatter automatically.",
28
39
  )
29
- def init(project_name: str, language: str, dest: str, requirements: str | None):
40
+ @click.option(
41
+ "--config-dir",
42
+ default="codd",
43
+ help="Name of the CoDD config directory (default: codd). Use .codd when codd/ is your source directory.",
44
+ )
45
+ def init(project_name: str, language: str, dest: str, requirements: str | None, config_dir: str):
30
46
  """Initialize CoDD in a project directory."""
31
47
  dest_path = Path(dest).resolve()
32
- codd_dir = dest_path / "codd"
48
+ codd_dir = dest_path / config_dir
33
49
 
34
50
  if codd_dir.exists():
35
51
  if requirements:
@@ -81,11 +97,7 @@ def scan(path: str):
81
97
  """Scan codebase and update dependency graph (Stage 1)."""
82
98
  from codd.scanner import run_scan
83
99
  project_root = Path(path).resolve()
84
- codd_dir = project_root / "codd"
85
-
86
- if not codd_dir.exists():
87
- click.echo("Error: codd/ not found. Run 'codd init' first.")
88
- raise SystemExit(1)
100
+ codd_dir = _require_codd_dir(project_root)
89
101
 
90
102
  run_scan(project_root, codd_dir)
91
103
 
@@ -98,11 +110,7 @@ def impact(diff: str, path: str, output: str):
98
110
  """Analyze change impact from git diff."""
99
111
  from codd.propagate import run_impact
100
112
  project_root = Path(path).resolve()
101
- codd_dir = project_root / "codd"
102
-
103
- if not codd_dir.exists():
104
- click.echo("Error: codd/ not found. Run 'codd init' first.")
105
- raise SystemExit(1)
113
+ codd_dir = _require_codd_dir(project_root)
106
114
 
107
115
  run_impact(project_root, codd_dir, diff, output)
108
116
 
@@ -121,11 +129,7 @@ def generate(wave: int, path: str, force: bool, ai_cmd: str | None):
121
129
  from codd.generator import generate_wave, _load_project_config
122
130
 
123
131
  project_root = Path(path).resolve()
124
- codd_dir = project_root / "codd"
125
-
126
- if not codd_dir.exists():
127
- click.echo("Error: codd/ not found. Run 'codd init' first.")
128
- raise SystemExit(1)
132
+ codd_dir = _require_codd_dir(project_root)
129
133
 
130
134
  # Auto-generate wave_config if missing
131
135
  config = _load_project_config(project_root)
@@ -174,11 +178,7 @@ def implement(sprint: int, path: str, task: str | None, ai_cmd: str | None):
174
178
  from codd.implementer import implement_sprint
175
179
 
176
180
  project_root = Path(path).resolve()
177
- codd_dir = project_root / "codd"
178
-
179
- if not codd_dir.exists():
180
- click.echo("Error: codd/ not found. Run 'codd init' first.")
181
- raise SystemExit(1)
181
+ codd_dir = _require_codd_dir(project_root)
182
182
 
183
183
  try:
184
184
  results = implement_sprint(project_root, sprint, task=task, ai_command=ai_cmd)
@@ -204,11 +204,7 @@ def verify(path: str, sprint: int | None) -> None:
204
204
  from codd.verifier import VerifyPreflightError, run_verify
205
205
 
206
206
  project_root = Path(path).resolve()
207
- codd_dir = project_root / "codd"
208
-
209
- if not codd_dir.exists():
210
- click.echo("Error: codd/ not found. Run 'codd init' first.")
211
- raise SystemExit(1)
207
+ codd_dir = _require_codd_dir(project_root)
212
208
 
213
209
  try:
214
210
  result = run_verify(project_root, sprint=sprint)
@@ -248,7 +244,7 @@ def verify(path: str, sprint: int | None) -> None:
248
244
 
249
245
  @main.command()
250
246
  @click.option("--path", default=".", help="Project root directory")
251
- @click.option("--language", default=None, help="Override language detection (python/typescript/javascript/java/go)")
247
+ @click.option("--language", default=None, help="Override language detection (python/typescript/javascript/go — full support; java — symbols only)")
252
248
  @click.option("--source-dirs", default=None, help="Comma-separated source directories (default: auto-detect)")
253
249
  @click.option("--output", default=None, help="Output directory (default: codd/extracted/)")
254
250
  def extract(path: str, language: str | None, source_dirs: str | None, output: str | None):
@@ -289,11 +285,7 @@ def validate(path: str):
289
285
  from codd.validator import run_validate
290
286
 
291
287
  project_root = Path(path).resolve()
292
- codd_dir = project_root / "codd"
293
-
294
- if not codd_dir.exists():
295
- click.echo("Error: codd/ not found. Run 'codd init' first.")
296
- raise SystemExit(1)
288
+ codd_dir = _require_codd_dir(project_root)
297
289
 
298
290
  raise SystemExit(run_validate(project_root, codd_dir))
299
291
 
@@ -313,11 +305,7 @@ def plan(path: str, as_json: bool, initialize: bool, force: bool, ai_cmd: str |
313
305
  from codd.planner import build_plan, plan_init, plan_to_dict, render_plan_text
314
306
 
315
307
  project_root = Path(path).resolve()
316
- codd_dir = project_root / "codd"
317
-
318
- if not codd_dir.exists():
319
- click.echo("Error: codd/ not found. Run 'codd init' first.")
320
- raise SystemExit(1)
308
+ codd_dir = _require_codd_dir(project_root)
321
309
 
322
310
  if initialize:
323
311
  if as_json:
@@ -12,10 +12,33 @@ import yaml
12
12
 
13
13
  DEFAULTS_PATH = Path(__file__).with_name("defaults.yaml")
14
14
 
15
+ # Supported CoDD config directory names, in priority order.
16
+ CODD_DIR_CANDIDATES = ("codd", ".codd")
17
+
18
+
19
+ def find_codd_dir(project_root: Path) -> Path | None:
20
+ """Discover the CoDD config directory under *project_root*.
21
+
22
+ A valid CoDD config directory must contain ``codd.yaml``.
23
+ Checks ``codd/`` first, then ``.codd/``. Returns ``None`` when
24
+ neither qualifies (caller decides whether that is an error).
25
+ """
26
+ for name in CODD_DIR_CANDIDATES:
27
+ candidate = project_root / name
28
+ if candidate.is_dir() and (candidate / "codd.yaml").exists():
29
+ return candidate
30
+ return None
31
+
15
32
 
16
33
  def load_project_config(project_root: Path) -> dict[str, Any]:
17
34
  """Load CoDD defaults and merge project-local overrides."""
18
- config_path = project_root / "codd" / "codd.yaml"
35
+ codd_dir = find_codd_dir(project_root)
36
+ if codd_dir is None:
37
+ raise FileNotFoundError(
38
+ f"CoDD config dir not found in {project_root} "
39
+ f"(looked for {', '.join(CODD_DIR_CANDIDATES)})"
40
+ )
41
+ config_path = codd_dir / "codd.yaml"
19
42
  if not config_path.exists():
20
43
  raise FileNotFoundError(f"{config_path} not found")
21
44