Skip to content

chore: restrict sourcery to local only, skip in CI#110

Merged
JacobCoffee merged 1 commit intomainfrom
chore/sourcery-local-only
Nov 23, 2025
Merged

chore: restrict sourcery to local only, skip in CI#110
JacobCoffee merged 1 commit intomainfrom
chore/sourcery-local-only

Conversation

@JacobCoffee
Copy link
Owner

@JacobCoffee JacobCoffee commented Nov 23, 2025

Summary

  • Adds stages: [commit] to Sourcery hook configuration in both .prek-config.yaml and .pre-commit-config.yaml
  • Ensures Sourcery only runs during local commits
  • Skips Sourcery when CI runs prek with --hook-stage manual

Changes

  • Modified .prek-config.yaml: Added stages: [commit] to Sourcery hook (line 31)
  • Modified .pre-commit-config.yaml: Added stages: [commit] to Sourcery hook (line 31)

Testing

✅ Verified Sourcery is skipped when running prek run --hook-stage manual (CI behavior)
✅ Verified Sourcery runs when running prek run --hook-stage pre-commit (local commits)

Result

Sourcery feedback remains available for developers locally while being excluded from CI workflows, reducing CI runtime.

🤖 Generated with Claude Code

Summary by Sourcery

Introduce a new Discord bot service and migrate the project build configuration to uv, while tightening developer tooling and CI targets.

New Features:

  • Add a dedicated Discord bot service package that integrates with the existing API service for guild management and provides commands for administration, forums, GitHub, Astral tools, Python PEP lookup, configuration, and testing.
  • Introduce reusable Discord UI views and utility modules for embeds, configuration flows, help threads, and formatting/linking helpers shared across bot plugins.
  • Add a typed configuration system for the bot and logging using pydantic settings and environment-based configuration.

Enhancements:

  • Refine command error handling and logging with centralized, environment-aware logging configuration and richer error embeds.
  • Improve help forum workflows by adding views and commands to manage thread lifecycle (solve/close) and guide users toward better problem reports (MCVE, paste links).

Build:

  • Migrate project and sub-packages from hatchling to uv_build as the build backend and align workspace configuration with the new bot service.
  • Update static analysis configuration (ty extra-paths) to include the new bot service source tree.

CI:

  • Adjust Makefile aggregate targets to use lint, type-check, fmt, and test for check-all and add a dedicated ci target.
  • Restrict the Sourcery pre-commit hook to run only on local commits so it is skipped in CI runs.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 23, 2025

Reviewer's Guide

Introduces a new Discord bot microservice (services/bot) with its own configuration, plugins, views, and utilities; migrates build backends from Hatchling to uv_build; updates tooling paths and Makefile targets; and restricts Sourcery to run only on local commits (not CI).

Sequence diagram for guild join and API-backed guild registration

sequenceDiagram
  actor U as "Discord Guild Owner"
  participant Discord as "Discord Gateway"
  participant Bot as "Byte (discord.Bot)"
  participant APIClient as "ByteAPIClient"
  participant API as "API Service (/api/guilds/create)"
  participant DB as "Database"

  U->>Discord: "Invite bot to guild"
  Discord->>Bot: "GUILD_CREATE event"
  Bot->>Bot: "on_guild_join(guild) handler"
  Bot->>Bot: "self.tree.sync(guild)"
  Bot->>APIClient: "Construct API URL for /api/guilds/create\n?guild_id=&guild_name="
  Bot->>API: "HTTP POST /api/guilds/create\nwith guild_id, guild_name"
  API->>DB: "Create guild record if not exists"
  DB-->>API: "Persisted guild data"
  API-->>Bot: "201 CREATED or error status"
  Bot->>Bot: "Build status Embed (success or failure)"
  Bot->>Discord: "Send embed to dev guild internal channel"
  Discord-->>U: "(Optionally) Feedback via dev guild logging"
Loading

Class diagram for core bot, configuration, and API client

classDiagram
  class Byte {
    +Byte(command_prefix: list[str], intents: Intents, activity: Activity)
    +setup_hook() async
    +load_cogs() async
    +on_ready() async
    +on_message(message: Message) async
    +on_command_error(ctx: Context, error: CommandError) async
    +on_member_join(member: Member) async
    +on_guild_join(guild: discord.Guild) async
  }

  class BotEntrypoint {
    +main() void
    +run_bot() void
  }

  class BotSettings {
    +discord_token: str
    +discord_dev_guild_id: int | None
    +discord_dev_user_id: int | None
    +command_prefix: list[str]
    +presence_url: str
    +api_service_url: str
    +plugins_dir: Path
    +environment: str
    +debug: bool
    +assemble_command_prefix(value: list[str]) list[str]
    +assemble_presence_url(value: str) str
    +get_discord_token(value: str | None) str
  }

  class LogSettingsBot {
    +level: int
    +discord_level: int
    +websockets_level: int
    +asyncio_level: int
    +httpx_level: int
    +format: str
    +file: Path | None
  }

  class DiscordSettings {
    +TOKEN: str
    +COMMAND_PREFIX: list[str]
    +DEV_GUILD_ID: int
    +DEV_USER_ID: int
    +DEV_GUILD_INTERNAL_ID: int
    +PLUGINS_LOC: Path
    +PLUGINS_DIRS: list[Path]
    +PRESENCE_URL: str
    +assemble_command_prefix(value: list[str]) list[str]
    +assemble_presence_url(value: str) str
  }

  class ProjectLogSettings {
    +LEVEL: int
    +DISCORD_LEVEL: int
    +WEBSOCKETS_LEVEL: int
    +ASYNCIO_LEVEL: int
    +HTTP_CORE_LEVEL: int
    +HTTPX_LEVEL: int
    +FORMAT: str
    +FILE: Path
  }

  class ProjectSettings {
    +DEBUG: bool
    +ENVIRONMENT: str
    +VERSION: str
  }

  class SettingsModule {
    +discord: DiscordSettings
    +log: ProjectLogSettings
    +project: ProjectSettings
    +load_settings() (DiscordSettings, ProjectLogSettings, ProjectSettings)
  }

  class LogModule {
    +setup_logging() void
    +get_logger(name: str) Logger
  }

  class APIError {
    +status_code: int | None
    +APIError(message: str, status_code: int | None)
  }

  class ByteAPIClient {
    -base_url: str
    -client: httpx.AsyncClient
    +ByteAPIClient(base_url: str, timeout: float = 10.0)
    +close() async
    +__aenter__() async Self
    +__aexit__(*args: object) async void
    +create_guild(guild_id: int, guild_name: str, prefix: str = "!", **kwargs) async GuildSchema
    +get_guild(guild_id: int) async GuildSchema | None
    +update_guild(guild_id: UUID, **updates) async GuildSchema
    +delete_guild(guild_id: UUID) async None
    +get_or_create_guild(guild_id: int, guild_name: str, prefix: str = "!") async GuildSchema
    +health_check() async dict[str, Any]
  }

  class ChecksModule {
    +is_guild_admin() Check
    +is_byte_dev() Check
  }

  BotEntrypoint --> Byte : "creates and runs"
  Byte --> SettingsModule : "uses for config"
  Byte --> LogModule : "uses get_logger()"
  Byte --> ByteAPIClient : "uses for API calls (guilds, health)"
  SettingsModule --> DiscordSettings
  SettingsModule --> ProjectLogSettings
  SettingsModule --> ProjectSettings
  LogModule --> ProjectLogSettings : "reads log settings"
  ByteAPIClient --> APIError : "raises on failures"
Loading

Class diagram for views and plugins in the new bot service

classDiagram
  class ButtonEmbedView {
    +author_id: int
    +bot: Bot
    +original_embed: Embed
    +minified_embed: Embed
    +ButtonEmbedView(author: int, bot: Bot, original_embed: Embed, minified_embed: Embed, *args, **kwargs)
    +delete_interaction_check(interaction: Interaction) async bool
    +delete_button_callback(interaction: Interaction) async void
    +learn_more_button_callback(interaction: Interaction) async void
    +delete_button(interaction: Interaction, button: Button) async void
    +learn_more_button(interaction: Interaction, button: Button) async void
  }

  class ExtendedEmbed {
    +add_field_dict(field: Field) ExtendedEmbed
    +add_field_dicts(fields: list[Field]) ExtendedEmbed
    +from_field_dicts(..., fields: list[Field] | None) ExtendedEmbed
    +deepcopy() ExtendedEmbed
  }

  class Field {
    +name: Any
    +value: Any
    +inline: bool (optional)
  }

  class RuffView {
  }

  class PEPView {
  }

  class HelpThreadView {
    +author: Member
    +guild_id: int
    +bot: Bot
    +HelpThreadView(author: Member, guild_id: int, bot: Bot, timeout: float | None)
    +setup() async void
    +delete_interaction_check(interaction: Interaction) async bool
    +solve_button_callback(interaction: Interaction, button: Button) async void
    +remove_button_callback(interaction: Interaction, button: Button) async void
  }

  class ConfigView {
    +ConfigView(preselected: str | None)
  }

  class ConfigKeyView {
    +ConfigKeyView(option: dict[str, Any])
  }

  class ConfigModal {
    +option: dict[str, Any] | None
    +ConfigModal(title: str, sub_setting: dict[str, str] | None, sub_settings: list[dict[str, str]] | None, option: dict[str, Any] | None)
    +on_submit(interaction: Interaction) async void
    +on_error(interaction: Interaction, error: Exception) async void
  }

  class ConfigSelect {
    +ConfigSelect(preselected: str | None)
    +callback(interaction: Interaction) async void
  }

  class ConfigKeySelect {
    +ConfigKeySelect(option: dict[str, Any])
    +callback(interaction: Interaction) async void
  }

  class AstralCog {
    +_rules: dict[str, RuffRule]
    +Astral(bot: Bot, rules: list[RuffRule])
    +_rule_autocomplete(interaction: Interaction, current_rule: str) async list[Choice[str]]
    +ruff_rule(interaction: Interaction, rule: str) async void
    +format_code(interaction: Interaction, code_block: str) async void
  }

  class PythonCog {
    +_peps: dict[int, PEP]
    +Python(bot: Bot, peps: list[PEP])
    +_pep_autocomplete(interaction: Interaction, current_pep: str) async list[Choice[str]]
    +peps(interaction: Interaction, pep: int) async void
  }

  class AdminCommands {
    +admin(ctx: Context) async void
    +list_cogs(ctx: Context) async void
    +reload(ctx: Context, cog: str) async void
    +reload_all_cogs(ctx: Context) async void
    +reload_single_cog(ctx: Context, cog: str, send_message: bool) async str
    +tree_sync(interaction: Interaction) async void
    +bootstrap_guild(ctx: Context, guild_id: int | None) async void
  }

  class ForumCommands {
    +solved(ctx: Context) async void
    +tags(ctx: Context) async void
    +tree_sync(interaction: Interaction, user: Member) async void
  }

  class EventsCog {
    +on_thread_create(thread: Thread) async void
  }

  class GeneralCommands {
    +show_paste(interaction: Interaction) async void
  }

  class ConfigCog {
    +config_options: list[dict[str, Any]]
    +Config(bot: Bot)
    +_config_autocomplete(interaction: Interaction, current: str) async list[Choice[str]]
    +config_rule(interaction: Interaction, setting: str | None) async void
  }

  RuffView --|> ButtonEmbedView
  PEPView --|> ButtonEmbedView
  ExtendedEmbed --|> Embed
  ButtonEmbedView --|> View
  HelpThreadView --|> View
  ConfigView --|> View
  ConfigKeyView --|> View
  ConfigModal --|> Modal
  ConfigSelect --|> Select
  ConfigKeySelect --|> Select

  AstralCog --|> Cog
  PythonCog --|> Cog
  AdminCommands --|> Cog
  ForumCommands --|> Cog
  EventsCog --|> Cog
  GeneralCommands --|> Cog
  ConfigCog --|> Cog

  AstralCog --> RuffView : "creates for /ruff"
  PythonCog --> PEPView : "creates for /pep"
  ForumCommands --> HelpThreadView : "invokes solve via button"
  ConfigCog --> ConfigView
  ConfigView --> ConfigSelect
  ConfigKeyView --> ConfigKeySelect
  ConfigSelect --> ConfigModal
  ConfigKeySelect --> ConfigModal
Loading

File-Level Changes

Change Details Files
Switch build backend to uv_build and register new bot service in the workspace.
  • Replace Hatchling build system configuration with uv_build in root and subproject pyproject files.
  • Configure uv workspace members to include the new bot service.
  • Define uv_build module-root for flat package layout in the main project.
pyproject.toml
packages/byte-common/pyproject.toml
services/api/pyproject.toml
services/bot/pyproject.toml
Add a new Discord bot service with configuration, entrypoint, and HTTP API client to talk to the API service instead of direct DB access.
  • Create a dedicated bot configuration module using pydantic settings and environment variables.
  • Add a bot runtime module that wires up discord.py intents, commands, cogs, and API bootstrapping on guild join.
  • Implement an async HTTP client wrapper for the API service, including guild CRUD operations and health checks.
  • Provide a CLI entrypoint for starting the bot via a console script.
services/bot/src/byte_bot/config.py
services/bot/src/byte_bot/bot.py
services/bot/src/byte_bot/api_client.py
services/bot/src/byte_bot/__main__.py
services/bot/src/byte_bot/__init__.py
Implement bot-side libraries for settings, logging, utility functions, and shared types.
  • Introduce settings models for Discord, logging, and project-level configuration reused across bot modules.
  • Add centralized logging configuration with rich console integration and rotating file/syslog handlers.
  • Provide utility helpers for chunking, Ruff integration, PEP and Ruff rule fetching, pastebin uploads, and time calculation.
  • Define TypedDict-based schemas for PEPs and Ruff rules used by plugins and views.
services/bot/src/byte_bot/lib/settings.py
services/bot/src/byte_bot/lib/log.py
services/bot/src/byte_bot/lib/utils.py
services/bot/src/byte_bot/lib/types/python.py
services/bot/src/byte_bot/lib/types/astral.py
services/bot/src/byte_bot/lib/__init__.py
Add reusable Discord views and helpers for embeds, configuration flows, forums, and Astral/Python features.
  • Create base button-enabled embed view and an extended embed helper for constructing complex embed payloads.
  • Add config UI components (dropdowns, modals, buttons) for interactive guild configuration flows.
  • Implement views used by Astral (Ruff), Python (PEPs), and forum help interactions, including solve/remove actions.
  • Add mention-formatting helpers and common configuration options metadata.
services/bot/src/byte_bot/views/abstract_views.py
services/bot/src/byte_bot/views/config.py
services/bot/src/byte_bot/views/forums.py
services/bot/src/byte_bot/views/astral.py
services/bot/src/byte_bot/views/python.py
services/bot/src/byte_bot/views/__init__.py
services/bot/src/byte_bot/lib/common/__init__.py
services/bot/src/byte_bot/lib/common/mention.py
Add a suite of Discord bot plugins (cogs) for admin, config, events, forums, GitHub, Astral (Ruff), Python (PEPs), and general commands.
  • Create admin commands for cog management, tree sync, and guild bootstrapping into the API-backed database.
  • Provide Astral/Ruff commands for rule lookup and (future) formatting, plus Python commands for PEP lookup.
  • Add GitHub issue creation flows from Discord messages for both generic and Litestar-specific contexts.
  • Implement forum helpers for help threads, MCVE prompts, and solve/close flows, as well as general paste guidance.
  • Register custom Litestar-specific commands and a testing/ping cog for validation.
services/bot/src/byte_bot/plugins/admin.py
services/bot/src/byte_bot/plugins/astral.py
services/bot/src/byte_bot/plugins/github.py
services/bot/src/byte_bot/plugins/custom/litestar.py
services/bot/src/byte_bot/plugins/forums.py
services/bot/src/byte_bot/plugins/events.py
services/bot/src/byte_bot/plugins/python.py
services/bot/src/byte_bot/plugins/general.py
services/bot/src/byte_bot/plugins/testing.py
services/bot/src/byte_bot/plugins/config.py
services/bot/src/byte_bot/plugins/__init__.py
services/bot/src/byte_bot/lib/checks.py
Wire the bot service into existing shared infrastructure and workspace modules.
  • Reference common shared package byte-common from the bot service via uv sources.
  • Update type-checker extra-paths to include the new bot service source tree.
  • Align shared imports for settings, logging, and DB-related helpers between bot and existing server code.
services/bot/pyproject.toml
pyproject.toml
services/bot/src/byte_bot/bot.py
services/bot/src/byte_bot/views/forums.py
services/bot/src/byte_bot/plugins/admin.py
services/bot/src/byte_bot/lib/log.py
Restrict Sourcery static analysis to run only on local commits and adjust CI targets.
  • Add stages: [commit] to the Sourcery hook in both pre-commit configurations so CI --hook-stage manual skips it.
  • Adjust the main Makefile check target to focus on linting, type-checking, formatting, and tests, and add a dedicated ci target.
.pre-commit-config.yaml
.prek-config.yaml
Makefile

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@railway-app
Copy link

railway-app bot commented Nov 23, 2025

🚅 Environment byte-pr-110 in byte has no services deployed.

@railway-app railway-app bot temporarily deployed to byte (byte / byte-pr-110) November 23, 2025 04:17 Destroyed
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • There are now two separate configuration systems for the bot (services/bot/src/byte_bot/config.py and services/bot/src/byte_bot/lib/settings.py) with overlapping responsibilities; consider consolidating them into a single settings source to avoid drift and confusion.
  • Several plugins (e.g., litestar, forums, events, admin) hard-code guild, channel, and role IDs; it would be more maintainable to move these IDs into configuration or a central mapping so they can be changed without touching code.
  • The GitHub issue modal logic is duplicated between the litestar-specific plugin and the generic github plugin; consider extracting a shared helper or base modal to reduce duplication and keep behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There are now two separate configuration systems for the bot (services/bot/src/byte_bot/config.py and services/bot/src/byte_bot/lib/settings.py) with overlapping responsibilities; consider consolidating them into a single settings source to avoid drift and confusion.
- Several plugins (e.g., litestar, forums, events, admin) hard-code guild, channel, and role IDs; it would be more maintainable to move these IDs into configuration or a central mapping so they can be changed without touching code.
- The GitHub issue modal logic is duplicated between the litestar-specific plugin and the generic github plugin; consider extracting a shared helper or base modal to reduce duplication and keep behavior consistent.

## Individual Comments

### Comment 1
<location> `Makefile:120` </location>
<code_context>
 	@$(UV) run --no-sync coverage xml

-check-all: lint test fmt-check coverage ## Run all linting, tests, and coverage checks
+check-all: lint type-check fmt test  ## Run all linting, tests, and coverage checks
+
+ci: check-all  ## Run all checks for CI
</code_context>

<issue_to_address>
**issue:** check-all description is now misleading since coverage is no longer run

Since `check-all` no longer runs `coverage`, consider either adding `coverage` back to the target or updating the help text so it accurately reflects what the target now does (and how `ci` relates to it).
</issue_to_address>

### Comment 2
<location> `services/bot/src/byte_bot/bot.py:98` </location>
<code_context>
+        embed.add_field(name="Guild", value=ctx.guild.name if ctx.guild else "DM")
+        embed.add_field(name="Location", value=f"[Jump]({ctx.message.jump_url})")
+        embed.set_footer(text=f"Time: {ctx.message.created_at.strftime('%Y-%m-%d %H:%M:%S')}")
+        await ctx.send(embed=embed, ephemeral=True)
+
+    @staticmethod
</code_context>

<issue_to_address>
**issue (bug_risk):** Using `ephemeral=True` with `ctx.send` is not supported and will be ignored or error

`ephemeral` is only valid on interaction responses (e.g., `InteractionResponse.send_message`), not on `Context.send`. Here it will either be ignored or may raise, depending on the library version. To make this message private, use an interaction-based response instead; otherwise remove `ephemeral=True`.
</issue_to_address>

### Comment 3
<location> `services/bot/src/byte_bot/plugins/events.py:3-12` </location>
<code_context>
+"""Plugins for events."""
+
+from threading import Thread
+from typing import cast
+
+from discord import Embed
+from discord.ext.commands import Bot, Cog
+
+from byte_bot.byte.lib.common.assets import litestar_logo_yellow
+from byte_bot.byte.lib.common.links import mcve
+from byte_bot.byte.lib.utils import linker
+from byte_bot.byte.views.forums import HelpThreadView
+
+__all__ = ("Events", "setup")
+
+
+class Events(Cog):
+    """Events cog."""
+
+    def __init__(self, bot: Bot) -> None:
+        """Initialize cog."""
+        self.bot = bot
+
+    @Cog.listener()
+    async def on_thread_create(self, thread: Thread) -> None:
+        """Handle thread create event.
+
</code_context>

<issue_to_address>
**issue (bug_risk):** Thread type annotation should use `discord.Thread`, not `threading.Thread`

Since this listener handles Discord thread creation, the parameter should be typed as `discord.Thread`, not `threading.Thread`, to reflect the actual runtime type and avoid confusing type checkers. Please import and use `discord.Thread` (or `from discord import Thread as DiscordThread`) here.
</issue_to_address>

### Comment 4
<location> `services/bot/src/byte_bot/plugins/admin.py:83` </location>
<code_context>
+    async def reload_single_cog(self, ctx: Context, cog: str, send_message: bool = True) -> str:
+        """Reload a single cog."""
+        try:
+            await self.bot.reload_extension(f"plugins.{cog}")
+            message = f"Cog `{cog}` reloaded!"
+        except (commands.ExtensionNotLoaded, commands.ExtensionNotFound) as e:
</code_context>

<issue_to_address>
**issue (bug_risk):** Reload path for cogs likely doesn’t match how they were originally loaded

`Byte.load_cogs` loads extensions using full module paths (e.g., `byte_bot...`), but `reload_extension` here assumes `plugins.{cog}`. If those don’t match, reloads will consistently raise `ExtensionNotLoaded`/`ExtensionNotFound`. Consider reloading by using the original extension name from `self.bot.extensions` (or persisting the full module path when listing cogs) instead of constructing `plugins.{cog}`.
</issue_to_address>

### Comment 5
<location> `services/bot/src/byte_bot/views/config.py:95` </location>
<code_context>
+        Args:
+            interaction: Interaction object.
+        """
+        selected_option = next(option for option in config_options if option["label"] == self.values[0])
+        if "sub_settings" in selected_option:
+            view = ConfigKeyView(selected_option)
</code_context>

<issue_to_address>
**issue:** `next(...)` without a default will raise if the selected label is missing from `config_options`

If `self.values[0]` doesn’t match any entry in `config_options`, this will raise `StopIteration` and break the interaction. Please pass a default to `next(...)` and handle the “not found” case (for example, by showing an error to the user) instead of letting it raise.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Adds stages restriction to Sourcery hook so it only runs locally
and is skipped when CI runs prek with --hook-stage manual.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@JacobCoffee JacobCoffee force-pushed the chore/sourcery-local-only branch from 8ab5c59 to 88f93fc Compare November 23, 2025 04:20
@railway-app railway-app bot temporarily deployed to byte (byte / byte-pr-110) November 23, 2025 04:20 Destroyed
@JacobCoffee JacobCoffee merged commit 98aaf11 into main Nov 23, 2025
4 of 5 checks passed
@JacobCoffee JacobCoffee deleted the chore/sourcery-local-only branch November 23, 2025 04:21
JacobCoffee added a commit that referenced this pull request Nov 23, 2025
- Resolve conflict with .prek-config.yaml (removed, using .pre-commit-config.yaml)
- Merge PR #110 (restrict sourcery to local only)
- Merge PR #112 (migrate to dependency-groups.dev)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
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