From 00e526512a25b92d2a6caf8724b882f1af386ab4 Mon Sep 17 00:00:00 2001 From: Jason Walker <913443@dadeschools.net> Date: Sat, 25 Jul 2026 19:12:07 -0400 Subject: [PATCH] fix(mcp): include untracked files in preflight porcelain parsing (Closes #697, Ref #700) --- gitea_mcp_server.py | 4 +-- tests/test_issue_697_untracked_preflight.py | 32 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/test_issue_697_untracked_preflight.py diff --git a/gitea_mcp_server.py b/gitea_mcp_server.py index 915eaad..875bfee 100644 --- a/gitea_mcp_server.py +++ b/gitea_mcp_server.py @@ -577,10 +577,10 @@ def _get_workspace_porcelain(worktree_path: str | None = None) -> str: def _parse_porcelain_entries(porcelain: str) -> dict[str, str]: - """Map tracked path -> full porcelain line (untracked ``??`` ignored).""" + """Map path -> full porcelain line (including untracked ``??``).""" entries: dict[str, str] = {} for line in (porcelain or "").splitlines(): - if not line or len(line) < 4 or line.startswith("??"): + if not line or len(line) < 4: continue path = line[3:].strip() if " -> " in path: diff --git a/tests/test_issue_697_untracked_preflight.py b/tests/test_issue_697_untracked_preflight.py new file mode 100644 index 0000000..bca4f8d --- /dev/null +++ b/tests/test_issue_697_untracked_preflight.py @@ -0,0 +1,32 @@ +"""Regression test for Issue #697: Native workspace preflight explicitly includes untracked files. + +Verifies that `_parse_porcelain_entries` includes untracked files (lines starting with '??') +so workspace preflight fails closed on untracked or dirty state. +""" + +from __future__ import annotations + +import unittest +import gitea_mcp_server as mcp_server + + +class TestIssue697UntrackedPreflight(unittest.TestCase): + def test_parse_porcelain_entries_includes_untracked_files(self): + porcelain = " M gitea_mcp_server.py\n?? untracked_file.txt\n?? new_dir/test.py\n" + entries = mcp_server._parse_porcelain_entries(porcelain) + self.assertIn("untracked_file.txt", entries) + self.assertIn("new_dir/test.py", entries) + self.assertEqual(entries["untracked_file.txt"], "?? untracked_file.txt") + self.assertEqual(entries["new_dir/test.py"], "?? new_dir/test.py") + + def test_parse_porcelain_entries_includes_tracked_and_untracked(self): + porcelain = " M tracked.py\n?? untracked.py\n D deleted.py\n" + entries = mcp_server._parse_porcelain_entries(porcelain) + self.assertEqual(len(entries), 3) + self.assertIn("tracked.py", entries) + self.assertIn("untracked.py", entries) + self.assertIn("deleted.py", entries) + + +if __name__ == "__main__": + unittest.main()