[ { "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" ] } ]