fix: resolve 8 bugs affecting real users
- Fix KeyError 'root' in FileGrouper when merging groups with no root-level files - Fix changelog commit parsing breaking on multiline bodies (use %x00 separator) - Add empty choices/content checks in OpenAI, Anthropic, OpenRouter providers - Fix config edit crash when config is empty - Fix validate command using wrong color for 'no staged files' - Add type check for corrupted YAML config (non-dict returns empty dict)
This commit is contained in:
8 files changed
+29
-10
No files matched your search
@@ -120,7 +120,7 @@ def validate() -> None:
|
||||
for f in staged:
|
||||
typer.echo(f"- {f}")
|
||||
else:
|
||||
typer.secho("[i] No staged files.", fg=typer.colors.RED)
|
||||
typer.secho("[i] No staged files.", fg=typer.colors.YELLOW)
|
||||
|
||||
|
||||
@app.command("undo")
|
||||
|
||||
@@ -48,6 +48,12 @@ def edit_config(
|
||||
|
||||
if not key:
|
||||
choices = list(config.keys())
|
||||
if not choices:
|
||||
typer.secho(
|
||||
"No configuration keys found. Run `devgen setup config` first.",
|
||||
fg=typer.colors.YELLOW,
|
||||
)
|
||||
raise typer.Exit()
|
||||
key = questionary.select(
|
||||
"Select key to update:", choices=choices, style=style
|
||||
).ask()
|
||||
|
||||
@@ -32,17 +32,21 @@ class ChangelogGenerator:
|
||||
else:
|
||||
cmd = self._resolve_range(to_ref)
|
||||
try:
|
||||
return run_git_command(cmd).split("\n")
|
||||
output = run_git_command(cmd)
|
||||
# Use %x00 as separator to avoid splitting on newlines in commit bodies
|
||||
# Then filter out empty strings
|
||||
return [c for c in output.split("\0") if c.strip()]
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.logger.error(f"Git command failed: {e}")
|
||||
raise RuntimeError(f"Git command failed: {e}") from e
|
||||
|
||||
def _log_cmd(self, range_spec: str) -> List[str]:
|
||||
# Format: hash|author|date|subject|body
|
||||
# Format: hash|author|date|subject|body (separated by %x00 to avoid
|
||||
# splitting on newlines within commit bodies)
|
||||
return [
|
||||
"git",
|
||||
"log",
|
||||
"--format=%H|%an|%ad|%s|%b",
|
||||
"--format=%H|%an|%ad|%s|%b%x00",
|
||||
"--date=short",
|
||||
range_spec,
|
||||
]
|
||||
|
||||
@@ -129,10 +129,7 @@ class FileGrouper:
|
||||
parent_path = str(Path(deepest).parent)
|
||||
new_key = "root" if parent_path == "." else parent_path
|
||||
files = groups.pop(deepest)
|
||||
if new_key in groups:
|
||||
groups[new_key].extend(files)
|
||||
else:
|
||||
groups["root"].extend(files)
|
||||
groups.setdefault(new_key, []).extend(files)
|
||||
return groups
|
||||
|
||||
|
||||
|
||||
@@ -16,4 +16,6 @@ class AnthropicProvider(BaseProvider):
|
||||
max_tokens=1024,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
)
|
||||
if not message.content:
|
||||
raise RuntimeError(f"Anthropic returned no content for model {model!r}.")
|
||||
return message.content[0].text or ""
|
||||
@@ -17,4 +17,6 @@ class OpenaiProvider(BaseProvider):
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
**kwargs,
|
||||
)
|
||||
if not response.choices:
|
||||
raise RuntimeError(f"OpenAI returned no choices for model {model!r}.")
|
||||
return response.choices[0].message.content or ""
|
||||
@@ -22,4 +22,6 @@ class OpenrouterProvider(BaseProvider):
|
||||
},
|
||||
**kwargs,
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
if not response.choices:
|
||||
raise RuntimeError(f"OpenRouter returned no choices for model {model!r}.")
|
||||
return response.choices[0].message.content or ""
|
||||
+7
-1
@@ -246,7 +246,13 @@ def load_config() -> Dict[str, Any]:
|
||||
|
||||
try:
|
||||
with config_path.open("r", encoding="utf-8") as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
data = yaml.safe_load(f)
|
||||
if not isinstance(data, dict):
|
||||
print(
|
||||
f"Warning: Config at {config_path} is not a dict, using defaults."
|
||||
)
|
||||
return {}
|
||||
return data
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to load config from {config_path}: {e}")
|
||||
return {}
|
||||
|
||||
Reference in new issue
Block a user