codd-dev 1.9.2__tar.gz → 1.10.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 (53) hide show
  1. {codd_dev-1.9.2 → codd_dev-1.10.0}/PKG-INFO +1 -1
  2. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/implementer.py +135 -16
  3. {codd_dev-1.9.2 → codd_dev-1.10.0}/pyproject.toml +1 -1
  4. {codd_dev-1.9.2 → codd_dev-1.10.0}/.gitignore +0 -0
  5. {codd_dev-1.9.2 → codd_dev-1.10.0}/LICENSE +0 -0
  6. {codd_dev-1.9.2 → codd_dev-1.10.0}/README.md +0 -0
  7. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/__init__.py +0 -0
  8. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/assembler.py +0 -0
  9. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/bridge.py +0 -0
  10. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/cli.py +0 -0
  11. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/clustering.py +0 -0
  12. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/config.py +0 -0
  13. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/contracts.py +0 -0
  14. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/defaults.yaml +0 -0
  15. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/e2e_runner.py +0 -0
  16. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/env_refs.py +0 -0
  17. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/extract_ai.py +0 -0
  18. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/extractor.py +0 -0
  19. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/fixer.py +0 -0
  20. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/generator.py +0 -0
  21. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/graph.py +0 -0
  22. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/hooks/__init__.py +0 -0
  23. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/hooks/pre-commit +0 -0
  24. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/inheritance.py +0 -0
  25. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/mcp_server.py +0 -0
  26. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/measure.py +0 -0
  27. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/parsing.py +0 -0
  28. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/planner.py +0 -0
  29. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/policy.py +0 -0
  30. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/propagate.py +0 -0
  31. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/propagator.py +0 -0
  32. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/repair_slice.py +0 -0
  33. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/require.py +0 -0
  34. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/require_plugins.py +0 -0
  35. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/restore.py +0 -0
  36. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/scanner.py +0 -0
  37. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/schema_refs.py +0 -0
  38. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/synth.py +0 -0
  39. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/codd.yaml.tmpl +0 -0
  40. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/conventions.yaml.tmpl +0 -0
  41. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/data_dependencies.yaml.tmpl +0 -0
  42. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/doc_links.yaml.tmpl +0 -0
  43. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/api-contract.md.j2 +0 -0
  44. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/architecture-overview.md.j2 +0 -0
  45. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/module-detail.md.j2 +0 -0
  46. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/schema-design.md.j2 +0 -0
  47. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/system-context.md.j2 +0 -0
  48. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/gitignore.tmpl +0 -0
  49. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/overrides.yaml.tmpl +0 -0
  50. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/traceability.py +0 -0
  51. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/validator.py +0 -0
  52. {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/wiring.py +0 -0
  53. {codd_dev-1.9.2 → codd_dev-1.10.0}/docs/requirements/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codd-dev
3
- Version: 1.9.2
3
+ Version: 1.10.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
@@ -21,6 +21,79 @@ from codd.scanner import _extract_frontmatter, build_document_node_path_map
21
21
 
22
22
  DEFAULT_IMPLEMENT_NODE_ID = "plan:implementation-plan"
23
23
  FILE_BLOCK_RE = re.compile(r"^=== FILE: (?P<path>.+?) ===\s*$", re.MULTILINE)
24
+ LANGUAGE_EXT_MAP: dict[str, tuple[str, ...]] = {
25
+ "typescript": (".ts", ".tsx"),
26
+ "javascript": (".js", ".jsx"),
27
+ "python": (".py",),
28
+ "rust": (".rs",),
29
+ "go": (".go",),
30
+ "java": (".java",),
31
+ "kotlin": (".kt",),
32
+ "swift": (".swift",),
33
+ "cpp": (".cpp", ".cc", ".h"),
34
+ "c": (".c", ".h"),
35
+ "csharp": (".cs",),
36
+ "ruby": (".rb",),
37
+ }
38
+ LANGUAGE_ALIASES = {
39
+ "ts": "typescript",
40
+ "tsx": "typescript",
41
+ "js": "javascript",
42
+ "jsx": "javascript",
43
+ "py": "python",
44
+ "rs": "rust",
45
+ "golang": "go",
46
+ "c++": "cpp",
47
+ "cc": "cpp",
48
+ "c#": "csharp",
49
+ "cs": "csharp",
50
+ }
51
+ LANGUAGE_DISPLAY_NAMES = {
52
+ "typescript": "TypeScript",
53
+ "javascript": "JavaScript",
54
+ "python": "Python",
55
+ "rust": "Rust",
56
+ "go": "Go",
57
+ "java": "Java",
58
+ "kotlin": "Kotlin",
59
+ "swift": "Swift",
60
+ "cpp": "C++",
61
+ "c": "C",
62
+ "csharp": "C#",
63
+ "ruby": "Ruby",
64
+ }
65
+ LANGUAGE_CODE_FENCE_MAP = {
66
+ "typescript": "ts",
67
+ "javascript": "js",
68
+ "python": "python",
69
+ "rust": "rust",
70
+ "go": "go",
71
+ "java": "java",
72
+ "kotlin": "kotlin",
73
+ "swift": "swift",
74
+ "cpp": "cpp",
75
+ "c": "c",
76
+ "csharp": "csharp",
77
+ "ruby": "ruby",
78
+ }
79
+ COMMENT_PREFIX_BY_SUFFIX = {
80
+ ".ts": "//",
81
+ ".tsx": "//",
82
+ ".js": "//",
83
+ ".jsx": "//",
84
+ ".py": "#",
85
+ ".rs": "//",
86
+ ".go": "//",
87
+ ".java": "//",
88
+ ".kt": "//",
89
+ ".swift": "//",
90
+ ".cpp": "//",
91
+ ".cc": "//",
92
+ ".h": "//",
93
+ ".c": "//",
94
+ ".cs": "//",
95
+ ".rb": "#",
96
+ }
24
97
 
25
98
  SPRINT_HEADING_RE = re.compile(
26
99
  r"^####\s+Sprint\s+(?P<number>\d+)(?:((?P<window>[^)]+)))?(?:\s*:\s*(?P<title>.+))?\s*$",
@@ -305,12 +378,14 @@ def _execute_task(
305
378
  raw_output = generator_module._invoke_ai_command(
306
379
  resolved_ai_command, prompt, project_root=project_root,
307
380
  )
381
+ language = _normalize_implementation_language((config.get("project") or {}).get("language"))
308
382
  generated_files = _write_generated_files(
309
383
  project_root=project_root,
310
384
  plan=plan,
311
385
  task=task_item,
312
386
  dependency_documents=dependency_documents,
313
387
  output_dir=task_item.output_dir,
388
+ language=language,
314
389
  raw_output=raw_output,
315
390
  )
316
391
  summary = _summarize_generated_task_output(project_root, task_item, generated_files)
@@ -880,8 +955,30 @@ def _build_implementation_prompt(
880
955
  ) -> str:
881
956
  project = config.get("project") or {}
882
957
  frameworks = project.get("frameworks") or []
883
- language = project.get("language") or "typescript"
958
+ language = _normalize_implementation_language(project.get("language"))
959
+ language_name = LANGUAGE_DISPLAY_NAMES.get(language, language)
960
+ preferred_extensions = _implementation_language_extensions(language)
961
+ default_extension = _default_generated_extension(language)
962
+ code_fence_language = LANGUAGE_CODE_FENCE_MAP.get(language, language)
884
963
  framework_text = ", ".join(str(item) for item in frameworks) if frameworks else "(unspecified)"
964
+ if frameworks:
965
+ framework_guidance = f"- Honor the configured framework stack ({framework_text}) when relevant."
966
+ else:
967
+ framework_guidance = f"- Use idiomatic {language_name} patterns for the target project."
968
+
969
+ if language in {"typescript", "javascript"} and len(preferred_extensions) > 1:
970
+ jsx_extension = preferred_extensions[1]
971
+ extension_guidance = (
972
+ f"- If the task needs JSX-style UI components, emit {jsx_extension} files. "
973
+ f"Otherwise prefer {default_extension} files."
974
+ )
975
+ elif len(preferred_extensions) > 1:
976
+ extension_guidance = (
977
+ f"- Use {default_extension} files by default. "
978
+ f"Additional allowed extensions for this language family: {', '.join(preferred_extensions)}."
979
+ )
980
+ else:
981
+ extension_guidance = f"- Use {default_extension} files for generated source unless the task explicitly requires another file type."
885
982
 
886
983
  lines = [
887
984
  "You are generating implementation code from CoDD design documents.",
@@ -897,20 +994,20 @@ def _build_implementation_prompt(
897
994
  f"Output directory: {task.output_dir}",
898
995
  "",
899
996
  "Mandatory instructions:",
900
- "- Generate concrete production-oriented TypeScript / TSX source files.",
901
- "- Use Next.js App Router, TypeScript, and Prisma-compatible patterns when relevant.",
997
+ f"- Generate concrete production-oriented {language_name} source files.",
998
+ framework_guidance,
902
999
  "- Reflect tenant isolation, RLS context propagation, authentication, authorization, and auditability explicitly where the design requires them.",
903
1000
  "- The tool will prepend traceability comments to each generated file; do not emit separate metadata files.",
904
1001
  "- Do not emit prose, explanations, Markdown headings, YAML, TODOs, placeholders, or file descriptions outside the required FILE blocks.",
905
1002
  "- Every generated file path must stay under the output directory shown above.",
906
- "- If a React component is needed, emit .tsx files. Otherwise prefer .ts files.",
1003
+ extension_guidance,
907
1004
  "- Favor small coherent modules rather than one monolithic file.",
908
1005
  "- Cross-file imports may use relative imports or '@/generated/...' style aliases, but keep the task internally coherent.",
909
1006
  "",
910
1007
  "Required output format (repeat this block for each file and output nothing else):",
911
- f"=== FILE: {task.output_dir}/<filename>.ts ===",
912
- "```ts",
913
- "// code",
1008
+ f"=== FILE: {task.output_dir}/<filename>{default_extension} ===",
1009
+ f"```{code_fence_language}",
1010
+ "# code" if default_extension in {".py", ".rb"} else "// code",
914
1011
  "```",
915
1012
  "",
916
1013
  "ABSOLUTE PROHIBITION: Outputting prose, planning notes, TODO markers, or files outside the output directory is a CRITICAL ERROR.",
@@ -943,7 +1040,8 @@ def _build_implementation_prompt(
943
1040
  lines.append(f"{index}. Targets: {targets or '(no explicit targets)'}")
944
1041
  lines.append(f" Reason: {reason}")
945
1042
 
946
- if prior_task_outputs:
1043
+ successful_prior_outputs = [s for s in prior_task_outputs if not s.get("error")]
1044
+ if successful_prior_outputs:
947
1045
  lines.extend(
948
1046
  [
949
1047
  "",
@@ -953,7 +1051,7 @@ def _build_implementation_prompt(
953
1051
  "- Reuse these implementations via imports. If a needed symbol already exists below, import it instead of redefining it.",
954
1052
  ]
955
1053
  )
956
- for summary in prior_task_outputs:
1054
+ for summary in successful_prior_outputs:
957
1055
  lines.extend(_format_prior_task_summary(summary))
958
1056
 
959
1057
  lines.extend(
@@ -982,9 +1080,10 @@ def _write_generated_files(
982
1080
  task: ImplementationTask,
983
1081
  dependency_documents: list[DependencyDocument],
984
1082
  output_dir: str,
1083
+ language: str,
985
1084
  raw_output: str,
986
1085
  ) -> list[Path]:
987
- file_payloads = _parse_file_payloads(raw_output, output_dir)
1086
+ file_payloads = _parse_file_payloads(raw_output, output_dir, language)
988
1087
  traceability_comment = _build_traceability_comment(plan, task, dependency_documents)
989
1088
  generated_paths: list[Path] = []
990
1089
  for relative_path, content in file_payloads:
@@ -995,14 +1094,14 @@ def _write_generated_files(
995
1094
  return generated_paths
996
1095
 
997
1096
 
998
- def _parse_file_payloads(raw_output: str, output_dir: str) -> list[tuple[str, str]]:
1097
+ def _parse_file_payloads(raw_output: str, output_dir: str, language: str) -> list[tuple[str, str]]:
999
1098
  cleaned_output = raw_output.strip()
1000
1099
  matches = list(FILE_BLOCK_RE.finditer(cleaned_output))
1001
1100
  if not matches:
1002
1101
  fallback_content = _strip_code_fence(cleaned_output).strip()
1003
1102
  if not fallback_content:
1004
1103
  raise ValueError("AI command returned empty implementation output")
1005
- extension = ".tsx" if _looks_like_tsx(fallback_content) else ".ts"
1104
+ extension = _default_generated_extension(language, fallback_content)
1006
1105
  return [(f"{output_dir}/index{extension}", fallback_content.rstrip() + "\n")]
1007
1106
 
1008
1107
  payloads: list[tuple[str, str]] = []
@@ -1142,13 +1241,13 @@ def _build_traceability_comment(
1142
1241
 
1143
1242
 
1144
1243
  def _prepend_traceability_comment(relative_path: str, comment_block: str, content: str) -> str:
1145
- suffix = PurePosixPath(relative_path).suffix
1146
- if suffix not in {".ts", ".tsx", ".js", ".jsx"}:
1244
+ prefix = COMMENT_PREFIX_BY_SUFFIX.get(PurePosixPath(relative_path).suffix.lower())
1245
+ if prefix is None:
1147
1246
  return content
1148
1247
 
1149
- formatted_comment = "\n".join(f"// {line}" for line in comment_block.splitlines())
1248
+ formatted_comment = "\n".join(f"{prefix} {line}" for line in comment_block.splitlines())
1150
1249
  stripped_content = content.lstrip()
1151
- if stripped_content.startswith("// @generated-by: codd implement"):
1250
+ if stripped_content.startswith(f"{prefix} @generated-by: codd implement"):
1152
1251
  return content
1153
1252
  return f"{formatted_comment}\n\n{content.lstrip()}"
1154
1253
 
@@ -1165,6 +1264,26 @@ def _looks_like_tsx(content: str) -> bool:
1165
1264
  return bool(re.search(r"</?[A-Z][A-Za-z0-9]*|return\s*\(\s*<", content))
1166
1265
 
1167
1266
 
1267
+ def _normalize_implementation_language(language: Any) -> str:
1268
+ normalized = str(language or "").strip().lower()
1269
+ if not normalized:
1270
+ return "typescript"
1271
+ return LANGUAGE_ALIASES.get(normalized, normalized)
1272
+
1273
+
1274
+ def _implementation_language_extensions(language: Any) -> tuple[str, ...]:
1275
+ normalized = _normalize_implementation_language(language)
1276
+ return LANGUAGE_EXT_MAP.get(normalized, LANGUAGE_EXT_MAP["typescript"])
1277
+
1278
+
1279
+ def _default_generated_extension(language: Any, content: str | None = None) -> str:
1280
+ normalized = _normalize_implementation_language(language)
1281
+ extensions = _implementation_language_extensions(normalized)
1282
+ if normalized in {"typescript", "javascript"} and len(extensions) > 1 and content and _looks_like_tsx(content):
1283
+ return extensions[1]
1284
+ return extensions[0]
1285
+
1286
+
1168
1287
  def _split_deliverable_chunks(text: str) -> list[str]:
1169
1288
  chunks = re.split(r"[、/]", text or "")
1170
1289
  return [re.sub(r"\s+", " ", chunk).strip(" ・") for chunk in chunks if chunk.strip(" ・")]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codd-dev"
7
- version = "1.9.2"
7
+ version = "1.10.0"
8
8
  description = "CoDD: Coherence-Driven Development — cross-artifact change impact analysis"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes