Skip to content

feat: add --tools filter to issues command#4

Merged
alerizzo merged 3 commits intomainfrom
feat/issues-tools-filter
Apr 13, 2026
Merged

feat: add --tools filter to issues command#4
alerizzo merged 3 commits intomainfrom
feat/issues-tools-filter

Conversation

@alerizzo
Copy link
Copy Markdown
Collaborator

Summary

  • Add -T, --tools <tools> option to the issues command, accepting comma-separated tool UUIDs or human-friendly name strings
  • Implement resolveToolUuids() in src/utils/formatting.ts with a three-tier resolution strategy:
    1. UUID passthrough — values matching UUID format are sent directly to the API without any lookup
    2. Exact match — case-insensitive match against the tool's name or shortName (e.g. eslint → ESLint, eslint9 → ESLint 9)
    3. Substring search — searches name, shortName, and prefix fields; only resolves when exactly one tool matches. Ambiguous inputs (e.g. mark matching both Markdownlint and Remarklint) produce a clear error listing all matches
  • The global tool list (ToolsService.listTools) is fetched lazily — only when at least one input is not a UUID — and handles pagination to collect all tools
  • Passes resolved UUIDs to the existing toolUuids field on SearchRepositoryIssuesBody

Examples

# Filter by tool name (case-insensitive exact match)
codacy issues gh my-org my-repo --tools eslint

# Filter by UUID
codacy issues gh my-org my-repo --tools a1b2c3d4-e5f6-7890-abcd-ef1234567890

# Mix names and UUIDs
codacy issues gh my-org my-repo --tools eslint,semgrep

# Ambiguous input → clear error
codacy issues gh my-org my-repo --tools mark
# Error: Tool "mark" is ambiguous, matches: Markdownlint, Remarklint

Test plan

  • 7 unit tests for resolveToolUuids() in formatting.test.ts (UUID passthrough, exact name, exact shortName, substring, ambiguous error, not-found error, mixed inputs)
  • 7 integration tests for --tools in issues.test.ts (UUID direct, name resolution, shortName resolution, prefix match, ambiguous error, not-found error, mixed values)
  • All 266 tests pass
  • Build succeeds (npm run build)

🤖 Generated with Claude Code

Allow filtering issues by tool using UUIDs or human-friendly names.
Resolution logic: exact match on name/shortName, then substring search
on name/shortName/prefix (only when unambiguous). Ambiguous or unknown
inputs produce a clear error message.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 13, 2026 15:46
@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 13, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 33 complexity · 16 duplication

Metric Results
Complexity 33
Duplication 16

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes. Give us feedback

Copy link
Copy Markdown

@codacy-production codacy-production bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

The implementation aligns with the requirement to add a --tools filter to the issues command. All required test scenarios for tool resolution—including UUID passthrough, name matching, and substring searches—have been addressed.

Codacy analysis indicates the PR is up to standards, though there are notable increases in complexity and code clones within the test files (src/commands/issues.test.ts and src/utils/formatting.test.ts). The most critical finding is the use of process-level side effects in utility logic, which should be corrected to ensure consistent error handling across the CLI. There are no major security flaws or logic bugs preventing a merge after these minor refactors.

About this PR

  • There is a noticeable increase in complexity and code duplication within the test files. Consider abstracting common setup, mocking, and assertion logic into helper functions to improve maintainability as the CLI command set grows.

Test suggestions

  • Verify UUIDs are passed through directly without triggering an API call for the tool list.
  • Verify exact case-insensitive match on tool name resolves to the correct UUID.
  • Verify exact case-insensitive match on tool shortName resolves to the correct UUID.
  • Verify unique substring search (across name, shortName, or prefix) resolves when exactly one tool matches.
  • Verify an error is raised with a list of candidates when a substring match is ambiguous.
  • Verify an error is raised when a tool name or UUID is not found.
  • Verify mixed inputs of UUIDs and names are resolved correctly in a single command execution.
  • Verify the tool list is fetched using pagination until all tools are retrieved.

🗒️ Improve review quality by adding custom instructions

Comment thread src/utils/formatting.ts Outdated
uuids.push(matches[0].uuid);
} else if (matches.length === 0) {
console.error(ansis.red(`Error: Tool "${input}" not found.`));
process.exit(1);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

Utility functions should throw Errors instead of calling process.exit(). This allows the caller (which already uses handleError) to manage the failure gracefully and ensures consistent error formatting.

Try running the following prompt in your coding agent:

Refactor resolveToolUuids in src/utils/formatting.ts to throw descriptive Errors instead of using console.error and process.exit(1). Then update the associated tests in src/utils/formatting.test.ts to use expect().rejects.toThrow().

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — refactoring to throw errors instead of process.exit(). The caller in issues.ts will catch these via the existing handleError() flow.

Comment thread src/utils/formatting.ts Outdated
}
}

return uuids;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Suggestion: Ensure the returned list of UUIDs contains only unique values to avoid redundant filters in the API request.

Suggested change
return uuids;
return [...new Set(uuids)];

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — adding deduplication.

Comment on lines +703 to +715
it("should error when tool name is ambiguous", async () => {
vi.mocked(ToolsService.listTools).mockResolvedValue(mockToolList as any);

const mockExit = vi.spyOn(process, "exit").mockImplementation(() => {
throw new Error("process.exit called");
});
const mockStderr = vi.spyOn(console, "error").mockImplementation(() => {});

const program = createProgram();
await expect(
program.parseAsync([
"node", "test", "issues", "gh", "test-org", "test-repo",
"--tools", "mark",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Nitpick: The new test cases for the --tools filter introduce significant duplication. Extracting a helper function to handle the common CLI setup and service mocking would improve maintainability and simplify the test suite.

Try running the following prompt in your IDE agent:

In src/commands/issues.test.ts, extract a helper function for the --tools filter tests to reduce duplication in command invocation and error handling assertions.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing test suite in this file follows the same inline pattern. Introducing abstractions in tests reduces readability — keeping it as-is for consistency.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a --tools filter to the issues CLI command by resolving user-provided tool identifiers (UUIDs or friendly names) into tool UUIDs and sending them via the existing toolUuids request field.

Changes:

  • Added -T, --tools <tools> option to codacy issues, accepting comma-separated UUIDs or names.
  • Implemented resolveToolUuids() to resolve inputs using UUID passthrough, exact match, then unique substring match.
  • Added unit + integration tests covering UUID passthrough, resolution, and error cases.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.

File Description
src/utils/formatting.ts Adds resolveToolUuids() helper to map tool inputs to UUIDs.
src/utils/formatting.test.ts Adds unit tests for resolveToolUuids().
src/commands/issues.ts Wires --tools into the issues search request, fetching/paginating tool list when needed.
src/commands/issues.test.ts Adds integration tests for --tools behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/formatting.ts Outdated
Comment on lines +597 to +604
console.error(ansis.red(`Error: Tool "${input}" not found.`));
process.exit(1);
} else {
const names = matches.map((t) => t.name).join(", ");
console.error(
ansis.red(`Error: Tool "${input}" is ambiguous, matches: ${names}`),
);
process.exit(1);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — refactoring to throw errors. Same fix as the Codacy comment above.

Comment thread src/utils/formatting.ts
Comment on lines +600 to +605
const names = matches.map((t) => t.name).join(", ");
console.error(
ansis.red(`Error: Tool "${input}" is ambiguous, matches: ${names}`),
);
process.exit(1);
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed together with the not-found branch.

Comment on lines +135 to +151
it("should error on ambiguous substring match", async () => {
const mockExit = vi.spyOn(process, "exit").mockImplementation(() => {
throw new Error("process.exit called");
});
vi.spyOn(console, "error").mockImplementation(() => {});

await expect(resolveToolUuids(["mark"], fetchTools)).rejects.toThrow(
"process.exit called",
);

expect(console.error).toHaveBeenCalledWith(
expect.stringContaining("ambiguous"),
);

mockExit.mockRestore();
(console.error as any).mockRestore();
});
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixing the spy references.

Comment thread src/commands/issues.test.ts Outdated
Comment on lines +684 to +696
it("should resolve a substring match via prefix when only one tool matches", async () => {
vi.mocked(ToolsService.listTools).mockResolvedValue(mockToolList as any);
vi.mocked(AnalysisService.searchRepositoryIssues).mockResolvedValue({
data: [],
} as any);

const program = createProgram();
// "eslint9" matches shortName "eslint9" exactly
await program.parseAsync([
"node", "test", "issues", "gh", "test-org", "test-repo",
"--tools", "eslint9",
]);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — renaming to reflect what it actually tests, and adding a separate test for true substring/prefix matching.

- Refactor resolveToolUuids to throw errors instead of process.exit(),
  letting the caller's handleError() manage the exit flow
- Deduplicate resolved UUIDs via Set
- Fix misleading test name and add true substring match test
- Fix test spy restoration to use stored variable references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alerizzo alerizzo merged commit df6a92d into main Apr 13, 2026
4 checks passed
@alerizzo alerizzo deleted the feat/issues-tools-filter branch April 13, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants