diff --git a/README.md b/README.md index 05400d9b82..63e2914cd7 100644 --- a/README.md +++ b/README.md @@ -1913,6 +1913,7 @@ File reads accept raw repository-relative paths and preserve spaces, Unicode, `#`, `?`, and literal percent signs with either HTTP client. Directory listings ask for the repository's own default branch, so a repository that uses neither `main` nor `master` reports its current tree instead of a stale branch. +They indicate when GitHub or the local output limit truncates the listing. The `issues` command excludes pull requests and follows additional pages to fill its requested issue limit; the `prs` command remains available for pull requests. diff --git a/skills/public/github-deep-research/SKILL.md b/skills/public/github-deep-research/SKILL.md index 694db9b3f0..6d55278d51 100644 --- a/skills/public/github-deep-research/SKILL.md +++ b/skills/public/github-deep-research/SKILL.md @@ -57,6 +57,7 @@ python /path/to/skill/scripts/github_api.py tree - releases The `issues` command returns repository issues only, excluding pull requests. It follows additional pages when needed to fill the requested issue limit; use `prs` to research pull requests separately. +The `tree` command marks results truncated by GitHub or by its own output limit; inspect subdirectories separately before drawing conclusions from an incomplete tree. **Round 2 - Discovery (3-5 web_search)** - Get overview and identify key terms diff --git a/skills/public/github-deep-research/scripts/github_api.py b/skills/public/github-deep-research/scripts/github_api.py index ac09e57dce..058dd21afb 100644 --- a/skills/public/github-deep-research/scripts/github_api.py +++ b/skills/public/github-deep-research/scripts/github_api.py @@ -252,7 +252,12 @@ class GitHubAPI: else: lines.append(f"{indent}{name}") - return "\n".join(lines[:100]) # Limit output + output = lines[:100] + if len(lines) > 100: + output.append("[Tree output limit: additional entries omitted]") + if tree_data.get("truncated"): + output.append("[GitHub truncated the tree; additional entries may be missing]") + return "\n".join(output) def summarize_repo(self, owner: str, repo: str) -> Dict: """ diff --git a/tests/skills/test_github_deep_research.py b/tests/skills/test_github_deep_research.py index 23a78dc3ce..4f175ce535 100644 --- a/tests/skills/test_github_deep_research.py +++ b/tests/skills/test_github_deep_research.py @@ -173,6 +173,35 @@ def test_tree_does_not_guess_another_branch(monkeypatch, fallback_client): assert asked == ["/repos/owner/repo/git/trees/HEAD"] +@pytest.mark.parametrize("truncated", [False, True]) +def test_tree_reports_upstream_truncation(fallback_client, truncated): + client, _ = fallback_client + result = client.format_tree({"tree": [{"path": "README.md", "type": "blob"}], "truncated": truncated}) + assert result.startswith("README.md") + assert ("GitHub" in result and "truncated" in result) is truncated + + +@pytest.mark.parametrize("count", [99, 100, 101]) +def test_tree_reports_local_entry_limit(fallback_client, count): + client, _ = fallback_client + tree = [{"path": f"file-{i}", "type": "blob"} for i in range(count)] + result = client.format_tree({"tree": tree, "truncated": False}) + assert ("output limit" in result) is (count > 100) + assert "file-98" in result + if count > 100: + assert "file-100" not in result + + +def test_tree_reports_both_limits_and_ignores_entries_below_max_depth(fallback_client): + client, _ = fallback_client + hidden = [{"path": f"folder/file-{i}", "type": "blob"} for i in range(101)] + assert client.format_tree({"tree": hidden}, max_depth=1) == "" + visible = [{"path": f"file-{i}", "type": "blob"} for i in range(101)] + result = client.format_tree({"tree": visible, "truncated": True}) + assert "output limit" in result + assert "GitHub truncated" in result + + def _client_returning_status(monkeypatch, transport, status, body=b'{"ok": true}'): """One client whose transport answers ``status`` for every request, plus its module.