llmcodegen/check_results.json

122 lines
110 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[
{
"tool": "black",
"file": "tests/__init__.py",
"returncode": 0,
"stdout": "",
"stderr": "All done! ✨ 🍰 ✨\n1 file would be left unchanged.\n",
"errors": [
"All done! ✨ 🍰 ✨\n1 file would be left unchanged."
]
},
{
"tool": "black",
"file": "tests/test_cli.py",
"returncode": 1,
"stdout": "--- tests/test_cli.py\t2026-03-17 14:27:38.333874+00:00\n+++ tests/test_cli.py\t2026-03-17 14:57:42.893165+00:00\n@@ -10,143 +10,153 @@\n \n \n def test_cli_init_success():\n \"\"\"测试 init 命令成功执行\"\"\"\n from src.llm_codegen.cli import app # 假设从项目根目录运行测试\n- \n+\n # 模拟 CodeGenerator 和其方法,避免实际调用 API\n- with patch('src.llm_codegen.cli.CodeGenerator') as mock_generator:\n+ with patch(\"src.llm_codegen.cli.CodeGenerator\") as mock_generator:\n mock_instance = Mock()\n mock_instance.run = Mock()\n mock_generator.return_value = mock_instance\n- \n+\n # 创建一个虚拟的 README 文件用于测试\n test_readme = Path(\"test_readme.md\")\n test_readme.write_text(\"# Test Project\\n\\nA test project for CLI.\")\n- \n- result = runner.invoke(app, [\"init\", str(test_readme), \"--output\", \"./test_output\"])\n- \n+\n+ result = runner.invoke(\n+ app, [\"init\", str(test_readme), \"--output\", \"./test_output\"]\n+ )\n+\n # 清理\n test_readme.unlink()\n- \n+\n assert result.exit_code == 0\n assert \"初始化失败\" not in result.stdout\n mock_generator.assert_called_once()\n mock_instance.run.assert_called_once_with(test_readme)\n \n \n def test_cli_init_failure_no_readme():\n \"\"\"测试 init 命令当 README 不存在时失败\"\"\"\n from src.llm_codegen.cli import app\n- \n+\n result = runner.invoke(app, [\"init\", \"nonexistent.md\"])\n- \n+\n assert result.exit_code != 0 # 应该退出码非零\n \n \n def test_cli_enhance_success():\n \"\"\"测试 enhance 命令成功执行(简化版,基于工单)\"\"\"\n from src.llm_codegen.cli import app\n- \n+\n # 模拟依赖文件和环境\n- with patch('src.llm_codegen.cli.CodeGenerator') as mock_generator, \\\n- patch('src.llm_codegen.cli.Checker') as mock_checker, \\\n- patch('pathlib.Path.exists') as mock_exists:\n- \n+ with (\n+ patch(\"src.llm_codegen.cli.CodeGenerator\") as mock_generator,\n+ patch(\"src.llm_codegen.cli.Checker\") as mock_checker,\n+ patch(\"pathlib.Path.exists\") as mock_exists,\n+ ):\n+\n mock_exists.return_value = True # 模拟 design.json 存在\n mock_instance = Mock()\n mock_instance.run_full_check_and_fix = Mock(return_value=True)\n mock_checker.return_value = mock_instance\n mock_generator.return_value = Mock()\n- \n+\n # 创建一个虚拟的工单文件\n test_issue = Path(\"test_feature.issue\")\n test_issue.write_text(\"name: Add feature\\ndescription: Test feature\")\n- \n- result = runner.invoke(app, [\"enhance\", str(test_issue), \"--output\", \"./test_output\"])\n- \n+\n+ result = runner.invoke(\n+ app, [\"enhance\", str(test_issue), \"--output\", \"./test_output\"]\n+ )\n+\n # 清理\n test_issue.unlink()\n- \n+\n assert result.exit_code == 0\n assert \"增强失败\" not in result.stdout\n mock_checker.assert_called_once()\n mock_instance.run_full_check_and_fix.assert_called_once()\n \n \n def test_cli_fix_success():\n \"\"\"测试 fix 命令成功执行(简化版,基于工单)\"\"\"\n from src.llm_codegen.cli import app\n- \n- with patch('src.llm_codegen.cli.CodeGenerator') as mock_generator, \\\n- patch('src.llm_codegen.cli.Checker') as mock_checker, \\\n- patch('pathlib.Path.exists') as mock_exists:\n- \n+\n+ with (\n+ patch(\"src.llm_codegen.cli.CodeGenerator\") as mock_generator,\n+ patch(\"src.llm_codegen.cli.Checker\") as mock_checker,\n+ patch(\"pathlib.Path.exists\") as mock_exists,\n+ ):\n+\n mock_exists.return_value = True\n mock_instance = Mock()\n mock_instance.run_full_check_and_fix = Mock(return_value=True)\n mock_checker.return_value = mock_instance\n mock_generator.return_value = Mock()\n- \n+\n test_issue = Path(\"test_bug.issue\")\n test_issue.write_text(\"name: Fix bug\\ndescription: Test bug\")\n- \n- result = runner.invoke(app, [\"fix\", str(test_issue), \"--output\", \"./test_output\"])\n- \n+\n+ result = runner.invoke(\n+ app, [\"fix\", str(test_issue), \"--output\", \"./test_output\"]\n+ )\n+\n test_issue.unlink()\n- \n+\n assert result.exit_code == 0\n assert \"修复失败\" not in result.stdout\n mock_checker.assert_called_once()\n mock_instance.run_full_check_and_fix.assert_called_once()\n \n \n def test_cli_help():\n \"\"\"测试 CLI 帮助命令\"\"\"\n from src.llm_codegen.cli import app\n- \n+\n result = runner.invoke(app, [\"--help\"])\n assert result.exit_code == 0\n assert \"基于LLM的自动化代码生成与维护工具\" in result.stdout\n- \n+\n # 测试子命令帮助\n result = runner.invoke(app, [\"init\", \"--help\"])\n assert result.exit_code == 0\n assert \"README.md 文件路径\" in result.stdout\n \n \n def test_cli_enhance_no_design():\n \"\"\"测试 enhance 命令当 design.json 不存在时失败\"\"\"\n from src.llm_codegen.cli import app\n- \n- with patch('pathlib.Path.exists') as mock_exists:\n+\n+ with patch(\"pathlib.Path.exists\") as mock_exists:\n mock_exists.return_value = False # 模拟 design.json 不存在\n- \n+\n test_issue = Path(\"test_feature.issue\")\n test_issue.write_text(\"name: Test\")\n- \n+\n result = runner.invoke(app, [\"enhance\", str(test_issue)])\n- \n+\n test_issue.unlink()\n- \n+\n assert result.exit_code != 0\n \n \n def test_cli_fix_no_design():\n \"\"\"测试 fix 命令当 design.json 不存在时失败\"\"\"\n from src.llm_codegen.cli import app\n- \n- with patch('pathlib.Path.exists') as mock_exists:\n+\n+ with patch(\"pathlib.Path.exists\") as mock_exists:\n mock_exists.return_value = False\n- \n+\n test_issue = Path(\"test_bug.issue\")\n test_issue.write_text(\"name: Test\")\n- \n+\n result = runner.invoke(app, [\"fix\", str(test_issue)])\n- \n+\n test_issue.unlink()\n- \n+\n assert result.exit_code != 0\n \n \n if __name__ == \"__main__\":\n pytest.main([__file__])\n",
"stderr": "would reformat tests/test_cli.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat tests/test_cli.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- tests/test_cli.py\t2026-03-17 14:27:38.333874+00:00\n+++ tests/test_cli.py\t2026-03-17 14:57:42.893165+00:00\n@@ -10,143 +10,153 @@\n \n \n def test_cli_init_success():\n \"\"\"测试 init 命令成功执行\"\"\"\n from src.llm_codegen.cli import app # 假设从项目根目录运行测试\n- \n+\n # 模拟 CodeGenerator 和其方法,避免实际调用 API\n- with patch('src.llm_codegen.cli.CodeGenerator') as mock_generator:\n+ with patch(\"src.llm_codegen.cli.CodeGenerator\") as mock_generator:\n mock_instance = Mock()\n mock_instance.run = Mock()\n mock_generator.return_value = mock_instance\n- \n+\n # 创建一个虚拟的 README 文件用于测试\n test_readme = Path(\"test_readme.md\")\n test_readme.write_text(\"# Test Project\\n\\nA test project for CLI.\")\n- \n- result = runner.invoke(app, [\"init\", str(test_readme), \"--output\", \"./test_output\"])\n- \n+\n+ result = runner.invoke(\n+ app, [\"init\", str(test_readme), \"--output\", \"./test_output\"]\n+ )\n+\n # 清理\n test_readme.unlink()\n- \n+\n assert result.exit_code == 0\n assert \"初始化失败\" not in result.stdout\n mock_generator.assert_called_once()\n mock_instance.run.assert_called_once_with(test_readme)\n \n \n def test_cli_init_failure_no_readme():\n \"\"\"测试 init 命令当 README 不存在时失败\"\"\"\n from src.llm_codegen.cli import app\n- \n+\n result = runner.invoke(app, [\"init\", \"nonexistent.md\"])\n- \n+\n assert result.exit_code != 0 # 应该退出码非零\n \n \n def test_cli_enhance_success():\n \"\"\"测试 enhance 命令成功执行(简化版,基于工单)\"\"\"\n from src.llm_codegen.cli import app\n- \n+\n # 模拟依赖文件和环境\n- with patch('src.llm_codegen.cli.CodeGenerator') as mock_generator, \\\n- patch('src.llm_codegen.cli.Checker') as mock_checker, \\\n- patch('pathlib.Path.exists') as mock_exists:\n- \n+ with (\n+ patch(\"src.llm_codegen.cli.CodeGenerator\") as mock_generator,\n+ patch(\"src.llm_codegen.cli.Checker\") as mock_checker,\n+ patch(\"pathlib.Path.exists\") as mock_exists,\n+ ):\n+\n mock_exists.return_value = True # 模拟 design.json 存在\n mock_instance = Mock()\n mock_instance.run_full_check_and_fix = Mock(return_value=True)\n mock_checker.return_value = mock_instance\n mock_generator.return_value = Mock()\n- \n+\n # 创建一个虚拟的工单文件\n test_issue = Path(\"test_feature.issue\")\n test_issue.write_text(\"name: Add feature\\ndescription: Test feature\")\n- \n- result = runner.invoke(app, [\"enhance\", str(test_issue), \"--output\", \"./test_output\"])\n- \n+\n+ result = runner.invoke(\n+ app, [\"enhance\", str(test_issue), \"--output\", \"./test_output\"]\n+ )\n+\n # 清理\n test_issue.unlink()\n- \n+\n assert result.exit_code == 0\n assert \"增强失败\" not in result.stdout\n mock_checker.assert_called_once()\n mock_instance.run_full_check_and_fix.assert_called_once()\n \n \n def test_cli_fix_success():\n \"\"\"测试 fix 命令成功执行(简化版,基于工单)\"\"\"\n from src.llm_codegen.cli import app\n- \n- with patch('src.llm_codegen.cli.CodeGenerator') as mock_generator, \\\n- patch('src.llm_codegen.cli.Checker') as mock_checker, \\\n- patch('pathlib.Path.exists') as mock_exists:\n- \n+\n+ with (\n+ patch(\"src.llm_codegen.cli.CodeGenerator\") as mock_generator,\n+ patch(\"src.llm_codegen.cli.Checker\") as mock_checker,\n+ patch(\"pathlib.Path.exists\") as mock_exists,\n+ ):\n+\n mock_exists.return_value = True\n mock_instance = Mock()\n mock_instance.run_full_check_and_fix = Mock(return_value=True)\n mock_checker.return_value = mock_instance\n mock_generator.return_value = Mock()\n- \n+\n test_issue = Path(\"test_bug.issue\")\n test_issue.write_text(\"name: Fix bug\\ndescription: Test bug\")\n- \n- result = runner.invoke(app, [\"fix\", str(test_issue), \"--output\", \"./test_output\"])\n- \n+\n+ result = runner.invoke(\n+ app, [\"fix\", str(test_issue), \"--output\", \"./test_output\"]\n+ )\n+\n test_issue.unlink()\n- \n+\n assert result.exit_code == 0\n assert \"修复失败\" not in result.stdout\n mock_checker.assert_called_once()\n mock_instance.run_full_check_and_fix.assert_called_once()\n \n \n def test_cli_help():\n \"\"\"测试 CLI 帮助命令\"\"\"\n from src.llm_codegen.cli import app\n- \n+\n result = runner.invoke(app, [\"--help\"])\n assert result.exit_code == 0\n assert \"基于LLM的自动化代码生成与维护工具\" in result.stdout\n- \n+\n # 测试子命令帮助\n result = runner.invoke(app, [\"init\", \"--help\"])\n assert result.exit_code == 0\n assert \"README.md 文件路径\" in result.stdout\n \n \n def test_cli_enhance_no_design():\n \"\"\"测试 enhance 命令当 design.json 不存在时失败\"\"\"\n from src.llm_codegen.cli import app\n- \n- with patch('pathlib.Path.exists') as mock_exists:\n+\n+ with patch(\"pathlib.Path.exists\") as mock_exists:\n mock_exists.return_value = False # 模拟 design.json 不存在\n- \n+\n test_issue = Path(\"test_feature.issue\")\n test_issue.write_text(\"name: Test\")\n- \n+\n result = runner.invoke(app, [\"enhance\", str(test_issue)])\n- \n+\n test_issue.unlink()\n- \n+\n assert result.exit_code != 0\n \n \n def test_cli_fix_no_design():\n \"\"\"测试 fix 命令当 design.json 不存在时失败\"\"\"\n from src.llm_codegen.cli import app\n- \n- with patch('pathlib.Path.exists') as mock_exists:\n+\n+ with patch(\"pathlib.Path.exists\") as mock_exists:\n mock_exists.return_value = False\n- \n+\n test_issue = Path(\"test_bug.issue\")\n test_issue.write_text(\"name: Test\")\n- \n+\n result = runner.invoke(app, [\"fix\", str(test_issue)])\n- \n+\n test_issue.unlink()\n- \n+\n assert result.exit_code != 0\n \n \n if __name__ == \"__main__\":\n pytest.main([__file__])"
]
},
{
"tool": "black",
"file": "tests/test_core.py",
"returncode": 1,
"stdout": "--- tests/test_core.py\t2026-03-17 14:27:38.333874+00:00\n+++ tests/test_core.py\t2026-03-17 14:57:42.956752+00:00\n@@ -9,31 +9,37 @@\n \n \n # ---------- Fake 类 ----------\n class FakeChatCompletion:\n \"\"\"模拟 OpenAI 的 chat.completions.create 返回值\"\"\"\n+\n def __init__(self, content):\n self.choices = [FakeChoice(FakeMessage(content))]\n+\n \n class FakeChoice:\n def __init__(self, message):\n self.message = message\n+\n \n class FakeMessage:\n def __init__(self, content):\n self.content = content\n self.reasoning_content = None\n \n \n class FakeOpenAIClient:\n \"\"\"假的 OpenAI 客户端,用于替换真实客户端\"\"\"\n+\n def __init__(self):\n self.chat = FakeChat()\n+\n \n class FakeChat:\n def __init__(self):\n self.completions = FakeCompletions()\n+\n \n class FakeCompletions:\n def __init__(self):\n self.create_called = False\n self.create_kwargs = None\n@@ -50,11 +56,13 @@\n # ---------- Fixtures ----------\n @pytest.fixture\n def fake_openai_client(monkeypatch):\n \"\"\"用假的 OpenAI 客户端替换真实的客户端\"\"\"\n fake_client = FakeOpenAIClient()\n- monkeypatch.setattr(\"src.llm_codegen.core.OpenAI\", lambda *args, **kwargs: fake_client)\n+ monkeypatch.setattr(\n+ \"src.llm_codegen.core.OpenAI\", lambda *args, **kwargs: fake_client\n+ )\n return fake_client\n \n \n @pytest.fixture\n def code_generator(tmp_path, monkeypatch, fake_openai_client):\n@@ -103,14 +111,16 @@\n \"project_name\": \"test-project\",\n \"version\": \"1.0.0\",\n \"description\": \"A test project\",\n \"files\": [],\n \"commands\": [],\n- \"check_tools\": []\n+ \"check_tools\": [],\n }\n \n- def fake_call_llm(system_prompt, user_prompt, temperature=0.2, expect_json=True):\n+ def fake_call_llm(\n+ system_prompt, user_prompt, temperature=0.2, expect_json=True\n+ ):\n return mock_response\n \n monkeypatch.setattr(code_generator, \"_call_llm\", fake_call_llm)\n \n design = code_generator.generate_design_json()\n@@ -122,11 +132,13 @@\n assert design_path.exists()\n with open(design_path) as f:\n saved = json.load(f)\n assert saved[\"project_name\"] == \"test-project\"\n \n- def test_generate_file_with_dependencies(self, code_generator, monkeypatch, tmp_path):\n+ def test_generate_file_with_dependencies(\n+ self, code_generator, monkeypatch, tmp_path\n+ ):\n \"\"\"测试生成文件,有依赖文件\"\"\"\n # 创建依赖文件\n dep_path = tmp_path / \"dep.py\"\n dep_path.write_text(\"# Dependency file\")\n code_generator.output_dir = tmp_path\n@@ -134,50 +146,62 @@\n \n # 模拟 _call_llm 的返回值\n llm_response = {\n \"code\": \"print('Hello, world!')\",\n \"description\": \"测试文件\",\n- \"commands\": []\n+ \"commands\": [],\n }\n \n- def fake_call_llm(system_prompt, user_prompt, temperature=0.2, expect_json=True):\n+ def fake_call_llm(\n+ system_prompt, user_prompt, temperature=0.2, expect_json=True\n+ ):\n return llm_response\n \n monkeypatch.setattr(code_generator, \"_call_llm\", fake_call_llm)\n \n code, desc, commands = code_generator.generate_file(\n file_path=\"test.py\",\n prompt_instruction=\"生成测试文件\",\n- dependency_files=[str(dep_path)]\n+ dependency_files=[str(dep_path)],\n )\n \n assert code == \"print('Hello, world!')\"\n assert desc == \"测试文件\"\n assert commands == []\n \n def test_execute_command_success(self, code_generator, monkeypatch):\n \"\"\"测试执行命令成功\"\"\"\n+\n def fake_run(cmd, *args, **kwargs):\n- return subprocess.CompletedProcess(args=cmd, returncode=0, stdout=\"\", stderr=\"\")\n+ return subprocess.CompletedProcess(\n+ args=cmd, returncode=0, stdout=\"\", stderr=\"\"\n+ )\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run)\n \n success = code_generator.execute_command(\"echo test\")\n assert success is True\n \n def test_execute_command_dangerous(self, code_generator, monkeypatch):\n \"\"\"测试阻止危险命令\"\"\"\n+\n def fake_dangerous(cmd):\n return (True, \"包含危险关键词\")\n+\n monkeypatch.setattr(\"src.llm_codegen.core.is_dangerous_command\", fake_dangerous)\n \n success = code_generator.execute_command(\"rm -rf /\")\n assert success is False\n \n def test_execute_command_failure(self, code_generator, monkeypatch):\n \"\"\"测试命令执行失败\"\"\"\n+\n def fake_run(cmd, *args, **kwargs):\n- return subprocess.CompletedProcess(args=cmd, returncode=1, stdout=\"\", stderr=\"\")\n+ return subprocess.CompletedProcess(\n+ args=cmd, returncode=1, stdout=\"\", stderr=\"\"\n+ )\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run)\n \n success = code_generator.execute_command(\"false\")\n assert success is False\n \n@@ -189,44 +213,65 @@\n \"current_file_index\": 1,\n \"generated_files\": [\"file1.py\"],\n \"dependencies_map\": {},\n \"total_files\": 3,\n \"output_dir\": str(tmp_path),\n- \"readme_path\": \"test\"\n+ \"readme_path\": \"test\",\n }\n state_file.write_text(json.dumps(state_data))\n \n # 创建设计文件\n design_path = tmp_path / \"design.json\"\n design_data = {\n \"project_name\": \"test\",\n \"version\": \"1.0.0\",\n \"description\": \"test\",\n \"files\": [\n- {\"path\": \"file1.py\", \"summary\": \"\", \"dependencies\": [], \"functions\": [], \"classes\": []},\n- {\"path\": \"file2.py\", \"summary\": \"\", \"dependencies\": [], \"functions\": [], \"classes\": []},\n- {\"path\": \"file3.py\", \"summary\": \"\", \"dependencies\": [], \"functions\": [], \"classes\": []}\n+ {\n+ \"path\": \"file1.py\",\n+ \"summary\": \"\",\n+ \"dependencies\": [],\n+ \"functions\": [],\n+ \"classes\": [],\n+ },\n+ {\n+ \"path\": \"file2.py\",\n+ \"summary\": \"\",\n+ \"dependencies\": [],\n+ \"functions\": [],\n+ \"classes\": [],\n+ },\n+ {\n+ \"path\": \"file3.py\",\n+ \"summary\": \"\",\n+ \"dependencies\": [],\n+ \"functions\": [],\n+ \"classes\": [],\n+ },\n ],\n \"commands\": [],\n- \"check_tools\": []\n+ \"check_tools\": [],\n }\n design_path.write_text(json.dumps(design_data))\n \n code_generator.output_dir = tmp_path\n code_generator.state_file = state_file\n \n # 模拟内部方法\n def fake_parse_readme(path):\n return \"# README\"\n+\n monkeypatch.setattr(code_generator, \"parse_readme\", fake_parse_readme)\n \n def fake_generate_file(file_path, prompt_instruction, dependency_files):\n return (\"code\", \"desc\", [])\n+\n monkeypatch.setattr(code_generator, \"generate_file\", fake_generate_file)\n \n def fake_execute_command(cmd, cwd=None):\n return True\n+\n monkeypatch.setattr(code_generator, \"execute_command\", fake_execute_command)\n \n # 运行,预期不抛出异常\n code_generator.run(Path(tmp_path / \"README.md\"))\n \n@@ -238,27 +283,35 @@\n code_generator.output_dir = tmp_path\n \n # 模拟 parse_readme\n def fake_parse_readme(path):\n return \"# README\"\n+\n monkeypatch.setattr(code_generator, \"parse_readme\", fake_parse_readme)\n \n # 模拟 generate_design_json 返回设计\n fake_design = DesignModel(\n project_name=\"test\",\n version=\"1.0.0\",\n description=\"test\",\n files=[], # 无文件,简化流程\n commands=[],\n- check_tools=[]\n+ check_tools=[],\n )\n+\n def fake_generate_design_json():\n return fake_design\n- monkeypatch.setattr(code_generator, \"generate_design_json\", fake_generate_design_json)\n+\n+ monkeypatch.setattr(\n+ code_generator, \"generate_design_json\", fake_generate_design_json\n+ )\n \n # 模拟 get_project_structure\n def fake_get_project_structure():\n return [], {}\n- monkeypatch.setattr(code_generator, \"get_project_structure\", fake_get_project_structure)\n+\n+ monkeypatch.setattr(\n+ code_generator, \"get_project_structure\", fake_get_project_structure\n+ )\n \n # 运行,预期不抛出异常\n code_generator.run(Path(tmp_path / \"README.md\"))\n",
"stderr": "would reformat tests/test_core.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat tests/test_core.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- tests/test_core.py\t2026-03-17 14:27:38.333874+00:00\n+++ tests/test_core.py\t2026-03-17 14:57:42.956752+00:00\n@@ -9,31 +9,37 @@\n \n \n # ---------- Fake 类 ----------\n class FakeChatCompletion:\n \"\"\"模拟 OpenAI 的 chat.completions.create 返回值\"\"\"\n+\n def __init__(self, content):\n self.choices = [FakeChoice(FakeMessage(content))]\n+\n \n class FakeChoice:\n def __init__(self, message):\n self.message = message\n+\n \n class FakeMessage:\n def __init__(self, content):\n self.content = content\n self.reasoning_content = None\n \n \n class FakeOpenAIClient:\n \"\"\"假的 OpenAI 客户端,用于替换真实客户端\"\"\"\n+\n def __init__(self):\n self.chat = FakeChat()\n+\n \n class FakeChat:\n def __init__(self):\n self.completions = FakeCompletions()\n+\n \n class FakeCompletions:\n def __init__(self):\n self.create_called = False\n self.create_kwargs = None\n@@ -50,11 +56,13 @@\n # ---------- Fixtures ----------\n @pytest.fixture\n def fake_openai_client(monkeypatch):\n \"\"\"用假的 OpenAI 客户端替换真实的客户端\"\"\"\n fake_client = FakeOpenAIClient()\n- monkeypatch.setattr(\"src.llm_codegen.core.OpenAI\", lambda *args, **kwargs: fake_client)\n+ monkeypatch.setattr(\n+ \"src.llm_codegen.core.OpenAI\", lambda *args, **kwargs: fake_client\n+ )\n return fake_client\n \n \n @pytest.fixture\n def code_generator(tmp_path, monkeypatch, fake_openai_client):\n@@ -103,14 +111,16 @@\n \"project_name\": \"test-project\",\n \"version\": \"1.0.0\",\n \"description\": \"A test project\",\n \"files\": [],\n \"commands\": [],\n- \"check_tools\": []\n+ \"check_tools\": [],\n }\n \n- def fake_call_llm(system_prompt, user_prompt, temperature=0.2, expect_json=True):\n+ def fake_call_llm(\n+ system_prompt, user_prompt, temperature=0.2, expect_json=True\n+ ):\n return mock_response\n \n monkeypatch.setattr(code_generator, \"_call_llm\", fake_call_llm)\n \n design = code_generator.generate_design_json()\n@@ -122,11 +132,13 @@\n assert design_path.exists()\n with open(design_path) as f:\n saved = json.load(f)\n assert saved[\"project_name\"] == \"test-project\"\n \n- def test_generate_file_with_dependencies(self, code_generator, monkeypatch, tmp_path):\n+ def test_generate_file_with_dependencies(\n+ self, code_generator, monkeypatch, tmp_path\n+ ):\n \"\"\"测试生成文件,有依赖文件\"\"\"\n # 创建依赖文件\n dep_path = tmp_path / \"dep.py\"\n dep_path.write_text(\"# Dependency file\")\n code_generator.output_dir = tmp_path\n@@ -134,50 +146,62 @@\n \n # 模拟 _call_llm 的返回值\n llm_response = {\n \"code\": \"print('Hello, world!')\",\n \"description\": \"测试文件\",\n- \"commands\": []\n+ \"commands\": [],\n }\n \n- def fake_call_llm(system_prompt, user_prompt, temperature=0.2, expect_json=True):\n+ def fake_call_llm(\n+ system_prompt, user_prompt, temperature=0.2, expect_json=True\n+ ):\n return llm_response\n \n monkeypatch.setattr(code_generator, \"_call_llm\", fake_call_llm)\n \n code, desc, commands = code_generator.generate_file(\n file_path=\"test.py\",\n prompt_instruction=\"生成测试文件\",\n- dependency_files=[str(dep_path)]\n+ dependency_files=[str(dep_path)],\n )\n \n assert code == \"print('Hello, world!')\"\n assert desc == \"测试文件\"\n assert commands == []\n \n def test_execute_command_success(self, code_generator, monkeypatch):\n \"\"\"测试执行命令成功\"\"\"\n+\n def fake_run(cmd, *args, **kwargs):\n- return subprocess.CompletedProcess(args=cmd, returncode=0, stdout=\"\", stderr=\"\")\n+ return subprocess.CompletedProcess(\n+ args=cmd, returncode=0, stdout=\"\", stderr=\"\"\n+ )\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run)\n \n success = code_generator.execute_command(\"echo test\")\n assert success is True\n \n def test_execute_command_dangerous(self, code_generator, monkeypatch):\n \"\"\"测试阻止危险命令\"\"\"\n+\n def fake_dangerous(cmd):\n return (True, \"包含危险关键词\")\n+\n monkeypatch.setattr(\"src.llm_codegen.core.is_dangerous_command\", fake_dangerous)\n \n success = code_generator.execute_command(\"rm -rf /\")\n assert success is False\n \n def test_execute_command_failure(self, code_generator, monkeypatch):\n \"\"\"测试命令执行失败\"\"\"\n+\n def fake_run(cmd, *args, **kwargs):\n- return subprocess.CompletedProcess(args=cmd, returncode=1, stdout=\"\", stderr=\"\")\n+ return subprocess.CompletedProcess(\n+ args=cmd, returncode=1, stdout=\"\", stderr=\"\"\n+ )\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run)\n \n success = code_generator.execute_command(\"false\")\n assert success is False\n \n@@ -189,44 +213,65 @@\n \"current_file_index\": 1,\n \"generated_files\": [\"file1.py\"],\n \"dependencies_map\": {},\n \"total_files\": 3,\n \"output_dir\": str(tmp_path),\n- \"readme_path\": \"test\"\n+ \"readme_path\": \"test\",\n }\n state_file.write_text(json.dumps(state_data))\n \n # 创建设计文件\n design_path = tmp_path / \"design.json\"\n design_data = {\n \"project_name\": \"test\",\n \"version\": \"1.0.0\",\n \"description\": \"test\",\n \"files\": [\n- {\"path\": \"file1.py\", \"summary\": \"\", \"dependencies\": [], \"functions\": [], \"classes\": []},\n- {\"path\": \"file2.py\", \"summary\": \"\", \"dependencies\": [], \"functions\": [], \"classes\": []},\n- {\"path\": \"file3.py\", \"summary\": \"\", \"dependencies\": [], \"functions\": [], \"classes\": []}\n+ {\n+ \"path\": \"file1.py\",\n+ \"summary\": \"\",\n+ \"dependencies\": [],\n+ \"functions\": [],\n+ \"classes\": [],\n+ },\n+ {\n+ \"path\": \"file2.py\",\n+ \"summary\": \"\",\n+ \"dependencies\": [],\n+ \"functions\": [],\n+ \"classes\": [],\n+ },\n+ {\n+ \"path\": \"file3.py\",\n+ \"summary\": \"\",\n+ \"dependencies\": [],\n+ \"functions\": [],\n+ \"classes\": [],\n+ },\n ],\n \"commands\": [],\n- \"check_tools\": []\n+ \"check_tools\": [],\n }\n design_path.write_text(json.dumps(design_data))\n \n code_generator.output_dir = tmp_path\n code_generator.state_file = state_file\n \n # 模拟内部方法\n def fake_parse_readme(path):\n return \"# README\"\n+\n monkeypatch.setattr(code_generator, \"parse_readme\", fake_parse_readme)\n \n def fake_generate_file(file_path, prompt_instruction, dependency_files):\n return (\"code\", \"desc\", [])\n+\n monkeypatch.setattr(code_generator, \"generate_file\", fake_generate_file)\n \n def fake_execute_command(cmd, cwd=None):\n return True\n+\n monkeypatch.setattr(code_generator, \"execute_command\", fake_execute_command)\n \n # 运行,预期不抛出异常\n code_generator.run(Path(tmp_path / \"README.md\"))\n \n@@ -238,27 +283,35 @@\n code_generator.output_dir = tmp_path\n \n # 模拟 parse_readme\n def fake_parse_readme(path):\n return \"# README\"\n+\n monkeypatch.setattr(code_generator, \"parse_readme\", fake_parse_readme)\n \n # 模拟 generate_design_json 返回设计\n fake_design = DesignModel(\n project_name=\"test\",\n version=\"1.0.0\",\n description=\"test\",\n files=[], # 无文件,简化流程\n commands=[],\n- check_tools=[]\n+ check_tools=[],\n )\n+\n def fake_generate_design_json():\n return fake_design\n- monkeypatch.setattr(code_generator, \"generate_design_json\", fake_generate_design_json)\n+\n+ monkeypatch.setattr(\n+ code_generator, \"generate_design_json\", fake_generate_design_json\n+ )\n \n # 模拟 get_project_structure\n def fake_get_project_structure():\n return [], {}\n- monkeypatch.setattr(code_generator, \"get_project_structure\", fake_get_project_structure)\n+\n+ monkeypatch.setattr(\n+ code_generator, \"get_project_structure\", fake_get_project_structure\n+ )\n \n # 运行,预期不抛出异常\n code_generator.run(Path(tmp_path / \"README.md\"))"
]
},
{
"tool": "black",
"file": "llmcodegen.py",
"returncode": 1,
"stdout": "--- llmcodegen.py\t2026-03-17 14:27:38.333874+00:00\n+++ llmcodegen.py\t2026-03-17 14:57:43.010113+00:00\n@@ -22,10 +22,11 @@\n DANGEROUS_COMMANDS = [\"rm\", \"sudo\", \"chmod\", \"dd\", \"mkfs\", \"> /dev/sda\", \"format\"]\n ALLOWED_COMMANDS = [] # 可设置白名单,为空则只检查黑名单\n \n app = typer.Typer(help=\"基于LLM的自动化代码生成工具\")\n console = Console()\n+\n \n # ==================== 工具函数 ====================\n def is_dangerous_command(cmd: str) -> Tuple[bool, str]:\n \"\"\"\n 判断命令是否危险\n@@ -35,10 +36,11 @@\n for danger in DANGEROUS_COMMANDS:\n if danger in cmd_lower:\n return True, f\"包含危险关键词 '{danger}'\"\n return False, \"\"\n \n+\n # ==================== 核心类 ====================\n class CodeGenerator:\n \"\"\"代码生成器,封装所有逻辑\"\"\"\n \n def __init__(\n@@ -136,13 +138,15 @@\n logger.info(f\"读取README文件: {readme_path}\")\n try:\n with open(readme_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n logger.debug(f\"README内容长度: {len(content)} 字符\")\n- if (readme_path.parent / 'design.json').exists():\n- with open((readme_path.parent / 'design.json')) as f:\n- content += f'\\n\\ndesign.json(包含项目设计有关信息)内容如下:{f.read()}'\n+ if (readme_path.parent / \"design.json\").exists():\n+ with open((readme_path.parent / \"design.json\")) as f:\n+ content += (\n+ f\"\\n\\ndesign.json(包含项目设计有关信息)内容如下:{f.read()}\"\n+ )\n return content\n except Exception as e:\n logger.error(f\"读取README失败: {e}\")\n raise\n \n@@ -204,11 +208,13 @@\n else:\n logger.warning(FileNotFoundError(f\"依赖文件不存在: {dep}\"))\n \n with open(dep_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n- context_content.append(f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\")\n+ context_content.append(\n+ f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\"\n+ )\n \n full_context = \"\\n\".join(context_content)\n \n system_prompt = (\n \"你是一个专业的编程助手。根据用户指令和提供的上下文文件,生成完整的代码。\"\n@@ -257,11 +263,10 @@\n except subprocess.TimeoutExpired:\n logger.error(f\"命令执行超时: {cmd}\")\n except Exception as e:\n logger.error(f\"命令执行失败: {e}\")\n \n-\n def run(self, readme_path: Path):\n \"\"\"\n 主执行流程\n \"\"\"\n logger.info(\"=\" * 50)\n@@ -300,11 +305,13 @@\n try:\n # 获取依赖文件\n deps = dependencies.get(file, [])\n \n # 构造生成指令\n- instruction = f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ instruction = (\n+ f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ )\n \n # 调用LLM生成代码\n code, desc, commands = self.generate_file(file, instruction, deps)\n \n logger.info(f\"生成完成: {file} - {desc}\")\n@@ -328,33 +335,47 @@\n progress.remove_task(file_task)\n progress.update(total_task, advance=1)\n \n logger.success(\"所有文件处理完成!\")\n \n+\n # ==================== CLI入口 ====================\n @app.command()\n def main(\n- readme: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"README.md文件路径\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"输出根目录默认为readme所在目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥也可通过环境变量DEEPSEEK_APIKEY设置\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ readme: Path = typer.Argument(\n+ ..., exists=True, file_okay=True, dir_okay=False, help=\"README.md文件路径\"\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"输出根目录默认为readme所在目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None,\n+ \"--api-key\",\n+ envvar=\"DEEPSEEK_APIKEY\",\n+ help=\"API密钥也可通过环境变量DEEPSEEK_APIKEY设置\",\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n- log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径默认输出目录下generator.log\"),\n+ log_file: Optional[str] = typer.Option(\n+ None, \"--log\", help=\"日志文件路径默认输出目录下generator.log\"\n+ ),\n ):\n \"\"\"\n 根据README自动生成项目代码\n \"\"\"\n if output_dir is None:\n output_dir = readme.parent\n \n generator = CodeGenerator(\n- api_key=api_key,\n- base_url=base_url,\n- model=model,\n- output_dir=output_dir,\n- log_file=log_file,\n- )\n+ api_key=api_key,\n+ base_url=base_url,\n+ model=model,\n+ output_dir=output_dir,\n+ log_file=log_file,\n+ )\n generator.run(readme)\n \n \n if __name__ == \"__main__\":\n app()\n",
"stderr": "would reformat llmcodegen.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat llmcodegen.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- llmcodegen.py\t2026-03-17 14:27:38.333874+00:00\n+++ llmcodegen.py\t2026-03-17 14:57:43.010113+00:00\n@@ -22,10 +22,11 @@\n DANGEROUS_COMMANDS = [\"rm\", \"sudo\", \"chmod\", \"dd\", \"mkfs\", \"> /dev/sda\", \"format\"]\n ALLOWED_COMMANDS = [] # 可设置白名单,为空则只检查黑名单\n \n app = typer.Typer(help=\"基于LLM的自动化代码生成工具\")\n console = Console()\n+\n \n # ==================== 工具函数 ====================\n def is_dangerous_command(cmd: str) -> Tuple[bool, str]:\n \"\"\"\n 判断命令是否危险\n@@ -35,10 +36,11 @@\n for danger in DANGEROUS_COMMANDS:\n if danger in cmd_lower:\n return True, f\"包含危险关键词 '{danger}'\"\n return False, \"\"\n \n+\n # ==================== 核心类 ====================\n class CodeGenerator:\n \"\"\"代码生成器,封装所有逻辑\"\"\"\n \n def __init__(\n@@ -136,13 +138,15 @@\n logger.info(f\"读取README文件: {readme_path}\")\n try:\n with open(readme_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n logger.debug(f\"README内容长度: {len(content)} 字符\")\n- if (readme_path.parent / 'design.json').exists():\n- with open((readme_path.parent / 'design.json')) as f:\n- content += f'\\n\\ndesign.json(包含项目设计有关信息)内容如下:{f.read()}'\n+ if (readme_path.parent / \"design.json\").exists():\n+ with open((readme_path.parent / \"design.json\")) as f:\n+ content += (\n+ f\"\\n\\ndesign.json(包含项目设计有关信息)内容如下:{f.read()}\"\n+ )\n return content\n except Exception as e:\n logger.error(f\"读取README失败: {e}\")\n raise\n \n@@ -204,11 +208,13 @@\n else:\n logger.warning(FileNotFoundError(f\"依赖文件不存在: {dep}\"))\n \n with open(dep_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n- context_content.append(f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\")\n+ context_content.append(\n+ f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\"\n+ )\n \n full_context = \"\\n\".join(context_content)\n \n system_prompt = (\n \"你是一个专业的编程助手。根据用户指令和提供的上下文文件,生成完整的代码。\"\n@@ -257,11 +263,10 @@\n except subprocess.TimeoutExpired:\n logger.error(f\"命令执行超时: {cmd}\")\n except Exception as e:\n logger.error(f\"命令执行失败: {e}\")\n \n-\n def run(self, readme_path: Path):\n \"\"\"\n 主执行流程\n \"\"\"\n logger.info(\"=\" * 50)\n@@ -300,11 +305,13 @@\n try:\n # 获取依赖文件\n deps = dependencies.get(file, [])\n \n # 构造生成指令\n- instruction = f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ instruction = (\n+ f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ )\n \n # 调用LLM生成代码\n code, desc, commands = self.generate_file(file, instruction, deps)\n \n logger.info(f\"生成完成: {file} - {desc}\")\n@@ -328,33 +335,47 @@\n progress.remove_task(file_task)\n progress.update(total_task, advance=1)\n \n logger.success(\"所有文件处理完成!\")\n \n+\n # ==================== CLI入口 ====================\n @app.command()\n def main(\n- readme: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"README.md文件路径\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"输出根目录默认为readme所在目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥也可通过环境变量DEEPSEEK_APIKEY设置\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ readme: Path = typer.Argument(\n+ ..., exists=True, file_okay=True, dir_okay=False, help=\"README.md文件路径\"\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"输出根目录默认为readme所在目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None,\n+ \"--api-key\",\n+ envvar=\"DEEPSEEK_APIKEY\",\n+ help=\"API密钥也可通过环境变量DEEPSEEK_APIKEY设置\",\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n- log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径默认输出目录下generator.log\"),\n+ log_file: Optional[str] = typer.Option(\n+ None, \"--log\", help=\"日志文件路径默认输出目录下generator.log\"\n+ ),\n ):\n \"\"\"\n 根据README自动生成项目代码\n \"\"\"\n if output_dir is None:\n output_dir = readme.parent\n \n generator = CodeGenerator(\n- api_key=api_key,\n- base_url=base_url,\n- model=model,\n- output_dir=output_dir,\n- log_file=log_file,\n- )\n+ api_key=api_key,\n+ base_url=base_url,\n+ model=model,\n+ output_dir=output_dir,\n+ log_file=log_file,\n+ )\n generator.run(readme)\n \n \n if __name__ == \"__main__\":\n app()"
]
},
{
"tool": "black",
"file": "src/llm_codegen/models.py",
"returncode": 1,
"stdout": "--- src/llm_codegen/models.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/models.py\t2026-03-17 14:57:43.085000+00:00\n@@ -3,34 +3,38 @@\n \n \n # 模型用于 design.json 结构\n class FunctionModel(BaseModel):\n \"\"\"函数模型,对应 design.json 中的 functions 字段。\"\"\"\n+\n name: str\n summary: str\n inputs: List[str]\n outputs: List[str]\n \n \n class ClassModel(BaseModel):\n \"\"\"类模型,对应 design.json 中的 classes 字段。\"\"\"\n+\n name: str\n summary: str\n methods: List[str]\n \n \n class FileModel(BaseModel):\n \"\"\"文件模型,对应 design.json 中的 files 字段。\"\"\"\n+\n path: str\n summary: str\n dependencies: List[str] = Field(default_factory=list)\n functions: List[FunctionModel] = Field(default_factory=list)\n classes: List[ClassModel] = Field(default_factory=list)\n \n \n class DesignModel(BaseModel):\n \"\"\"设计模型,对应 design.json 的根结构。\"\"\"\n+\n project_name: str\n version: str\n description: str\n files: List[FileModel]\n commands: List[str] = Field(default_factory=list)\n@@ -38,18 +42,20 @@\n \n \n # 模型用于工单\n class FeatureIssue(BaseModel):\n \"\"\"需求工单模型,基于 README 中的模板。\"\"\"\n+\n name: str\n description: str\n affected_files: Optional[List[str]] = Field(default_factory=list)\n acceptance_criteria: List[str]\n \n \n class BugIssue(BaseModel):\n \"\"\"Bug 工单模型,基于 README 中的模板。\"\"\"\n+\n name: str\n description: str\n steps_to_reproduce: List[str]\n expected_behavior: str\n actual_behavior: str\n@@ -57,10 +63,11 @@\n \n \n # 模型用于断点续写状态\n class StateModel(BaseModel):\n \"\"\"状态模型,用于保存生成过程中的断点状态。\"\"\"\n+\n current_file_index: int = 0\n generated_files: List[str] = Field(default_factory=list)\n dependencies_map: Dict[str, List[str]] = Field(default_factory=dict)\n total_files: int\n output_dir: str\n@@ -68,8 +75,9 @@\n \n \n # 可选:通用响应模型,用于 LLM 调用\n class LLMResponse(BaseModel):\n \"\"\"LLM 响应模型,用于解析 generate_file 方法的返回。\"\"\"\n+\n code: str\n description: str\n commands: List[str] = Field(default_factory=list)\n",
"stderr": "would reformat src/llm_codegen/models.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat src/llm_codegen/models.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- src/llm_codegen/models.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/models.py\t2026-03-17 14:57:43.085000+00:00\n@@ -3,34 +3,38 @@\n \n \n # 模型用于 design.json 结构\n class FunctionModel(BaseModel):\n \"\"\"函数模型,对应 design.json 中的 functions 字段。\"\"\"\n+\n name: str\n summary: str\n inputs: List[str]\n outputs: List[str]\n \n \n class ClassModel(BaseModel):\n \"\"\"类模型,对应 design.json 中的 classes 字段。\"\"\"\n+\n name: str\n summary: str\n methods: List[str]\n \n \n class FileModel(BaseModel):\n \"\"\"文件模型,对应 design.json 中的 files 字段。\"\"\"\n+\n path: str\n summary: str\n dependencies: List[str] = Field(default_factory=list)\n functions: List[FunctionModel] = Field(default_factory=list)\n classes: List[ClassModel] = Field(default_factory=list)\n \n \n class DesignModel(BaseModel):\n \"\"\"设计模型,对应 design.json 的根结构。\"\"\"\n+\n project_name: str\n version: str\n description: str\n files: List[FileModel]\n commands: List[str] = Field(default_factory=list)\n@@ -38,18 +42,20 @@\n \n \n # 模型用于工单\n class FeatureIssue(BaseModel):\n \"\"\"需求工单模型,基于 README 中的模板。\"\"\"\n+\n name: str\n description: str\n affected_files: Optional[List[str]] = Field(default_factory=list)\n acceptance_criteria: List[str]\n \n \n class BugIssue(BaseModel):\n \"\"\"Bug 工单模型,基于 README 中的模板。\"\"\"\n+\n name: str\n description: str\n steps_to_reproduce: List[str]\n expected_behavior: str\n actual_behavior: str\n@@ -57,10 +63,11 @@\n \n \n # 模型用于断点续写状态\n class StateModel(BaseModel):\n \"\"\"状态模型,用于保存生成过程中的断点状态。\"\"\"\n+\n current_file_index: int = 0\n generated_files: List[str] = Field(default_factory=list)\n dependencies_map: Dict[str, List[str]] = Field(default_factory=dict)\n total_files: int\n output_dir: str\n@@ -68,8 +75,9 @@\n \n \n # 可选:通用响应模型,用于 LLM 调用\n class LLMResponse(BaseModel):\n \"\"\"LLM 响应模型,用于解析 generate_file 方法的返回。\"\"\"\n+\n code: str\n description: str\n commands: List[str] = Field(default_factory=list)"
]
},
{
"tool": "black",
"file": "tests/test_checker.py",
"returncode": 1,
"stdout": "--- tests/test_checker.py\t2026-03-17 14:57:24.156832+00:00\n+++ tests/test_checker.py\t2026-03-17 14:57:43.083978+00:00\n@@ -9,10 +9,11 @@\n \n \n # ---------- Fake 对象 ----------\n class FakeCodeGenerator:\n \"\"\"假的 CodeGenerator用于替代真实的 LLM 调用\"\"\"\n+\n def __init__(self, return_value=None):\n self._call_llm_called = False\n self._call_llm_args = None\n self.return_value = return_value or {\"patches\": [], \"description\": \"模拟修复\"}\n \n@@ -56,15 +57,13 @@\n file_path = Path(\"test_file.py\")\n \n # 模拟 subprocess.run 返回成功\n def fake_run(cmd, *args, **kwargs):\n return subprocess.CompletedProcess(\n- args=cmd,\n- returncode=0,\n- stdout=\"\",\n- stderr=\"\"\n+ args=cmd, returncode=0, stdout=\"\", stderr=\"\"\n )\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run)\n \n result = checker.run_check(\"pylint\", file_path)\n \n assert result[\"tool\"] == \"pylint\"\n@@ -77,10 +76,11 @@\n file_path = Path(\"test_file.py\")\n \n # 让 subprocess.run 抛出超时异常\n def fake_run_timeout(*args, **kwargs):\n raise subprocess.TimeoutExpired(cmd=\"pylint\", timeout=60)\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run_timeout)\n \n result = checker.run_check(\"pylint\", file_path)\n \n assert result[\"returncode\"] == -1\n@@ -91,19 +91,42 @@\n test_file = tmp_path / \"test.py\"\n test_file.write_text(\"print('hello')\\n\")\n \n # 替换 run_check 方法,避免真正执行\n fake_results = [\n- {\"tool\": \"pylint\", \"file\": str(test_file), \"returncode\": 0, \"stdout\": \"\", \"stderr\": \"\", \"errors\": []},\n- {\"tool\": \"mypy\", \"file\": str(test_file), \"returncode\": 0, \"stdout\": \"\", \"stderr\": \"\", \"errors\": []},\n- {\"tool\": \"black\", \"file\": str(test_file), \"returncode\": 0, \"stdout\": \"\", \"stderr\": \"\", \"errors\": []}\n+ {\n+ \"tool\": \"pylint\",\n+ \"file\": str(test_file),\n+ \"returncode\": 0,\n+ \"stdout\": \"\",\n+ \"stderr\": \"\",\n+ \"errors\": [],\n+ },\n+ {\n+ \"tool\": \"mypy\",\n+ \"file\": str(test_file),\n+ \"returncode\": 0,\n+ \"stdout\": \"\",\n+ \"stderr\": \"\",\n+ \"errors\": [],\n+ },\n+ {\n+ \"tool\": \"black\",\n+ \"file\": str(test_file),\n+ \"returncode\": 0,\n+ \"stdout\": \"\",\n+ \"stderr\": \"\",\n+ \"errors\": [],\n+ },\n ]\n call_count = 0\n+\n def fake_run_check(tool, file):\n nonlocal call_count\n call_count += 1\n return fake_results[call_count - 1]\n+\n monkeypatch.setattr(checker, \"run_check\", fake_run_check)\n \n results = checker.run_parallel_checks([test_file])\n \n assert len(results) == 1\n@@ -115,11 +138,11 @@\n results = [{\"tool\": \"pylint\", \"file\": \"file1.py\", \"returncode\": 0}]\n checker.save_results(results)\n \n results_file = checker.output_dir / \"check_results.json\"\n assert results_file.exists()\n- with open(results_file, 'r') as f:\n+ with open(results_file, \"r\") as f:\n loaded = json.load(f)\n assert loaded == results\n \n def test_collect_errors(self, checker, tmp_path):\n \"\"\"测试收集错误\"\"\"\n@@ -171,11 +194,11 @@\n checker.code_generator.return_value = fake_return\n \n success = checker.auto_fix(errors, context_files=[\"test.py\"])\n \n assert success is True\n- with open(test_file, 'r') as f:\n+ with open(test_file, \"r\") as f:\n assert f.read() == \"print('hi')\\n\"\n assert checker.code_generator._call_llm_called is True\n \n def test_auto_fix_no_errors(self, checker):\n \"\"\"测试自动修复无错误时\"\"\"\n",
"stderr": "would reformat tests/test_checker.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat tests/test_checker.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- tests/test_checker.py\t2026-03-17 14:57:24.156832+00:00\n+++ tests/test_checker.py\t2026-03-17 14:57:43.083978+00:00\n@@ -9,10 +9,11 @@\n \n \n # ---------- Fake 对象 ----------\n class FakeCodeGenerator:\n \"\"\"假的 CodeGenerator用于替代真实的 LLM 调用\"\"\"\n+\n def __init__(self, return_value=None):\n self._call_llm_called = False\n self._call_llm_args = None\n self.return_value = return_value or {\"patches\": [], \"description\": \"模拟修复\"}\n \n@@ -56,15 +57,13 @@\n file_path = Path(\"test_file.py\")\n \n # 模拟 subprocess.run 返回成功\n def fake_run(cmd, *args, **kwargs):\n return subprocess.CompletedProcess(\n- args=cmd,\n- returncode=0,\n- stdout=\"\",\n- stderr=\"\"\n+ args=cmd, returncode=0, stdout=\"\", stderr=\"\"\n )\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run)\n \n result = checker.run_check(\"pylint\", file_path)\n \n assert result[\"tool\"] == \"pylint\"\n@@ -77,10 +76,11 @@\n file_path = Path(\"test_file.py\")\n \n # 让 subprocess.run 抛出超时异常\n def fake_run_timeout(*args, **kwargs):\n raise subprocess.TimeoutExpired(cmd=\"pylint\", timeout=60)\n+\n monkeypatch.setattr(subprocess, \"run\", fake_run_timeout)\n \n result = checker.run_check(\"pylint\", file_path)\n \n assert result[\"returncode\"] == -1\n@@ -91,19 +91,42 @@\n test_file = tmp_path / \"test.py\"\n test_file.write_text(\"print('hello')\\n\")\n \n # 替换 run_check 方法,避免真正执行\n fake_results = [\n- {\"tool\": \"pylint\", \"file\": str(test_file), \"returncode\": 0, \"stdout\": \"\", \"stderr\": \"\", \"errors\": []},\n- {\"tool\": \"mypy\", \"file\": str(test_file), \"returncode\": 0, \"stdout\": \"\", \"stderr\": \"\", \"errors\": []},\n- {\"tool\": \"black\", \"file\": str(test_file), \"returncode\": 0, \"stdout\": \"\", \"stderr\": \"\", \"errors\": []}\n+ {\n+ \"tool\": \"pylint\",\n+ \"file\": str(test_file),\n+ \"returncode\": 0,\n+ \"stdout\": \"\",\n+ \"stderr\": \"\",\n+ \"errors\": [],\n+ },\n+ {\n+ \"tool\": \"mypy\",\n+ \"file\": str(test_file),\n+ \"returncode\": 0,\n+ \"stdout\": \"\",\n+ \"stderr\": \"\",\n+ \"errors\": [],\n+ },\n+ {\n+ \"tool\": \"black\",\n+ \"file\": str(test_file),\n+ \"returncode\": 0,\n+ \"stdout\": \"\",\n+ \"stderr\": \"\",\n+ \"errors\": [],\n+ },\n ]\n call_count = 0\n+\n def fake_run_check(tool, file):\n nonlocal call_count\n call_count += 1\n return fake_results[call_count - 1]\n+\n monkeypatch.setattr(checker, \"run_check\", fake_run_check)\n \n results = checker.run_parallel_checks([test_file])\n \n assert len(results) == 1\n@@ -115,11 +138,11 @@\n results = [{\"tool\": \"pylint\", \"file\": \"file1.py\", \"returncode\": 0}]\n checker.save_results(results)\n \n results_file = checker.output_dir / \"check_results.json\"\n assert results_file.exists()\n- with open(results_file, 'r') as f:\n+ with open(results_file, \"r\") as f:\n loaded = json.load(f)\n assert loaded == results\n \n def test_collect_errors(self, checker, tmp_path):\n \"\"\"测试收集错误\"\"\"\n@@ -171,11 +194,11 @@\n checker.code_generator.return_value = fake_return\n \n success = checker.auto_fix(errors, context_files=[\"test.py\"])\n \n assert success is True\n- with open(test_file, 'r') as f:\n+ with open(test_file, \"r\") as f:\n assert f.read() == \"print('hi')\\n\"\n assert checker.code_generator._call_llm_called is True\n \n def test_auto_fix_no_errors(self, checker):\n \"\"\"测试自动修复无错误时\"\"\""
]
},
{
"tool": "black",
"file": "src/llm_codegen/__init__.py",
"returncode": 1,
"stdout": "--- src/llm_codegen/__init__.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/__init__.py\t2026-03-17 14:57:43.116906+00:00\n@@ -8,8 +8,9 @@\n __version__ = \"1.0.0\"\n __description__ = \"一个基于大语言模型的智能代码生成与维护工具\"\n \n # 导出核心模块以便从包级别导入\n from .core import CodeGenerator\n+\n # from .cli import main\n \n __all__ = [\"CodeGenerator\", \"__version__\", \"__description__\"]\n",
"stderr": "would reformat src/llm_codegen/__init__.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat src/llm_codegen/__init__.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- src/llm_codegen/__init__.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/__init__.py\t2026-03-17 14:57:43.116906+00:00\n@@ -8,8 +8,9 @@\n __version__ = \"1.0.0\"\n __description__ = \"一个基于大语言模型的智能代码生成与维护工具\"\n \n # 导出核心模块以便从包级别导入\n from .core import CodeGenerator\n+\n # from .cli import main\n \n __all__ = [\"CodeGenerator\", \"__version__\", \"__description__\"]"
]
},
{
"tool": "black",
"file": "src/llm_codegen/utils.py",
"returncode": 1,
"stdout": "--- src/llm_codegen/utils.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/utils.py\t2026-03-17 14:57:43.297071+00:00\n@@ -8,14 +8,14 @@\n \n \n def is_dangerous_command(cmd: str) -> Tuple[bool, str]:\n \"\"\"\n 判断命令是否危险\n- \n+\n Args:\n cmd: 命令字符串\n- \n+\n Returns:\n Tuple[bool, str]: (是否危险, 原因)\n \"\"\"\n cmd_lower = cmd.lower()\n for danger in DANGEROUS_COMMANDS:\n@@ -25,59 +25,59 @@\n \n \n def read_file(file_path: str) -> str:\n \"\"\"\n 读取文件内容\n- \n+\n Args:\n file_path: 文件路径\n- \n+\n Returns:\n str: 文件内容\n \"\"\"\n try:\n- with open(file_path, 'r', encoding='utf-8') as f:\n+ with open(file_path, \"r\", encoding=\"utf-8\") as f:\n return f.read()\n except Exception as e:\n raise IOError(f\"读取文件失败: {file_path}, 错误: {e}\")\n \n \n def write_file(file_path: str, content: str) -> None:\n \"\"\"\n 写入文件内容\n- \n+\n Args:\n file_path: 文件路径\n content: 要写入的内容\n \"\"\"\n try:\n path = Path(file_path)\n path.parent.mkdir(parents=True, exist_ok=True)\n- with open(file_path, 'w', encoding='utf-8') as f:\n+ with open(file_path, \"w\", encoding=\"utf-8\") as f:\n f.write(content)\n except Exception as e:\n raise IOError(f\"写入文件失败: {file_path}, 错误: {e}\")\n \n \n def ensure_dir(directory: str) -> None:\n \"\"\"\n 确保目录存在,如果不存在则创建\n- \n+\n Args:\n directory: 目录路径\n \"\"\"\n os.makedirs(directory, exist_ok=True)\n \n \n def safe_join(base_path: str, *paths: str) -> str:\n \"\"\"\n 安全地拼接路径,防止目录遍历攻击\n- \n+\n Args:\n base_path: 基础路径\n *paths: 要拼接的部分\n- \n+\n Returns:\n str: 拼接后的绝对路径\n \"\"\"\n full_path = os.path.abspath(os.path.join(base_path, *paths))\n base_abs = os.path.abspath(base_path)\n",
"stderr": "would reformat src/llm_codegen/utils.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat src/llm_codegen/utils.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- src/llm_codegen/utils.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/utils.py\t2026-03-17 14:57:43.297071+00:00\n@@ -8,14 +8,14 @@\n \n \n def is_dangerous_command(cmd: str) -> Tuple[bool, str]:\n \"\"\"\n 判断命令是否危险\n- \n+\n Args:\n cmd: 命令字符串\n- \n+\n Returns:\n Tuple[bool, str]: (是否危险, 原因)\n \"\"\"\n cmd_lower = cmd.lower()\n for danger in DANGEROUS_COMMANDS:\n@@ -25,59 +25,59 @@\n \n \n def read_file(file_path: str) -> str:\n \"\"\"\n 读取文件内容\n- \n+\n Args:\n file_path: 文件路径\n- \n+\n Returns:\n str: 文件内容\n \"\"\"\n try:\n- with open(file_path, 'r', encoding='utf-8') as f:\n+ with open(file_path, \"r\", encoding=\"utf-8\") as f:\n return f.read()\n except Exception as e:\n raise IOError(f\"读取文件失败: {file_path}, 错误: {e}\")\n \n \n def write_file(file_path: str, content: str) -> None:\n \"\"\"\n 写入文件内容\n- \n+\n Args:\n file_path: 文件路径\n content: 要写入的内容\n \"\"\"\n try:\n path = Path(file_path)\n path.parent.mkdir(parents=True, exist_ok=True)\n- with open(file_path, 'w', encoding='utf-8') as f:\n+ with open(file_path, \"w\", encoding=\"utf-8\") as f:\n f.write(content)\n except Exception as e:\n raise IOError(f\"写入文件失败: {file_path}, 错误: {e}\")\n \n \n def ensure_dir(directory: str) -> None:\n \"\"\"\n 确保目录存在,如果不存在则创建\n- \n+\n Args:\n directory: 目录路径\n \"\"\"\n os.makedirs(directory, exist_ok=True)\n \n \n def safe_join(base_path: str, *paths: str) -> str:\n \"\"\"\n 安全地拼接路径,防止目录遍历攻击\n- \n+\n Args:\n base_path: 基础路径\n *paths: 要拼接的部分\n- \n+\n Returns:\n str: 拼接后的绝对路径\n \"\"\"\n full_path = os.path.abspath(os.path.join(base_path, *paths))\n base_abs = os.path.abspath(base_path)"
]
},
{
"tool": "black",
"file": "src/llm_codegen/cli.py",
"returncode": 1,
"stdout": "--- src/llm_codegen/cli.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/cli.py\t2026-03-17 14:57:43.351695+00:00\n@@ -19,23 +19,31 @@\n console = Console()\n \n \n @app.command()\n def init(\n- readme: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"README.md 文件路径\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"输出根目录,默认为当前目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ readme: Path = typer.Argument(\n+ ..., exists=True, file_okay=True, dir_okay=False, help=\"README.md 文件路径\"\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"输出根目录,默认为当前目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径\"),\n ):\n \"\"\"\n 初始化项目:根据 README.md 自动生成完整的代码。\n \"\"\"\n if output_dir is None:\n output_dir = Path.cwd()\n- \n+\n try:\n generator = CodeGenerator(\n api_key=api_key,\n base_url=base_url,\n model=model,\n@@ -48,48 +56,64 @@\n raise typer.Exit(code=1)\n \n \n @app.command()\n def enhance(\n- issue_file: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"需求工单文件路径(如 feature.issue\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ issue_file: Path = typer.Argument(\n+ ...,\n+ exists=True,\n+ file_okay=True,\n+ dir_okay=False,\n+ help=\"需求工单文件路径(如 feature.issue\",\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径\"),\n ):\n \"\"\"\n 增强项目:根据需求工单添加新功能。\n \"\"\"\n if output_dir is None:\n output_dir = Path.cwd()\n- \n+\n # 读取工单文件\n try:\n with open(issue_file, \"r\", encoding=\"utf-8\") as f:\n issue_content = f.read()\n except Exception as e:\n logger.error(f\"读取工单文件失败: {e}\")\n raise typer.Exit(code=1)\n- \n+\n # 检查 design.json 是否存在\n design_path = output_dir / \"design.json\"\n if not design_path.exists():\n- logger.error(f\"design.json 不存在于 {output_dir},请先运行 init 命令初始化项目。\")\n+ logger.error(\n+ f\"design.json 不存在于 {output_dir},请先运行 init 命令初始化项目。\"\n+ )\n raise typer.Exit(code=1)\n- \n+\n try:\n generator = CodeGenerator(\n api_key=api_key,\n base_url=base_url,\n model=model,\n output_dir=str(output_dir),\n log_file=log_file,\n )\n # 简化增强逻辑:基于工单内容调用 LLM 生成代码变更\n logger.info(f\"处理增强工单: {issue_file}\")\n- console.print(f\"[yellow]注意:增强功能为简化实现,基于工单内容生成变更。工单内容预览: {issue_content[:100]}...[/yellow]\")\n+ console.print(\n+ f\"[yellow]注意:增强功能为简化实现,基于工单内容生成变更。工单内容预览: {issue_content[:100]}...[/yellow]\"\n+ )\n # 实际应用中,这里应解析工单并调用 generator 或类似方法生成代码\n # 示例:生成一个占位文件或调用检查器\n checker = Checker(output_dir=output_dir, code_generator=generator)\n success = checker.run_full_check_and_fix()\n if not success:\n@@ -101,48 +125,62 @@\n raise typer.Exit(code=1)\n \n \n @app.command()\n def fix(\n- issue_file: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"Bug工单文件路径如 bug.issue\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ issue_file: Path = typer.Argument(\n+ ...,\n+ exists=True,\n+ file_okay=True,\n+ dir_okay=False,\n+ help=\"Bug工单文件路径如 bug.issue\",\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径\"),\n ):\n \"\"\"\n 修复项目根据Bug工单自动修复 Bug。\n \"\"\"\n if output_dir is None:\n output_dir = Path.cwd()\n- \n+\n # 读取工单文件\n try:\n with open(issue_file, \"r\", encoding=\"utf-8\") as f:\n issue_content = f.read()\n except Exception as e:\n logger.error(f\"读取工单文件失败: {e}\")\n raise typer.Exit(code=1)\n- \n+\n # 检查 design.json 是否存在\n design_path = output_dir / \"design.json\"\n if not design_path.exists():\n logger.error(f\"design.json 不存在于 {output_dir},请确保项目已初始化。\")\n raise typer.Exit(code=1)\n- \n+\n try:\n generator = CodeGenerator(\n api_key=api_key,\n base_url=base_url,\n model=model,\n output_dir=str(output_dir),\n log_file=log_file,\n )\n # 简化修复逻辑:基于工单内容调用检查器进行修复\n logger.info(f\"处理Bug工单: {issue_file}\")\n- console.print(f\"[yellow]注意:修复功能为简化实现,基于工单内容调用检查器。工单内容预览: {issue_content[:100]}...[/yellow]\")\n+ console.print(\n+ f\"[yellow]注意:修复功能为简化实现,基于工单内容调用检查器。工单内容预览: {issue_content[:100]}...[/yellow]\"\n+ )\n checker = Checker(output_dir=output_dir, code_generator=generator)\n success = checker.run_full_check_and_fix()\n if not success:\n logger.error(\"修复过程中检查失败\")\n raise typer.Exit(code=1)\n",
"stderr": "would reformat src/llm_codegen/cli.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat src/llm_codegen/cli.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- src/llm_codegen/cli.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/cli.py\t2026-03-17 14:57:43.351695+00:00\n@@ -19,23 +19,31 @@\n console = Console()\n \n \n @app.command()\n def init(\n- readme: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"README.md 文件路径\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"输出根目录,默认为当前目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ readme: Path = typer.Argument(\n+ ..., exists=True, file_okay=True, dir_okay=False, help=\"README.md 文件路径\"\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"输出根目录,默认为当前目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径\"),\n ):\n \"\"\"\n 初始化项目:根据 README.md 自动生成完整的代码。\n \"\"\"\n if output_dir is None:\n output_dir = Path.cwd()\n- \n+\n try:\n generator = CodeGenerator(\n api_key=api_key,\n base_url=base_url,\n model=model,\n@@ -48,48 +56,64 @@\n raise typer.Exit(code=1)\n \n \n @app.command()\n def enhance(\n- issue_file: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"需求工单文件路径(如 feature.issue\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ issue_file: Path = typer.Argument(\n+ ...,\n+ exists=True,\n+ file_okay=True,\n+ dir_okay=False,\n+ help=\"需求工单文件路径(如 feature.issue\",\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径\"),\n ):\n \"\"\"\n 增强项目:根据需求工单添加新功能。\n \"\"\"\n if output_dir is None:\n output_dir = Path.cwd()\n- \n+\n # 读取工单文件\n try:\n with open(issue_file, \"r\", encoding=\"utf-8\") as f:\n issue_content = f.read()\n except Exception as e:\n logger.error(f\"读取工单文件失败: {e}\")\n raise typer.Exit(code=1)\n- \n+\n # 检查 design.json 是否存在\n design_path = output_dir / \"design.json\"\n if not design_path.exists():\n- logger.error(f\"design.json 不存在于 {output_dir},请先运行 init 命令初始化项目。\")\n+ logger.error(\n+ f\"design.json 不存在于 {output_dir},请先运行 init 命令初始化项目。\"\n+ )\n raise typer.Exit(code=1)\n- \n+\n try:\n generator = CodeGenerator(\n api_key=api_key,\n base_url=base_url,\n model=model,\n output_dir=str(output_dir),\n log_file=log_file,\n )\n # 简化增强逻辑:基于工单内容调用 LLM 生成代码变更\n logger.info(f\"处理增强工单: {issue_file}\")\n- console.print(f\"[yellow]注意:增强功能为简化实现,基于工单内容生成变更。工单内容预览: {issue_content[:100]}...[/yellow]\")\n+ console.print(\n+ f\"[yellow]注意:增强功能为简化实现,基于工单内容生成变更。工单内容预览: {issue_content[:100]}...[/yellow]\"\n+ )\n # 实际应用中,这里应解析工单并调用 generator 或类似方法生成代码\n # 示例:生成一个占位文件或调用检查器\n checker = Checker(output_dir=output_dir, code_generator=generator)\n success = checker.run_full_check_and_fix()\n if not success:\n@@ -101,48 +125,62 @@\n raise typer.Exit(code=1)\n \n \n @app.command()\n def fix(\n- issue_file: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False, help=\"Bug工单文件路径如 bug.issue\"),\n- output_dir: Optional[Path] = typer.Option(None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"),\n- api_key: Optional[str] = typer.Option(None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"),\n- base_url: str = typer.Option(\"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"),\n+ issue_file: Path = typer.Argument(\n+ ...,\n+ exists=True,\n+ file_okay=True,\n+ dir_okay=False,\n+ help=\"Bug工单文件路径如 bug.issue\",\n+ ),\n+ output_dir: Optional[Path] = typer.Option(\n+ None, \"--output\", \"-o\", help=\"项目根目录,默认为当前目录\"\n+ ),\n+ api_key: Optional[str] = typer.Option(\n+ None, \"--api-key\", envvar=\"DEEPSEEK_APIKEY\", help=\"API密钥\"\n+ ),\n+ base_url: str = typer.Option(\n+ \"https://api.deepseek.com\", \"--base-url\", help=\"API基础URL\"\n+ ),\n model: str = typer.Option(\"deepseek-reasoner\", \"--model\", \"-m\", help=\"使用的模型\"),\n log_file: Optional[str] = typer.Option(None, \"--log\", help=\"日志文件路径\"),\n ):\n \"\"\"\n 修复项目根据Bug工单自动修复 Bug。\n \"\"\"\n if output_dir is None:\n output_dir = Path.cwd()\n- \n+\n # 读取工单文件\n try:\n with open(issue_file, \"r\", encoding=\"utf-8\") as f:\n issue_content = f.read()\n except Exception as e:\n logger.error(f\"读取工单文件失败: {e}\")\n raise typer.Exit(code=1)\n- \n+\n # 检查 design.json 是否存在\n design_path = output_dir / \"design.json\"\n if not design_path.exists():\n logger.error(f\"design.json 不存在于 {output_dir},请确保项目已初始化。\")\n raise typer.Exit(code=1)\n- \n+\n try:\n generator = CodeGenerator(\n api_key=api_key,\n base_url=base_url,\n model=model,\n output_dir=str(output_dir),\n log_file=log_file,\n )\n # 简化修复逻辑:基于工单内容调用检查器进行修复\n logger.info(f\"处理Bug工单: {issue_file}\")\n- console.print(f\"[yellow]注意:修复功能为简化实现,基于工单内容调用检查器。工单内容预览: {issue_content[:100]}...[/yellow]\")\n+ console.print(\n+ f\"[yellow]注意:修复功能为简化实现,基于工单内容调用检查器。工单内容预览: {issue_content[:100]}...[/yellow]\"\n+ )\n checker = Checker(output_dir=output_dir, code_generator=generator)\n success = checker.run_full_check_and_fix()\n if not success:\n logger.error(\"修复过程中检查失败\")\n raise typer.Exit(code=1)"
]
},
{
"tool": "black",
"file": "src/llm_codegen/checker.py",
"returncode": 1,
"stdout": "--- src/llm_codegen/checker.py\t2026-03-17 14:52:45.835518+00:00\n+++ src/llm_codegen/checker.py\t2026-03-17 14:57:43.402370+00:00\n@@ -12,14 +12,16 @@\n \n # 尝试导入 pathspec用于精确解析 .gitignore\n try:\n from pathspec import PathSpec\n from pathspec.patterns import GitWildMatchPattern\n+\n HAS_PATHSPEC = True\n except ImportError:\n HAS_PATHSPEC = False\n import fnmatch\n+\n warnings.warn(\n \"pathspec 未安装,将使用简单的通配符匹配处理 .gitignore可能不完全准确。\"\n \"建议安装pip install pathspec\"\n )\n \n@@ -69,11 +71,13 @@\n model=model,\n output_dir=str(self.output_dir),\n )\n \n self.results_file = self.output_dir / \"check_results.json\"\n- logger.info(f\"Checker 初始化完成,输出目录: {self.output_dir},检查工具: {self.check_tools}\")\n+ logger.info(\n+ f\"Checker 初始化完成,输出目录: {self.output_dir},检查工具: {self.check_tools}\"\n+ )\n \n def _load_gitignore_patterns(self) -> Optional[Any]:\n \"\"\"\n 加载 .gitignore 文件中的模式,返回一个可用于匹配的函数或对象。\n 若文件不存在或解析失败,返回 None。\n@@ -128,11 +132,13 @@\n # 简单匹配:对于每个模式,如果模式以 / 结尾,则匹配目录;否则匹配文件\n for pattern in gitignore_matcher:\n # 处理目录模式(以 / 结尾)\n if pattern.endswith(\"/\"):\n # 检查路径是否以该目录开头\n- if rel_path.startswith(pattern.rstrip(\"/\") + \"/\") or rel_path == pattern.rstrip(\"/\"):\n+ if rel_path.startswith(\n+ pattern.rstrip(\"/\") + \"/\"\n+ ) or rel_path == pattern.rstrip(\"/\"):\n return True\n else:\n # 文件/通配符模式,使用 fnmatch\n if fnmatch.fnmatch(rel_path, pattern):\n return True\n@@ -148,11 +154,13 @@\n try:\n # 计算相对于输出目录的路径\n rel_path = file_path.relative_to(self.output_dir).as_posix()\n except ValueError:\n # 如果文件不在输出目录下(例如绝对路径),则保留(不过滤)\n- logger.warning(f\"文件 {file_path} 不在输出目录 {self.output_dir} 下,将保留\")\n+ logger.warning(\n+ f\"文件 {file_path} 不在输出目录 {self.output_dir} 下,将保留\"\n+ )\n filtered.append(file_path)\n continue\n \n # 硬编码忽略 .git 目录\n if rel_path.startswith(\".git/\") or rel_path == \".git\":\n@@ -244,11 +252,13 @@\n \"stdout\": \"\",\n \"stderr\": str(e),\n \"errors\": [str(e)],\n }\n \n- def run_parallel_checks(self, files: Optional[List[Path]] = None) -> List[Dict[str, Any]]:\n+ def run_parallel_checks(\n+ self, files: Optional[List[Path]] = None\n+ ) -> List[Dict[str, Any]]:\n \"\"\"\n 并行运行检查工具在指定文件上(仅使用配置的第一个工具)\n \n Args:\n files: 要检查的文件路径列表,如果为 None 则自动查找输出目录下所有 .py 文件(排除 .gitignore 中的)\n@@ -266,11 +276,13 @@\n tool = self.check_tools[0]\n logger.info(f\"开始并行检查,文件数: {len(files)},工具: {tool}\")\n \n all_results = []\n with ThreadPoolExecutor(max_workers=min(4, len(files))) as executor:\n- futures = [executor.submit(self.run_check, tool, file_path) for file_path in files]\n+ futures = [\n+ executor.submit(self.run_check, tool, file_path) for file_path in files\n+ ]\n \n for future in as_completed(futures):\n try:\n result = future.result()\n all_results.append(result)\n@@ -289,11 +301,13 @@\n json.dump(results, f, indent=2, ensure_ascii=False)\n logger.debug(f\"检查结果已保存至: {self.results_file}\")\n except Exception as e:\n logger.error(f\"保存检查结果失败: {e}\")\n \n- def collect_errors(self, results: Optional[List[Dict[str, Any]]] = None) -> List[Dict[str, Any]]:\n+ def collect_errors(\n+ self, results: Optional[List[Dict[str, Any]]] = None\n+ ) -> List[Dict[str, Any]]:\n \"\"\"\n 从检查结果中收集所有错误\n \n Args:\n results: 检查结果列表,如果为 None 则从文件加载\n@@ -316,19 +330,23 @@\n errors = []\n for result in results:\n if result.get(\"errors\") and result[\"errors\"]:\n for error_msg in result[\"errors\"]:\n if error_msg: # 跳过空错误\n- errors.append({\n- \"file\": result[\"file\"],\n- \"tool\": result[\"tool\"],\n- \"error\": error_msg,\n- })\n+ errors.append(\n+ {\n+ \"file\": result[\"file\"],\n+ \"tool\": result[\"tool\"],\n+ \"error\": error_msg,\n+ }\n+ )\n logger.info(f\"收集到 {len(errors)} 个错误\")\n return errors\n \n- def auto_fix(self, errors: List[Dict[str, Any]], context_files: Optional[List[str]] = None) -> bool:\n+ def auto_fix(\n+ self, errors: List[Dict[str, Any]], context_files: Optional[List[str]] = None\n+ ) -> bool:\n \"\"\"\n 自动调用 LLM 生成修复补丁并应用\n \n Args:\n errors: 错误列表,来自 collect_errors\n@@ -365,11 +383,13 @@\n path = Path(file_path)\n if not path.exists():\n path = self.output_dir / file_path\n if path.exists():\n with open(path, \"r\", encoding=\"utf-8\") as f:\n- context_content.append(f\"### 文件: {path.name} (路径: {file_path}) ###\\n{f.read()}\\n\")\n+ context_content.append(\n+ f\"### 文件: {path.name} (路径: {file_path}) ###\\n{f.read()}\\n\"\n+ )\n \n # 添加错误信息\n errors_str = json.dumps(errors, indent=2, ensure_ascii=False)\n context_content.append(f\"### 检查错误列表 ###\\n{errors_str}\\n\")\n \n@@ -384,11 +404,13 @@\n \"注意:只修复提到的错误,保持代码风格一致。\"\n )\n user_prompt = f\"请修复以下检查错误:\\n\\n{full_context}\"\n \n try:\n- result = self.code_generator._call_llm(system_prompt, user_prompt, temperature=0.1)\n+ result = self.code_generator._call_llm(\n+ system_prompt, user_prompt, temperature=0.1\n+ )\n patches = result.get(\"patches\", [])\n description = result.get(\"description\", \"无描述\")\n logger.info(f\"LLM 生成修复补丁: {description}, 补丁数: {len(patches)}\")\n \n # 应用补丁\n",
"stderr": "would reformat src/llm_codegen/checker.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat src/llm_codegen/checker.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- src/llm_codegen/checker.py\t2026-03-17 14:52:45.835518+00:00\n+++ src/llm_codegen/checker.py\t2026-03-17 14:57:43.402370+00:00\n@@ -12,14 +12,16 @@\n \n # 尝试导入 pathspec用于精确解析 .gitignore\n try:\n from pathspec import PathSpec\n from pathspec.patterns import GitWildMatchPattern\n+\n HAS_PATHSPEC = True\n except ImportError:\n HAS_PATHSPEC = False\n import fnmatch\n+\n warnings.warn(\n \"pathspec 未安装,将使用简单的通配符匹配处理 .gitignore可能不完全准确。\"\n \"建议安装pip install pathspec\"\n )\n \n@@ -69,11 +71,13 @@\n model=model,\n output_dir=str(self.output_dir),\n )\n \n self.results_file = self.output_dir / \"check_results.json\"\n- logger.info(f\"Checker 初始化完成,输出目录: {self.output_dir},检查工具: {self.check_tools}\")\n+ logger.info(\n+ f\"Checker 初始化完成,输出目录: {self.output_dir},检查工具: {self.check_tools}\"\n+ )\n \n def _load_gitignore_patterns(self) -> Optional[Any]:\n \"\"\"\n 加载 .gitignore 文件中的模式,返回一个可用于匹配的函数或对象。\n 若文件不存在或解析失败,返回 None。\n@@ -128,11 +132,13 @@\n # 简单匹配:对于每个模式,如果模式以 / 结尾,则匹配目录;否则匹配文件\n for pattern in gitignore_matcher:\n # 处理目录模式(以 / 结尾)\n if pattern.endswith(\"/\"):\n # 检查路径是否以该目录开头\n- if rel_path.startswith(pattern.rstrip(\"/\") + \"/\") or rel_path == pattern.rstrip(\"/\"):\n+ if rel_path.startswith(\n+ pattern.rstrip(\"/\") + \"/\"\n+ ) or rel_path == pattern.rstrip(\"/\"):\n return True\n else:\n # 文件/通配符模式,使用 fnmatch\n if fnmatch.fnmatch(rel_path, pattern):\n return True\n@@ -148,11 +154,13 @@\n try:\n # 计算相对于输出目录的路径\n rel_path = file_path.relative_to(self.output_dir).as_posix()\n except ValueError:\n # 如果文件不在输出目录下(例如绝对路径),则保留(不过滤)\n- logger.warning(f\"文件 {file_path} 不在输出目录 {self.output_dir} 下,将保留\")\n+ logger.warning(\n+ f\"文件 {file_path} 不在输出目录 {self.output_dir} 下,将保留\"\n+ )\n filtered.append(file_path)\n continue\n \n # 硬编码忽略 .git 目录\n if rel_path.startswith(\".git/\") or rel_path == \".git\":\n@@ -244,11 +252,13 @@\n \"stdout\": \"\",\n \"stderr\": str(e),\n \"errors\": [str(e)],\n }\n \n- def run_parallel_checks(self, files: Optional[List[Path]] = None) -> List[Dict[str, Any]]:\n+ def run_parallel_checks(\n+ self, files: Optional[List[Path]] = None\n+ ) -> List[Dict[str, Any]]:\n \"\"\"\n 并行运行检查工具在指定文件上(仅使用配置的第一个工具)\n \n Args:\n files: 要检查的文件路径列表,如果为 None 则自动查找输出目录下所有 .py 文件(排除 .gitignore 中的)\n@@ -266,11 +276,13 @@\n tool = self.check_tools[0]\n logger.info(f\"开始并行检查,文件数: {len(files)},工具: {tool}\")\n \n all_results = []\n with ThreadPoolExecutor(max_workers=min(4, len(files))) as executor:\n- futures = [executor.submit(self.run_check, tool, file_path) for file_path in files]\n+ futures = [\n+ executor.submit(self.run_check, tool, file_path) for file_path in files\n+ ]\n \n for future in as_completed(futures):\n try:\n result = future.result()\n all_results.append(result)\n@@ -289,11 +301,13 @@\n json.dump(results, f, indent=2, ensure_ascii=False)\n logger.debug(f\"检查结果已保存至: {self.results_file}\")\n except Exception as e:\n logger.error(f\"保存检查结果失败: {e}\")\n \n- def collect_errors(self, results: Optional[List[Dict[str, Any]]] = None) -> List[Dict[str, Any]]:\n+ def collect_errors(\n+ self, results: Optional[List[Dict[str, Any]]] = None\n+ ) -> List[Dict[str, Any]]:\n \"\"\"\n 从检查结果中收集所有错误\n \n Args:\n results: 检查结果列表,如果为 None 则从文件加载\n@@ -316,19 +330,23 @@\n errors = []\n for result in results:\n if result.get(\"errors\") and result[\"errors\"]:\n for error_msg in result[\"errors\"]:\n if error_msg: # 跳过空错误\n- errors.append({\n- \"file\": result[\"file\"],\n- \"tool\": result[\"tool\"],\n- \"error\": error_msg,\n- })\n+ errors.append(\n+ {\n+ \"file\": result[\"file\"],\n+ \"tool\": result[\"tool\"],\n+ \"error\": error_msg,\n+ }\n+ )\n logger.info(f\"收集到 {len(errors)} 个错误\")\n return errors\n \n- def auto_fix(self, errors: List[Dict[str, Any]], context_files: Optional[List[str]] = None) -> bool:\n+ def auto_fix(\n+ self, errors: List[Dict[str, Any]], context_files: Optional[List[str]] = None\n+ ) -> bool:\n \"\"\"\n 自动调用 LLM 生成修复补丁并应用\n \n Args:\n errors: 错误列表,来自 collect_errors\n@@ -365,11 +383,13 @@\n path = Path(file_path)\n if not path.exists():\n path = self.output_dir / file_path\n if path.exists():\n with open(path, \"r\", encoding=\"utf-8\") as f:\n- context_content.append(f\"### 文件: {path.name} (路径: {file_path}) ###\\n{f.read()}\\n\")\n+ context_content.append(\n+ f\"### 文件: {path.name} (路径: {file_path}) ###\\n{f.read()}\\n\"\n+ )\n \n # 添加错误信息\n errors_str = json.dumps(errors, indent=2, ensure_ascii=False)\n context_content.append(f\"### 检查错误列表 ###\\n{errors_str}\\n\")\n \n@@ -384,11 +404,13 @@\n \"注意:只修复提到的错误,保持代码风格一致。\"\n )\n user_prompt = f\"请修复以下检查错误:\\n\\n{full_context}\"\n \n try:\n- result = self.code_generator._call_llm(system_prompt, user_prompt, temperature=0.1)\n+ result = self.code_generator._call_llm(\n+ system_prompt, user_prompt, temperature=0.1\n+ )\n patches = result.get(\"patches\", [])\n description = result.get(\"description\", \"无描述\")\n logger.info(f\"LLM 生成修复补丁: {description}, 补丁数: {len(patches)}\")\n \n # 应用补丁"
]
},
{
"tool": "black",
"file": "src/llm_codegen/core.py",
"returncode": 1,
"stdout": "--- src/llm_codegen/core.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/core.py\t2026-03-17 14:57:43.474472+00:00\n@@ -130,46 +130,53 @@\n \"你是一个软件架构师。请根据README描述生成项目的中间设计文件design.json。\"\n \"design.json应包含项目名称、版本、描述、文件列表含路径、摘要、依赖、函数和类、建议命令和检查工具。\"\n \"返回严格的JSON对象符合DesignModel结构。\"\n )\n user_prompt = f\"README内容如下\\n\\n{self.readme_content}\"\n- \n+\n result = self._call_llm(system_prompt, user_prompt)\n design_data = result\n design = DesignModel(**design_data)\n- \n+\n # 写入design.json文件\n design_path = self.output_dir / \"design.json\"\n with open(design_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(design.dict(), f, indent=2, ensure_ascii=False)\n logger.info(f\"已生成design.json: {design_path}\")\n- \n+\n return design\n \n def load_state(self) -> Optional[StateModel]:\n \"\"\"加载断点续写状态\"\"\"\n if self.state_file.exists():\n try:\n with open(self.state_file, \"r\", encoding=\"utf-8\") as f:\n state_data = json.load(f)\n self.state = StateModel(**state_data)\n- logger.info(f\"加载状态成功: 当前文件索引 {self.state.current_file_index}\")\n+ logger.info(\n+ f\"加载状态成功: 当前文件索引 {self.state.current_file_index}\"\n+ )\n return self.state\n except Exception as e:\n logger.error(f\"加载状态失败: {e}\")\n return None\n return None\n \n- def save_state(self, current_file_index: int, generated_files: List[str], dependencies_map: Dict[str, List[str]]) -> None:\n+ def save_state(\n+ self,\n+ current_file_index: int,\n+ generated_files: List[str],\n+ dependencies_map: Dict[str, List[str]],\n+ ) -> None:\n \"\"\"保存断点续写状态\"\"\"\n state = StateModel(\n current_file_index=current_file_index,\n generated_files=generated_files,\n dependencies_map=dependencies_map,\n total_files=len(self.design.files) if self.design else 0,\n output_dir=str(self.output_dir),\n- readme_path=self.readme_content[:100] if self.readme_content else \"\"\n+ readme_path=self.readme_content[:100] if self.readme_content else \"\",\n )\n with open(self.state_file, \"w\", encoding=\"utf-8\") as f:\n json.dump(state.dict(), f, indent=2, ensure_ascii=False)\n logger.debug(f\"状态已保存: {self.state_file}\")\n \n@@ -182,18 +189,18 @@\n files: 按顺序需要生成的文件路径列表\n dependencies: 字典 {file: [依赖文件路径]}\n \"\"\"\n if not self.design:\n raise ValueError(\"design.json未加载请先调用generate_design_json\")\n- \n+\n files = [file.path for file in self.design.files]\n dependencies = {file.path: file.dependencies for file in self.design.files}\n- \n+\n logger.info(f\"从design.json解析到 {len(files)} 个待生成文件\")\n logger.debug(f\"文件列表: {files}\")\n logger.debug(f\"依赖关系: {dependencies}\")\n- \n+\n return files, dependencies\n \n def generate_file(\n self,\n file_path: str,\n@@ -206,18 +213,18 @@\n # 读取依赖文件内容\n context_content = []\n \n if self.readme_content:\n context_content.append(f\"### 项目 README ###\\n{self.readme_content}\\n\")\n- \n+\n # 添加design.json上下文\n design_path = self.output_dir / \"design.json\"\n if design_path.exists():\n with open(design_path, \"r\", encoding=\"utf-8\") as f:\n design_content = f.read()\n context_content.append(f\"### 设计文件: design.json ###\\n{design_content}\\n\")\n- \n+\n for dep in dependency_files:\n dep_path = Path(dep)\n if not dep_path.exists():\n # 尝试相对于当前目录或输出目录查找\n alt_path = self.output_dir / dep\n@@ -226,11 +233,13 @@\n else:\n raise FileNotFoundError(f\"依赖文件不存在: {dep}\")\n \n with open(dep_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n- context_content.append(f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\")\n+ context_content.append(\n+ f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\"\n+ )\n \n full_context = \"\\n\".join(context_content)\n \n system_prompt = (\n \"你是一个专业的编程助手。根据用户指令和提供的上下文文件,生成完整的代码。\"\n@@ -299,20 +308,24 @@\n self.readme_content = self.parse_readme(readme_path)\n \n # 加载状态\n state = self.load_state()\n if state:\n- console.print(f\"[green]✅ 检测到断点状态,从文件索引 {state.current_file_index} 继续[/green]\")\n+ console.print(\n+ f\"[green]✅ 检测到断点状态,从文件索引 {state.current_file_index} 继续[/green]\"\n+ )\n self.state = state\n # 从状态恢复设计假设design.json已存在\n design_path = self.output_dir / \"design.json\"\n if design_path.exists():\n with open(design_path, \"r\", encoding=\"utf-8\") as f:\n design_data = json.load(f)\n self.design = DesignModel(**design_data)\n else:\n- console.print(\"[bold yellow]⚠ design.json不存在重新生成...[/bold yellow]\")\n+ console.print(\n+ \"[bold yellow]⚠ design.json不存在重新生成...[/bold yellow]\"\n+ )\n self.design = self.generate_design_json()\n else:\n console.print(\"[bold yellow]📋 正在生成设计文件...[/bold yellow]\")\n self.design = self.generate_design_json()\n self.state = None\n@@ -345,11 +358,13 @@\n file_task = progress.add_task(f\"生成 {file}\", total=None)\n \n try:\n # 获取依赖文件\n deps = dependencies.get(file, [])\n- instruction = f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ instruction = (\n+ f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ )\n code, desc, commands = self.generate_file(file, instruction, deps)\n logger.info(f\"生成完成: {file} - {desc}\")\n \n # 写入文件\n output_path = self.output_dir / file\n",
"stderr": "would reformat src/llm_codegen/core.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.\n",
"errors": [
"would reformat src/llm_codegen/core.py\n\nOh no! 💥 💔 💥\n1 file would be reformatted.",
"--- src/llm_codegen/core.py\t2026-03-17 14:27:38.333874+00:00\n+++ src/llm_codegen/core.py\t2026-03-17 14:57:43.474472+00:00\n@@ -130,46 +130,53 @@\n \"你是一个软件架构师。请根据README描述生成项目的中间设计文件design.json。\"\n \"design.json应包含项目名称、版本、描述、文件列表含路径、摘要、依赖、函数和类、建议命令和检查工具。\"\n \"返回严格的JSON对象符合DesignModel结构。\"\n )\n user_prompt = f\"README内容如下\\n\\n{self.readme_content}\"\n- \n+\n result = self._call_llm(system_prompt, user_prompt)\n design_data = result\n design = DesignModel(**design_data)\n- \n+\n # 写入design.json文件\n design_path = self.output_dir / \"design.json\"\n with open(design_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(design.dict(), f, indent=2, ensure_ascii=False)\n logger.info(f\"已生成design.json: {design_path}\")\n- \n+\n return design\n \n def load_state(self) -> Optional[StateModel]:\n \"\"\"加载断点续写状态\"\"\"\n if self.state_file.exists():\n try:\n with open(self.state_file, \"r\", encoding=\"utf-8\") as f:\n state_data = json.load(f)\n self.state = StateModel(**state_data)\n- logger.info(f\"加载状态成功: 当前文件索引 {self.state.current_file_index}\")\n+ logger.info(\n+ f\"加载状态成功: 当前文件索引 {self.state.current_file_index}\"\n+ )\n return self.state\n except Exception as e:\n logger.error(f\"加载状态失败: {e}\")\n return None\n return None\n \n- def save_state(self, current_file_index: int, generated_files: List[str], dependencies_map: Dict[str, List[str]]) -> None:\n+ def save_state(\n+ self,\n+ current_file_index: int,\n+ generated_files: List[str],\n+ dependencies_map: Dict[str, List[str]],\n+ ) -> None:\n \"\"\"保存断点续写状态\"\"\"\n state = StateModel(\n current_file_index=current_file_index,\n generated_files=generated_files,\n dependencies_map=dependencies_map,\n total_files=len(self.design.files) if self.design else 0,\n output_dir=str(self.output_dir),\n- readme_path=self.readme_content[:100] if self.readme_content else \"\"\n+ readme_path=self.readme_content[:100] if self.readme_content else \"\",\n )\n with open(self.state_file, \"w\", encoding=\"utf-8\") as f:\n json.dump(state.dict(), f, indent=2, ensure_ascii=False)\n logger.debug(f\"状态已保存: {self.state_file}\")\n \n@@ -182,18 +189,18 @@\n files: 按顺序需要生成的文件路径列表\n dependencies: 字典 {file: [依赖文件路径]}\n \"\"\"\n if not self.design:\n raise ValueError(\"design.json未加载请先调用generate_design_json\")\n- \n+\n files = [file.path for file in self.design.files]\n dependencies = {file.path: file.dependencies for file in self.design.files}\n- \n+\n logger.info(f\"从design.json解析到 {len(files)} 个待生成文件\")\n logger.debug(f\"文件列表: {files}\")\n logger.debug(f\"依赖关系: {dependencies}\")\n- \n+\n return files, dependencies\n \n def generate_file(\n self,\n file_path: str,\n@@ -206,18 +213,18 @@\n # 读取依赖文件内容\n context_content = []\n \n if self.readme_content:\n context_content.append(f\"### 项目 README ###\\n{self.readme_content}\\n\")\n- \n+\n # 添加design.json上下文\n design_path = self.output_dir / \"design.json\"\n if design_path.exists():\n with open(design_path, \"r\", encoding=\"utf-8\") as f:\n design_content = f.read()\n context_content.append(f\"### 设计文件: design.json ###\\n{design_content}\\n\")\n- \n+\n for dep in dependency_files:\n dep_path = Path(dep)\n if not dep_path.exists():\n # 尝试相对于当前目录或输出目录查找\n alt_path = self.output_dir / dep\n@@ -226,11 +233,13 @@\n else:\n raise FileNotFoundError(f\"依赖文件不存在: {dep}\")\n \n with open(dep_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n- context_content.append(f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\")\n+ context_content.append(\n+ f\"### 文件: {dep_path.name} (路径: {dep}) ###\\n{content}\\n\"\n+ )\n \n full_context = \"\\n\".join(context_content)\n \n system_prompt = (\n \"你是一个专业的编程助手。根据用户指令和提供的上下文文件,生成完整的代码。\"\n@@ -299,20 +308,24 @@\n self.readme_content = self.parse_readme(readme_path)\n \n # 加载状态\n state = self.load_state()\n if state:\n- console.print(f\"[green]✅ 检测到断点状态,从文件索引 {state.current_file_index} 继续[/green]\")\n+ console.print(\n+ f\"[green]✅ 检测到断点状态,从文件索引 {state.current_file_index} 继续[/green]\"\n+ )\n self.state = state\n # 从状态恢复设计假设design.json已存在\n design_path = self.output_dir / \"design.json\"\n if design_path.exists():\n with open(design_path, \"r\", encoding=\"utf-8\") as f:\n design_data = json.load(f)\n self.design = DesignModel(**design_data)\n else:\n- console.print(\"[bold yellow]⚠ design.json不存在重新生成...[/bold yellow]\")\n+ console.print(\n+ \"[bold yellow]⚠ design.json不存在重新生成...[/bold yellow]\"\n+ )\n self.design = self.generate_design_json()\n else:\n console.print(\"[bold yellow]📋 正在生成设计文件...[/bold yellow]\")\n self.design = self.generate_design_json()\n self.state = None\n@@ -345,11 +358,13 @@\n file_task = progress.add_task(f\"生成 {file}\", total=None)\n \n try:\n # 获取依赖文件\n deps = dependencies.get(file, [])\n- instruction = f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ instruction = (\n+ f\"请根据README描述和依赖文件生成文件 '{file}' 的完整代码。\"\n+ )\n code, desc, commands = self.generate_file(file, instruction, deps)\n logger.info(f\"生成完成: {file} - {desc}\")\n \n # 写入文件\n output_path = self.output_dir / file"
]
}
]