Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@jordanpartridge
Copy link
Owner

@jordanpartridge jordanpartridge commented Jun 27, 2025

Summary

Significantly improves repository listing performance and prepares for GitHub's native search functionality.

Performance Improvements

  • 3-5x faster: Reduced default fetch limit from 100 to 10 repositories
  • 🎯 TUI optimized: Perfect amount for terminal interface browsing
  • 📱 Better UX: Commands complete in ~1s instead of 3-5s

Search Functionality

  • 🔍 GitHub native search: Integrated with github-client v2.2.0 search API
  • 🛡️ Graceful fallback: Falls back to listing when search unavailable in standalone mode
  • 🌍 Framework ready: Search works perfectly in Laravel/Conduit contexts

Technical Changes

  • Updated composer dependency to github-client ^2.2.0
  • Improved error handling and user feedback
  • Maintained full backward compatibility
  • Added comprehensive filtering and natural language query support

Benefits

  • Much faster repository browsing experience
  • Ready for powerful search capabilities
  • Better error handling and user feedback
  • Maintains all existing functionality

Test Commands

./bin/github repo:list --limit=5        # Fast listing
./bin/github repo:list --interactive     # Interactive mode
./bin/github repo:list --search="laravel" # Search (when available)

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

Summary by CodeRabbit

  • New Features

    • Introduced advanced filtering and searching for repositories and issues, including natural language queries, language, stars, labels, assignees, visibility, and activity.
    • Added interactive and non-interactive CLI commands for listing, cloning, and exploring repositories and issues.
    • Provided commands for listing available features and subcommands with status indicators.
    • Added statistics summaries for repositories and issues.
    • Included example scripts demonstrating filtering capabilities.
  • Bug Fixes

    • Improved error handling and user feedback in CLI commands.
  • Style

    • Updated command names and descriptions for clarity.
    • Enhanced formatting and consistency in output messages.
  • Chores

    • Added configuration files for local settings and tooling.
    • Updated dependency versions.
    • Streamlined service provider and command registration.
  • Tests

    • Added comprehensive tests for filtering logic and CLI application behavior.
    • Introduced a new test suite and setup for consistent testing.

…tionality

- Reduce default repository fetch limit from 100 to 10 for faster TUI experience
- Add support for GitHub's native search API via updated github-client
- Implement graceful fallback when search unavailable in standalone mode
- Update composer dependency to github-client ^2.2.0
- Maintain backward compatibility for all existing features

Performance improvement: Commands now complete in ~1s instead of 3-5s
@coderabbitai
Copy link

coderabbitai bot commented Jun 27, 2025

Warning

Rate limit exceeded

@jordanpartridge has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8d56673 and eaf489d.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • composer.json (2 hunks)
  • src/Application.php (1 hunks)
  • src/Commands/ReposCommand.php (10 hunks)

Walkthrough

This update introduces advanced filtering and search capabilities for GitHub repositories and issues, adds a new data filtering utility, expands the CLI with new commands and improved command registration, and provides comprehensive tests. Several new configuration and example files are included, along with stylistic improvements and enhanced error handling throughout the codebase.

Changes

File(s) Change Summary
.claude/settings.local.json, .phpactor.json Added configuration files for command permissions and PHP Actor settings.
composer.json Updated "jordanpartridge/github-client" dependency version constraint.
config/github-zero.php Added newline at end of file.
examples/filtering-demo.php Added example script demonstrating filtering on GitHub repository data.
src/Application.php Modularized application setup, refactored command registration, introduced constants, and improved GitHub client instantiation.
src/Commands/CloneCommand.php Renamed command to 'repo:clone', improved descriptions, and applied stylistic formatting changes.
src/Commands/IssueCommand.php, src/Commands/IssuesCommand.php, src/Commands/ListCommand.php, Added new command classes for issues and listing commands.
src/Commands/RepoCommand.php, src/Commands/ReposCommand.php Added/updated repository command classes with advanced filtering and search options.
src/ConduitExtension.php Registered new IssuesCommand in the extension.
src/GitHubZeroServiceProvider.php Streamlined service provider, expanded registered commands, removed config publishing.
src/Support/FilterableGitHubData.php Introduced a new utility class for advanced filtering, sorting, and statistics on GitHub data.
test-package.php Applied stylistic and formatting improvements only.
tests/ApplicationTest.php, tests/Pest.php, tests/Support/FilterableGitHubDataTest.php, tests/TestCase.php Added new tests for application setup, command registration, and data filtering utility.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant Application
    participant Command
    participant GithubClient
    participant FilterableGitHubData

    User->>CLI: Run a command (e.g., repo:list, issue:list)
    CLI->>Application: Bootstrap and parse command
    Application->>GithubClient: (If needed) Create or use injected client
    Application->>Command: Instantiate command (inject client if required)
    Command->>GithubClient: Fetch repositories/issues data
    Command->>FilterableGitHubData: Initialize with data
    Command->>FilterableGitHubData: Apply filters/sorting/queries
    FilterableGitHubData-->>Command: Return filtered data
    Command->>CLI: Output results (list, stats, details)
Loading

Poem

In the warren where commands now grow,
Filtering magic makes data flow!
With queries and stats, and sorting galore,
Rabbits can search for repos and more.
New commands hop in, tests keep us right—
GitHub Zero leaps ahead,
In the moonlit code-burrow night!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🔭 Outside diff range comments (1)
src/Commands/ReposCommand.php (1)

345-355: Critical: Command injection vulnerability in git clone.

Using exec() with user-controlled input is dangerous and can lead to command injection attacks.

Use Symfony Process component like in CloneCommand:

         if (confirm(
             label: "🚀 Clone {$selected}?",
             default: true
         )) {
             $output->writeln("<info>🔄 Cloning {$selected}...</info>");

             $repoName = basename($selected, '.git');
-            exec("git clone {$selected} {$repoName}", $gitOutput, $exitCode);
+            $process = new \Symfony\Component\Process\Process(['git', 'clone', $selected, $repoName]);
+            $process->run();
+            $exitCode = $process->getExitCode();

             if ($exitCode === 0) {
                 $output->writeln("<info>✅ Successfully cloned to ./{$repoName}</info>");
             } else {
                 $output->writeln('<error>❌ Failed to clone repository</error>');
             }
         }
🧹 Nitpick comments (5)
tests/TestCase.php (1)

11-14: Consider removing empty setUp() override.

The setUp() method only calls parent::setUp() without adding any custom setup logic. Consider removing this method until custom setup is needed.

 abstract class TestCase extends BaseTestCase
 {
-    protected function setUp(): void
-    {
-        parent::setUp();
-    }
 }
src/Support/FilterableGitHubData.php (1)

222-233: Consider making the language list more extensible.

The hardcoded regex pattern for languages might miss some languages. Consider extracting this to a constant or configuration for easier maintenance.

+private const SUPPORTED_LANGUAGES = [
+    'php', 'python', 'javascript', 'js', 'typescript', 'ts', 'go', 'rust', 
+    'java', 'ruby', 'c++', 'cpp', 'c#', 'csharp', 'swift', 'kotlin', 'scala'
+];
+
 public function query(string $query): self
 {
     $query = strtolower(trim($query));
     $result = $this;

     // Language patterns
-    if (preg_match('/\b(php|python|javascript|js|typescript|ts|go|rust|java|ruby|c\+\+|cpp|c#|csharp)\b/', $query, $matches)) {
+    $languagePattern = '/\b(' . implode('|', array_map('preg_quote', self::SUPPORTED_LANGUAGES)) . ')\b/';
+    if (preg_match($languagePattern, $query, $matches)) {
         $lang = $matches[1];
tests/ApplicationTest.php (1)

14-27: Consider using environment isolation for better test reliability.

The test correctly verifies command registration, but setting environment variables globally could cause issues in parallel test execution.

Consider using a more isolated approach for environment variable testing:

 it('has required commands registered', function () {
-    $_ENV['GITHUB_TOKEN'] = 'fake-token-for-testing';
-
-    $app = new Application;
+    $app = new Application;
+    
+    // Mock or inject the GitHub client if needed for command registration
+    // This avoids global environment modification
 
     expect($app->has('list'))->toBeTrue()
         ->and($app->has('repo'))->toBeTrue()
         ->and($app->has('issue'))->toBeTrue()
         ->and($app->has('repo:list'))->toBeTrue()
         ->and($app->has('repo:clone'))->toBeTrue()
         ->and($app->has('issue:list'))->toBeTrue();
-
-    unset($_ENV['GITHUB_TOKEN']);
 });
examples/filtering-demo.php (1)

64-128: Comprehensive demonstration of filtering capabilities.

The demo effectively showcases the full range of FilterableGitHubData functionality including basic filtering, natural language queries, complex chaining, and statistics. The progressive examples help users understand the capabilities step-by-step.

Consider adding basic error handling for production-ready examples:

+try {
     $repos = FilterableGitHubData::repositories($sampleRepos);
     // ... demo code ...
+} catch (\Exception $e) {
+    echo "Error: " . $e->getMessage() . "\n";
+    exit(1);
+}
src/Application.php (1)

52-63: Consider simplifying command instantiation logic.

The differentiation between "discovery" and "functional" commands seems arbitrary. Consider either:

  1. Passing the GitHub client to all commands for consistency, or
  2. Making the distinction clearer with interfaces or base classes
 private function setupCommands(Github $github): void
 {
     foreach ($this->commandClasses as $commandClass) {
-        // Discovery commands don't need GitHub client
-        if (in_array($commandClass, [
-            Commands\ListCommand::class,
-            Commands\RepoCommand::class,
-            Commands\IssueCommand::class,
-        ])) {
-            $this->add(new $commandClass);
-        } else {
-            $this->add(new $commandClass($github));
-        }
+        // Check if command requires GitHub client via reflection or interface
+        $reflection = new \ReflectionClass($commandClass);
+        $constructor = $reflection->getConstructor();
+        
+        if ($constructor && $constructor->getNumberOfRequiredParameters() > 0) {
+            $this->add(new $commandClass($github));
+        } else {
+            $this->add(new $commandClass);
+        }
     }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de2dfc8 and 8d56673.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • .claude/settings.local.json (1 hunks)
  • .phpactor.json (1 hunks)
  • composer.json (1 hunks)
  • config/github-zero.php (1 hunks)
  • examples/filtering-demo.php (1 hunks)
  • src/Application.php (1 hunks)
  • src/Commands/CloneCommand.php (6 hunks)
  • src/Commands/IssueCommand.php (1 hunks)
  • src/Commands/IssuesCommand.php (1 hunks)
  • src/Commands/ListCommand.php (1 hunks)
  • src/Commands/RepoCommand.php (1 hunks)
  • src/Commands/ReposCommand.php (10 hunks)
  • src/ConduitExtension.php (3 hunks)
  • src/GitHubZeroServiceProvider.php (1 hunks)
  • src/Support/FilterableGitHubData.php (1 hunks)
  • test-package.php (5 hunks)
  • tests/ApplicationTest.php (1 hunks)
  • tests/Pest.php (1 hunks)
  • tests/Support/FilterableGitHubDataTest.php (1 hunks)
  • tests/TestCase.php (1 hunks)
🧰 Additional context used
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: The package uses the jordanpartridge/github-client package for all GitHub API operations, ensuring a consistent integration layer.
composer.json (2)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: The package uses the jordanpartridge/github-client package for all GitHub API operations, ensuring a consistent integration layer.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Interactive prompts in CLI commands should utilize Laravel\Prompts for a rich terminal UI experience.
tests/Pest.php (2)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Testing should be performed using the Pest framework, as indicated by the use of ./vendor/bin/pest.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Dependency injection should be used to provide the GitHub client to command classes, promoting testability and modularity.
src/ConduitExtension.php (5)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Dependency injection should be used to provide the GitHub client to command classes, promoting testability and modularity.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: The package uses the jordanpartridge/github-client package for all GitHub API operations, ensuring a consistent integration layer.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: All commands that interact with the GitHub API must require the GITHUB_TOKEN environment variable for authentication.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Command classes should validate the presence and correctness of the GitHub token in their handle() method before proceeding with API operations.
test-package.php (2)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.746Z
Learning: Testing should be performed using the Pest framework, as indicated by the use of ./vendor/bin/pest.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: All commands that interact with the GitHub API must require the GITHUB_TOKEN environment variable for authentication.
src/Commands/ListCommand.php (1)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
tests/ApplicationTest.php (1)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Testing should be performed using the Pest framework, as indicated by the use of ./vendor/bin/pest.
src/GitHubZeroServiceProvider.php (3)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Dependency injection should be used to provide the GitHub client to command classes, promoting testability and modularity.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: The package uses the jordanpartridge/github-client package for all GitHub API operations, ensuring a consistent integration layer.
src/Commands/CloneCommand.php (5)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Dependency injection should be used to provide the GitHub client to command classes, promoting testability and modularity.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Interactive prompts in CLI commands should utilize Laravel\Prompts for a rich terminal UI experience.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: The package uses the jordanpartridge/github-client package for all GitHub API operations, ensuring a consistent integration layer.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: All commands that interact with the GitHub API must require the GITHUB_TOKEN environment variable for authentication.
src/Commands/IssueCommand.php (1)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
src/Commands/RepoCommand.php (2)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Dependency injection should be used to provide the GitHub client to command classes, promoting testability and modularity.
src/Application.php (1)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Dependency injection should be used to provide the GitHub client to command classes, promoting testability and modularity.
src/Commands/IssuesCommand.php (2)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Dependency injection should be used to provide the GitHub client to command classes, promoting testability and modularity.
src/Commands/ReposCommand.php (3)
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: In this repository, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: All commands that interact with the GitHub API must require the GITHUB_TOKEN environment variable for authentication.
Learnt from: CR
PR: jordanpartridge/github-zero#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-27T07:38:04.768Z
Learning: Command classes should validate the presence and correctness of the GitHub token in their handle() method before proceeding with API operations.
🧬 Code Graph Analysis (3)
tests/Pest.php (1)
tests/TestCase.php (1)
  • TestCase (9-15)
src/Commands/ListCommand.php (6)
src/Commands/IssueCommand.php (2)
  • configure (13-18)
  • execute (20-51)
src/Commands/CloneCommand.php (2)
  • configure (27-35)
  • execute (37-61)
src/Commands/RepoCommand.php (2)
  • configure (13-18)
  • execute (20-51)
src/Commands/IssuesCommand.php (2)
  • configure (29-43)
  • execute (45-70)
src/Commands/ReposCommand.php (2)
  • configure (29-44)
  • execute (46-81)
src/ConduitExtension.php (2)
  • commands (40-47)
  • description (32-35)
examples/filtering-demo.php (1)
src/Support/FilterableGitHubData.php (11)
  • FilterableGitHubData (10-364)
  • repositories (25-28)
  • count (296-299)
  • language (41-47)
  • get (280-283)
  • minStars (84-87)
  • query (216-275)
  • visibility (92-99)
  • sortBy (192-200)
  • limit (205-211)
  • stats (331-340)
🔇 Additional comments (24)
config/github-zero.php (1)

55-55: Good practice: File ends with newline.

Adding a newline at the end of the file follows POSIX standards and prevents potential issues with certain tools.

.phpactor.json (1)

2-2: Verify the schema path is correct.

The schema path /phpactor.schema.json appears to be an absolute path from the root directory, which may not exist on all systems. Consider using a relative path or a URL to the official schema.

#!/bin/bash
# Check if phpactor.schema.json exists in common locations
fd -t f "phpactor.schema.json" -d 3

# Check for .phpactor.json files in the repo to see common schema patterns
fd -t f ".phpactor.json" -x head -n 5 {} \;
composer.json (1)

16-16: Dependency update aligns with PR objectives.

The update to github-client ^2.2.0 is necessary for the new search functionality mentioned in the PR summary.

src/Support/FilterableGitHubData.php (1)

10-364: Well-designed filtering utility with fluent interface.

The class demonstrates good design patterns:

  • Immutable filtering with method chaining
  • Clear separation of repository vs issue concerns
  • Comprehensive natural language query support
  • Good documentation

The implementation aligns well with the PR objectives for enhanced filtering capabilities.

tests/Pest.php (1)

1-5: LGTM! Standard Pest framework bootstrap configuration.

The setup correctly configures all tests in the directory to use the custom TestCase class, following Pest framework conventions.

.claude/settings.local.json (1)

1-15: Well-configured security boundaries for development environment.

The allowed bash commands are appropriate for the project's toolchain and development needs, including Pest testing, Composer operations, and GitHub CLI interactions.

src/ConduitExtension.php (2)

8-8: Proper import addition for new command.

The import follows the established pattern and namespace convention.


45-45: Consistent command registration.

The IssuesCommand is properly registered following the same pattern as existing commands, maintaining consistency in the extension's command mapping.

tests/ApplicationTest.php (1)

7-12: Clean test for application metadata.

The test correctly verifies the application name and version using Pest's fluent expectation syntax.

test-package.php (5)

10-11: Improved string concatenation formatting.

Consistent spacing around concatenation operators enhances readability.


24-24: Cleaner conditional spacing.

The spacing improvement makes the conditional check more readable.


78-84: Consistent object instantiation and string formatting.

Removing unnecessary parentheses in object instantiation and consistent concatenation spacing improves code style.


94-99: Consistent formatting improvements.

The stylistic changes maintain consistency with the rest of the file's formatting improvements.


131-131: Final formatting consistency.

The concatenation spacing aligns with the formatting improvements throughout the file.

src/Commands/IssueCommand.php (1)

20-51: The command logic is well-implemented.

The output formatting, command categorization, and examples are clear and helpful. When switching to Illuminate\Console\Command, this method will need to be renamed to handle() and the parameters will change to use Laravel's console output methods.

src/Commands/ListCommand.php (1)

27-67: Excellent command organization and user guidance.

The categorization of commands is logical and comprehensive, and the tips section provides valuable guidance for users. The output formatting is clean and consistent.

src/Commands/RepoCommand.php (1)

20-51: Well-structured repository command discovery.

The command provides clear organization of repository-related functionality with helpful examples. The implementation follows good patterns established in the other discovery commands.

src/GitHubZeroServiceProvider.php (1)

27-34: Command registration is properly implemented.

The service provider correctly registers all new commands using the appropriate Laravel patterns. The console-only registration is appropriate.

Note: Once the command classes are updated to extend Illuminate\Console\Command as suggested in the other reviews, ensure the registration continues to work properly.

examples/filtering-demo.php (2)

8-11: Clean demo setup with proper imports.

The autoloader inclusion and import statement are correctly implemented for the demo script.


13-59: Realistic sample data enhances demo value.

The sample repository data closely matches GitHub API response structure and includes realistic examples (Laravel, Vue, etc.) that users can easily relate to. This makes the demo more effective for understanding real-world usage.

tests/Support/FilterableGitHubDataTest.php (1)

7-67: Well-structured test data setup.

The sample data provides good coverage for testing various filtering scenarios with diverse attributes including different languages, star counts, visibility states, and issue properties.

src/Commands/CloneCommand.php (2)

30-31: Good command naming consistency.

The rename from ghz:clone to repo:clone aligns well with the new command organization where repository-related commands are grouped under the repo namespace.


131-137: Good security practice using Symfony Process.

Using Symfony's Process component instead of exec() prevents shell injection vulnerabilities when executing the git clone command. This is the correct approach.

src/Commands/ReposCommand.php (1)

250-276: Well-implemented natural language query parsing.

The regex-based parsing handles common query patterns effectively, extracting language and star filters from natural language input. Good user experience enhancement.

Comment on lines +156 to +163
public function assignedTo(string $username): self
{
return $this->filter(function ($item) use ($username) {
$assignee = $item['assignee']['login'] ?? null;

return $assignee === $username;
});
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use safe array access for nested assignee data.

Direct access to $item['assignee']['login'] could cause warnings if 'assignee' key doesn't exist.

 public function assignedTo(string $username): self
 {
     return $this->filter(function ($item) use ($username) {
-        $assignee = $item['assignee']['login'] ?? null;
+        $assignee = isset($item['assignee']['login']) ? $item['assignee']['login'] : null;

         return $assignee === $username;
     });
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function assignedTo(string $username): self
{
return $this->filter(function ($item) use ($username) {
$assignee = $item['assignee']['login'] ?? null;
return $assignee === $username;
});
}
public function assignedTo(string $username): self
{
return $this->filter(function ($item) use ($username) {
$assignee = isset($item['assignee']['login'])
? $item['assignee']['login']
: null;
return $assignee === $username;
});
}
🤖 Prompt for AI Agents
In src/Support/FilterableGitHubData.php around lines 156 to 163, the code
directly accesses $item['assignee']['login'], which can cause warnings if the
'assignee' key is missing. Modify the code to safely check if 'assignee' exists
and is an array before accessing 'login', using conditional checks or null
coalescing to avoid warnings.

Comment on lines +10 to +20
class FilterableGitHubData
{
private Collection $data;

private string $type;

public function __construct(array $data, string $type)
{
$this->data = collect($data);
$this->type = $type;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add type validation and use constants for valid types.

The constructor accepts any string for $type but only 'repository' and 'issue' are valid. Consider adding validation and using constants.

 class FilterableGitHubData
 {
+    private const TYPE_REPOSITORY = 'repository';
+    private const TYPE_ISSUE = 'issue';
+    private const VALID_TYPES = [self::TYPE_REPOSITORY, self::TYPE_ISSUE];
+
     private Collection $data;

     private string $type;

     public function __construct(array $data, string $type)
     {
+        if (!in_array($type, self::VALID_TYPES)) {
+            throw new \InvalidArgumentException("Invalid type: {$type}. Valid types are: " . implode(', ', self::VALID_TYPES));
+        }
         $this->data = collect($data);
         $this->type = $type;
     }

Then update the static constructors:

     public static function repositories(array $repos): self
     {
-        return new self($repos, 'repository');
+        return new self($repos, self::TYPE_REPOSITORY);
     }

     public static function issues(array $issues): self
     {
-        return new self($issues, 'issue');
+        return new self($issues, self::TYPE_ISSUE);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class FilterableGitHubData
{
private Collection $data;
private string $type;
public function __construct(array $data, string $type)
{
$this->data = collect($data);
$this->type = $type;
}
class FilterableGitHubData
{
private const TYPE_REPOSITORY = 'repository';
private const TYPE_ISSUE = 'issue';
private const VALID_TYPES = [self::TYPE_REPOSITORY, self::TYPE_ISSUE];
private Collection $data;
private string $type;
public function __construct(array $data, string $type)
{
if (! in_array($type, self::VALID_TYPES, true)) {
throw new \InvalidArgumentException(
"Invalid type: {$type}. Valid types are: " . implode(', ', self::VALID_TYPES)
);
}
$this->data = collect($data);
$this->type = $type;
}
public static function repositories(array $repos): self
{
return new self($repos, self::TYPE_REPOSITORY);
}
public static function issues(array $issues): self
{
return new self($issues, self::TYPE_ISSUE);
}
// ... other methods ...
}
🤖 Prompt for AI Agents
In src/Support/FilterableGitHubData.php around lines 10 to 20, the constructor
accepts any string for the $type parameter without validation, but only
'repository' and 'issue' are valid types. Define class constants for these valid
types and add validation in the constructor to check if the provided $type
matches one of these constants. If the $type is invalid, throw an appropriate
exception. Also, update any static constructors to use these constants instead
of hardcoded strings.

Comment on lines +192 to +200
public function sortBy(string $field, string $direction = 'desc'): self
{
$ascending = strtolower($direction) === 'asc';

return new self(
$this->data->sortBy($field, SORT_REGULAR, ! $ascending)->values()->toArray(),
$this->type
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using appropriate sort flags for different data types.

Using SORT_REGULAR for all fields might not sort numeric fields like 'stargazers_count' correctly. Consider detecting the field type or allowing sort flag customization.

 public function sortBy(string $field, string $direction = 'desc'): self
 {
     $ascending = strtolower($direction) === 'asc';
+    
+    // Determine sort flag based on common numeric fields
+    $numericFields = ['stargazers_count', 'forks_count', 'open_issues_count', 'size'];
+    $sortFlag = in_array($field, $numericFields) ? SORT_NUMERIC : SORT_REGULAR;

     return new self(
-        $this->data->sortBy($field, SORT_REGULAR, ! $ascending)->values()->toArray(),
+        $this->data->sortBy($field, $sortFlag, ! $ascending)->values()->toArray(),
         $this->type
     );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function sortBy(string $field, string $direction = 'desc'): self
{
$ascending = strtolower($direction) === 'asc';
return new self(
$this->data->sortBy($field, SORT_REGULAR, ! $ascending)->values()->toArray(),
$this->type
);
}
public function sortBy(string $field, string $direction = 'desc'): self
{
$ascending = strtolower($direction) === 'asc';
// Determine sort flag based on common numeric fields
$numericFields = ['stargazers_count', 'forks_count', 'open_issues_count', 'size'];
$sortFlag = in_array($field, $numericFields) ? SORT_NUMERIC : SORT_REGULAR;
return new self(
$this->data->sortBy($field, $sortFlag, ! $ascending)->values()->toArray(),
$this->type
);
}
🤖 Prompt for AI Agents
In src/Support/FilterableGitHubData.php around lines 192 to 200, the sortBy
method uses SORT_REGULAR for all fields, which may not correctly sort numeric
fields like 'stargazers_count'. Modify the method to detect the data type of the
field being sorted or add an optional parameter to specify the sort flag (e.g.,
SORT_NUMERIC for numbers). Use the appropriate sort flag when calling
$this->data->sortBy to ensure correct sorting behavior based on the field type.

Comment on lines +104 to +113
public function updatedSince(string $date): self
{
$since = Carbon::parse($date);

return $this->filter(function ($item) use ($since) {
$updated = Carbon::parse($item['updated_at'] ?? null);

return $updated->gte($since);
});
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for date parsing.

Carbon::parse() can throw exceptions for invalid date strings. Consider adding try-catch blocks or validation to handle invalid dates gracefully.

 public function updatedSince(string $date): self
 {
-    $since = Carbon::parse($date);
+    try {
+        $since = Carbon::parse($date);
+    } catch (\Exception $e) {
+        // Return unfiltered data or handle error appropriately
+        return $this;
+    }

     return $this->filter(function ($item) use ($since) {
-        $updated = Carbon::parse($item['updated_at'] ?? null);
+        if (!isset($item['updated_at'])) {
+            return false;
+        }
+        try {
+            $updated = Carbon::parse($item['updated_at']);
+        } catch (\Exception $e) {
+            return false;
+        }

         return $updated->gte($since);
     });
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function updatedSince(string $date): self
{
$since = Carbon::parse($date);
return $this->filter(function ($item) use ($since) {
$updated = Carbon::parse($item['updated_at'] ?? null);
return $updated->gte($since);
});
}
public function updatedSince(string $date): self
{
try {
$since = Carbon::parse($date);
} catch (\Exception $e) {
// Return unfiltered data or handle error appropriately
return $this;
}
return $this->filter(function ($item) use ($since) {
if (!isset($item['updated_at'])) {
return false;
}
try {
$updated = Carbon::parse($item['updated_at']);
} catch (\Exception $e) {
return false;
}
return $updated->gte($since);
});
}
🤖 Prompt for AI Agents
In src/Support/FilterableGitHubData.php around lines 104 to 113, the method
updatedSince uses Carbon::parse without handling exceptions, which can cause
errors if the date string is invalid. Add try-catch blocks around the
Carbon::parse calls for both the input $date and the item's 'updated_at' field
to catch parsing exceptions. In the catch block, handle the error gracefully,
such as by returning false in the filter callback or throwing a custom exception
with a clear message for the input date.

Comment on lines +7 to +11
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class IssueCommand extends Command
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use Illuminate\Console\Command for consistency across environments.

Based on the retrieved learnings, command classes should extend Illuminate\Console\Command to ensure consistency across standalone CLI, Laravel, and Conduit environments, rather than the Symfony base class.

Apply this diff to fix the base class:

-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
+use Illuminate\Console\Command;

-class IssueCommand extends Command
+class IssueCommand extends Command
-{
-    protected function configure(): void
-    {
-        $this
-            ->setName('issue')
-            ->setDescription('Show available issue commands');
-    }
-
-    protected function execute(InputInterface $input, OutputInterface $output): int
+{
+    protected $signature = 'issue';
+    protected $description = 'Show available issue commands';
+
+    public function handle(): int

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/Commands/IssueCommand.php around lines 7 to 11, the class currently
extends Symfony's Command class, but it should extend Illuminate\Console\Command
for consistency across environments. Replace the import of
Symfony\Component\Console\Command\Command with Illuminate\Console\Command and
update the class to extend this Illuminate base class instead.

Comment on lines +7 to +11
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class RepoCommand extends Command
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use Illuminate\Console\Command for consistency across environments.

This command also extends the wrong base class. Apply the same fix as the other command classes to use Illuminate\Console\Command.

🤖 Prompt for AI Agents
In src/Commands/RepoCommand.php around lines 7 to 11, the class extends
Symfony's Command instead of Illuminate's. Change the import statement to use
Illuminate\Console\Command and update the class to extend
Illuminate\Console\Command for consistency with other command classes.

Comment on lines 143 to 145
if ($directory && confirm("📂 Open {$directory} in your editor?", false)) {
exec("code \"{$directory}\"");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security: Potential command injection vulnerability.

The exec() function is used with a potentially user-controlled $directory variable without proper escaping. This could allow command injection attacks.

Use escapeshellarg() to properly escape the directory parameter:

-            if ($directory && confirm("📂 Open {$directory} in your editor?", false)) {
-                exec("code \"{$directory}\"");
-            }
+            if ($directory && confirm("📂 Open {$directory} in your editor?", false)) {
+                $escapedDirectory = escapeshellarg($directory);
+                exec("code {$escapedDirectory}");
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ($directory && confirm("📂 Open {$directory} in your editor?", false)) {
exec("code \"{$directory}\"");
}
if ($directory && confirm("📂 Open {$directory} in your editor?", false)) {
$escapedDirectory = escapeshellarg($directory);
exec("code {$escapedDirectory}");
}
🤖 Prompt for AI Agents
In src/Commands/CloneCommand.php around lines 143 to 145, the exec() call uses
the $directory variable directly, which can lead to command injection
vulnerabilities. To fix this, wrap the $directory variable with escapeshellarg()
before passing it to exec(), ensuring the directory path is safely escaped and
preventing injection attacks.

Comment on lines +322 to +327
$selectedIssue = collect($issues)->firstWhere('number', $selectedNumber);

if ($selectedIssue) {
$this->showIssueDetails($selectedIssue, $repository, $output);
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid Laravel-specific helpers for better compatibility.

Using collect() creates a dependency on Laravel's support package which might not be available in standalone mode.

Replace with standard PHP array functions:

-        $selectedIssue = collect($issues)->firstWhere('number', $selectedNumber);
+        $selectedIssue = null;
+        foreach ($issues as $issue) {
+            if ($issue['number'] === $selectedNumber) {
+                $selectedIssue = $issue;
+                break;
+            }
+        }
🤖 Prompt for AI Agents
In src/Commands/IssuesCommand.php around lines 322 to 327, the use of Laravel's
collect() helper creates an unnecessary dependency on Laravel's support package.
Replace collect($issues)->firstWhere('number', $selectedNumber) with a standard
PHP array function such as a foreach loop to iterate over $issues and find the
first element where 'number' equals $selectedNumber, then assign it to
$selectedIssue. This removes the Laravel-specific helper and improves
compatibility.

Comment on lines +137 to +143
[$owner, $repo] = explode('/', $repository, 2);

$rawIssues = spin(
fn () => $this->github->issues()->list($owner, $repo, [
'state' => $options['state'],
'per_page' => min($fetchLimit, 100),
])->json(),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation for repository format.

The explode() function assumes the repository is in "owner/repo" format, but there's no validation. This will cause an error if the format is incorrect.

Add validation before parsing:

             // Fetch more issues for better filtering
             $fetchLimit = max(100, (int)$options['limit'] * 2);
             
+            if (!str_contains($repository, '/')) {
+                $output->writeln('<error>❌ Invalid repository format. Use: owner/repo</error>');
+                return 1;
+            }
+            
             [$owner, $repo] = explode('/', $repository, 2);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[$owner, $repo] = explode('/', $repository, 2);
$rawIssues = spin(
fn () => $this->github->issues()->list($owner, $repo, [
'state' => $options['state'],
'per_page' => min($fetchLimit, 100),
])->json(),
// Fetch more issues for better filtering
$fetchLimit = max(100, (int)$options['limit'] * 2);
if (!str_contains($repository, '/')) {
$output->writeln('<error>❌ Invalid repository format. Use: owner/repo</error>');
return 1;
}
[$owner, $repo] = explode('/', $repository, 2);
$rawIssues = spin(
fn () => $this->github->issues()->list($owner, $repo, [
'state' => $options['state'],
'per_page' => min($fetchLimit, 100),
])->json(),
🤖 Prompt for AI Agents
In src/Commands/IssuesCommand.php around lines 137 to 143, the code uses
explode() on the repository string assuming it is in "owner/repo" format without
validation, which can cause errors if the format is incorrect. Add a validation
step before parsing to check if the repository string contains exactly one '/'
character and both owner and repo parts are non-empty. If the validation fails,
handle the error gracefully, for example by showing an error message and
stopping execution.

Comment on lines 221 to 223
if (!function_exists('config')) {
return null;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve standalone mode detection.

Checking for the config() function existence is a fragile way to detect standalone mode. Consider using a more explicit approach.

Add a property or method to detect the environment:

+    private function isStandaloneMode(): bool
+    {
+        // Check for Laravel/Conduit environment more reliably
+        return !class_exists(\Illuminate\Support\Facades\App::class);
+    }
+    
     private function buildSearchQuery(array $options): ?string
     {
         // Disable search in standalone mode due to Laravel dependency issues
-        if (!function_exists('config')) {
+        if ($this->isStandaloneMode()) {
             return null;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!function_exists('config')) {
return null;
}
// Add this helper to detect standalone mode more explicitly
private function isStandaloneMode(): bool
{
// Check for Laravel/Conduit environment more reliably
return !class_exists(\Illuminate\Support\Facades\App::class);
}
private function buildSearchQuery(array $options): ?string
{
// Disable search in standalone mode due to Laravel dependency issues
if ($this->isStandaloneMode()) {
return null;
}
// ... rest of existing search query logic ...
}
🤖 Prompt for AI Agents
In src/Commands/ReposCommand.php around lines 221 to 223, the current check for
standalone mode by verifying if the function 'config' exists is fragile. Replace
this with a more explicit environment detection by adding a dedicated property
or method in the class that clearly determines if the application is running in
standalone mode. Use this property or method instead of the function_exists
check to improve reliability and clarity.

- Update to github-client v2.3.0 with simple DTOs (no Laravel dependencies)
- Enable GitHub's native search API in standalone mode
- Update connector import to use conduit-ui/github-connector
- Remove Laravel Data dependency restrictions
- Support dev-master stability for cutting-edge features

Search functionality now works perfectly:
- Fast GitHub API search with proper query building
- Natural language query parsing (e.g., 'laravel', 'php projects')
- Graceful fallback to repository listing when needed
- Framework agnostic - works in any PHP environment
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.

1 participant