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.
- {codd_dev-1.9.2 → codd_dev-1.10.0}/PKG-INFO +1 -1
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/implementer.py +135 -16
- {codd_dev-1.9.2 → codd_dev-1.10.0}/pyproject.toml +1 -1
- {codd_dev-1.9.2 → codd_dev-1.10.0}/.gitignore +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/LICENSE +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/README.md +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/__init__.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/assembler.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/bridge.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/cli.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/clustering.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/config.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/contracts.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/defaults.yaml +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/e2e_runner.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/env_refs.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/extract_ai.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/extractor.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/fixer.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/generator.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/graph.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/hooks/__init__.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/hooks/pre-commit +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/inheritance.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/mcp_server.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/measure.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/parsing.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/planner.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/policy.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/propagate.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/propagator.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/repair_slice.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/require.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/require_plugins.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/restore.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/scanner.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/schema_refs.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/synth.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/codd.yaml.tmpl +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/conventions.yaml.tmpl +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/data_dependencies.yaml.tmpl +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/doc_links.yaml.tmpl +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/api-contract.md.j2 +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/architecture-overview.md.j2 +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/module-detail.md.j2 +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/schema-design.md.j2 +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/extracted/system-context.md.j2 +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/gitignore.tmpl +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/templates/overrides.yaml.tmpl +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/traceability.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/validator.py +0 -0
- {codd_dev-1.9.2 → codd_dev-1.10.0}/codd/wiring.py +0 -0
- {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.
|
|
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")
|
|
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
|
|
901
|
-
|
|
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
|
-
|
|
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
|
|
912
|
-
"```
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1146
|
-
if
|
|
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"
|
|
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("
|
|
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(" ・")]
|
|
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
|
|
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
|