diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 00000000..d689ffa1 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,16 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_mcc_build_guard.py\"", + "statusMessage": "Checking MCC build command policy" + } + ] + } + ] + } +} diff --git a/.codex/hooks/pre_tool_use_mcc_build_guard.py b/.codex/hooks/pre_tool_use_mcc_build_guard.py new file mode 100644 index 00000000..fb6d3c1b --- /dev/null +++ b/.codex/hooks/pre_tool_use_mcc_build_guard.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +import json +import re +import sys + + +RAW_DOTNET_BUILD_RE = re.compile(r"(^|[\s;&|()])dotnet\s+build(\s|$)") +ABSOLUTE_DOTNET_BUILD_RE = re.compile(r"(^|[\s;&|()])/\S*dotnet\s+build(\s|$)") +RAW_DOTNET_PUBLISH_RE = re.compile(r"(^|[\s;&|()])dotnet\s+publish(\s|$)") +ABSOLUTE_DOTNET_PUBLISH_RE = re.compile(r"(^|[\s;&|()])/\S*dotnet\s+publish(\s|$)") + + +def main() -> int: + try: + payload = json.load(sys.stdin) + except json.JSONDecodeError: + return 0 + + command = payload.get("tool_input", {}).get("command", "") + if not isinstance(command, str) or not command: + return 0 + + if ABSOLUTE_DOTNET_BUILD_RE.search(command) or ABSOLUTE_DOTNET_PUBLISH_RE.search(command): + return 0 + + if RAW_DOTNET_BUILD_RE.search(command): + response = { + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "permissionDecision": "deny", + "permissionDecisionReason": ( + "Raw 'dotnet build' is blocked in this repository. " + "Use 'source tools/mcc-env.sh && mcc-build' instead so MCC temp-build routing stays active. " + "If you intentionally need the raw .NET CLI, call it by absolute path such as '/usr/bin/dotnet build ...' to bypass this guard." + ), + }, + "systemMessage": ( + "Blocked raw 'dotnet build'. Use 'source tools/mcc-env.sh && mcc-build'. " + "If you intentionally need raw .NET CLI behavior, call '/usr/bin/dotnet build ...' explicitly." + ), + } + json.dump(response, sys.stdout) + sys.stdout.write("\n") + elif RAW_DOTNET_PUBLISH_RE.search(command): + response = { + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "permissionDecision": "deny", + "permissionDecisionReason": ( + "Raw 'dotnet publish' is blocked in this repository. " + "Use 'source tools/mcc-env.sh && mcc-publish --rid ' instead so MCC publish defaults stay aligned with the repo workflow. " + "If you intentionally need the raw .NET CLI, call it by absolute path such as '/usr/bin/dotnet publish ...' to bypass this guard." + ), + }, + "systemMessage": ( + "Blocked raw 'dotnet publish'. Use 'source tools/mcc-env.sh && mcc-publish --rid '. " + "If you intentionally need raw .NET CLI behavior, call '/usr/bin/dotnet publish ...' explicitly." + ), + } + json.dump(response, sys.stdout) + sys.stdout.write("\n") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.skills/mcc-dev-workflow/SKILL.md b/.skills/mcc-dev-workflow/SKILL.md index 79980b63..06614892 100644 --- a/.skills/mcc-dev-workflow/SKILL.md +++ b/.skills/mcc-dev-workflow/SKILL.md @@ -93,9 +93,12 @@ mc-reset-test-env 1.21.11 ## Build ```bash -dotnet build MinecraftClient.sln -c Release +source tools/mcc-env.sh +mcc-build ``` +Use `mcc-build` for normal local development so any `MCC_BUILD_MODE=tmpfs` routing stays active. Only use raw `dotnet build` when you are intentionally debugging the build system itself. + ## Server management Interactive shell: @@ -276,6 +279,7 @@ After `source tools/mcc-env.sh`: | `mc-wait-stop VER [SEC]` | Wait for server shutdown, with force-kill fallback | | `mc-reset-test-env [--all|VER...]` | Reset shared tmux server state and stale pipes | | `mcc-build` | Build MCC | +| `mcc-publish --rid ` | Publish MCC with the repo's CI-like defaults | | `mcc-build-clean` | Clear the current worktree's build output | | `mcc-run [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug --file-input --no-build` | | `mcc-tui [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug -m tui --no-build` | diff --git a/AGENTS.md b/AGENTS.md index fec9d8f8..b82cc5bf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,16 +8,17 @@ ## Build / Run - Init submodules first: `git submodule update --init --recursive` -- Build: `dotnet build MinecraftClient.sln -c Release` -- Publish (matches CI shape): `dotnet publish MinecraftClient.sln -f net10.0 -r --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded` -- Run from source: `dotnet run --project MinecraftClient -- --help` +- Build for local development: `source tools/mcc-env.sh && mcc-build` +- Publish (matches CI shape): `source tools/mcc-env.sh && mcc-publish --rid ` +- Run/debug from source: `source tools/mcc-env.sh && mcc-debug -v 1.21.11 --file-input` - Docs: `cd docs && npm install && npm run docs:dev` or `npm run docs:build` - Docker: `cd Docker && docker build -t minecraft-console-client:latest .` - Tests: no dedicated test project is present in the main solution. -- Current state: the solution builds after submodule init, but `dotnet build` emits many analyzer and NuGet vulnerability warnings; treat them as real. +- Current state: the solution builds after submodule init, but the underlying .NET build emits many analyzer and NuGet vulnerability warnings; treat them as real. - Server roots: `tools/` helpers look for server jars under `MinecraftOfficial/downloads//` by default, but also support an external root via the `MCC_SERVERS` environment variable. - Multi-version testing: tmux-based local server sessions are shared state. Run cross-version test matrices sequentially unless you have explicit per-version isolation. A server logging `Done` does not guarantee immediate RCON availability; retry RCON setup commands. - Automated test configs: for repeated or matrix test runs, prefer generating a temporary MCC config per run instead of reusing the repo-root `MinecraftClient.ini`, to avoid leaking state between runs. +- For agent-driven local development, prefer `mcc-build`, `mcc-publish`, `mcc-build-clean`, `mcc-debug`, `mcc-run`, and `mcc-tui` over raw `dotnet build`, `dotnet publish`, or `dotnet run`, so worktree-local temp build routing stays active. ## Architecture - `Program` bootstraps console I/O, TOML config, auth/session state, MC version selection, Forge detection, then creates `McClient`. diff --git a/docs/guide/ai-assisted-development.md b/docs/guide/ai-assisted-development.md index 852df5db..11686d64 100644 --- a/docs/guide/ai-assisted-development.md +++ b/docs/guide/ai-assisted-development.md @@ -54,6 +54,8 @@ It is built around two layers: - repo tools in `tools/`, which do the actual work - AI skills in `.skills/`, which tell the agent when and how to use those tools +For agent-driven local development, prefer the `mcc-*` wrappers after `source tools/mcc-env.sh`. They preserve session isolation, temp configs, and optional tmpfs build routing. Do not default to raw `dotnet build` or `dotnet run` for the normal MCC debug loop. + ## Setup You only do most of this once. @@ -716,6 +718,7 @@ Typical flow: 1. Decide whether this should be a standalone `/script` bot or a built-in bot. 2. Use the authoring skill's references and templates. 3. Build MCC. + Use `mcc-build` instead of raw `dotnet build` so worktree-local temp build output still applies. 4. Start a local server and join it. 5. Test the bot behavior through live commands, chat, or event-driven actions. 6. Make sure cleanup paths such as `OnUnload()` are correct. diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 1ba686ed..3b36c740 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -131,6 +131,12 @@ If the build succeeds, the published binary `MinecraftClient.exe` will be in `Mi #### Building using .NET manually without Visual Studio +

Tip

+ +If you are following the AI-assisted repo workflow, use WSL or another Unix-style shell and prefer `source tools/mcc-env.sh` followed by `mcc-build`. That path keeps MCC's session and temp-build helpers enabled. The `dotnet` commands below are the low-level manual fallback. + +
+ 1. Open the `Minecraft-Console-Client` folder you've cloned or downloaded 2. Open the PowerShell (`Right-Click` on the whitespace and click `Open PowerShell`, or in Windows Explorer: `File -> Open PowerShell`) 3. Install the .NET 10 SDK if you do not already have it. The easiest current option on Windows is: @@ -148,7 +154,8 @@ dotnet build MinecraftClient.sln -c Release 5. If you want a release-like published binary that matches the repo's CI workflow, run: ```bash -dotnet publish MinecraftClient.sln -f net10.0 -r win-x64 --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded +source tools/mcc-env.sh +mcc-publish --rid win-x64 ``` 6. Verify the SDK installation if needed: @@ -210,18 +217,26 @@ git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive 5. If you want to download translation resources, please check out [Download translation resources](#download-translation-resources-optional) -6. Run the following command for a normal local build: +6. For the repo's normal local development workflow, source the helper environment and build through `mcc-build`: + + ```bash + source tools/mcc-env.sh + mcc-build + ``` + +7. If you specifically want the low-level manual .NET command instead of the MCC wrapper, run: ```bash dotnet build MinecraftClient.sln -c Release ``` -7. Run the following command if you want a release-like published binary that matches the repo's CI workflow: +8. Run the following command if you want a release-like published binary that matches the repo's CI workflow: - On Linux: ```bash - dotnet publish MinecraftClient.sln -f net10.0 -r linux-x64 --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded + source tools/mcc-env.sh + mcc-publish --rid linux-x64 ```

Note

@@ -233,7 +248,8 @@ git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive - On macOS: ```bash - dotnet publish MinecraftClient.sln -f net10.0 -r osx-x64 --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded + source tools/mcc-env.sh + mcc-publish --rid osx-x64 ```

Note

diff --git a/tools/README.md b/tools/README.md index 5c9d29cd..be83c64d 100644 --- a/tools/README.md +++ b/tools/README.md @@ -13,6 +13,7 @@ source tools/mcc-env.sh mc-start 1.21.11 mcc-debug -v 1.21.11 --file-input mcc-cmd "debug state" +mcc-publish --rid linux-x64 ``` ### Shared server, isolated MCC sessions diff --git a/tools/mcc-env.sh b/tools/mcc-env.sh index 217f18cc..437c5c9a 100644 --- a/tools/mcc-env.sh +++ b/tools/mcc-env.sh @@ -126,11 +126,44 @@ _mcc_dotnet_env() { local build_root build_root="$(_mcc_build_root)" mkdir -p "$build_root" - env MCC_BUILD_ROOT="$build_root" "$@" + env MCC_BUILD_ROOT="$build_root" MCC_ALLOW_RAW_DOTNET=1 "$@" return $? fi - "$@" + MCC_ALLOW_RAW_DOTNET=1 "$@" +} + +_mcc_is_repo_dotnet_build_blocked() { + local repo_root cwd + repo_root="$(_mcc_repo_root)" + cwd="${PWD:-}" + + [[ -n "$repo_root" && -n "$cwd" && "$cwd" == "$repo_root"* ]] +} + +dotnet() { + if [[ "${MCC_ALLOW_RAW_DOTNET:-0}" != "1" ]] && _mcc_is_repo_dotnet_build_blocked; then + case "${1:-}" in + build) + cat >&2 <<'EOF' +[MCC] Raw 'dotnet build' is blocked in this repository. +[MCC] Use: source tools/mcc-env.sh && mcc-build +[MCC] If you intentionally need the raw .NET CLI, call it by absolute path to bypass this guard. +EOF + return 64 + ;; + publish) + cat >&2 <<'EOF' +[MCC] Raw 'dotnet publish' is blocked in this repository. +[MCC] Use: source tools/mcc-env.sh && mcc-publish --rid +[MCC] If you intentionally need the raw .NET CLI, call it by absolute path to bypass this guard. +EOF + return 64 + ;; + esac + fi + + command dotnet "$@" } # Helper: convert version to tmux session name (dots -> underscores) @@ -248,6 +281,52 @@ mcc-build() { repo_root="$(_mcc_repo_root)" _mcc_dotnet_env dotnet build "$repo_root/MinecraftClient.sln" -c Release } +mcc-publish() { + local repo_root rid="" + local -a extra_args=() + + repo_root="$(_mcc_repo_root)" + + while [[ $# -gt 0 ]]; do + case "$1" in + --rid|-r) + shift + if [[ $# -eq 0 ]]; then + echo "mcc-publish: --rid requires a value" >&2 + return 1 + fi + rid="$1" + shift + ;; + --) + shift + extra_args+=("$@") + break + ;; + *) + extra_args+=("$1") + shift + ;; + esac + done + + if [[ -z "$rid" ]]; then + echo "mcc-publish: missing required --rid " >&2 + echo "mcc-publish: example: mcc-publish --rid linux-x64" >&2 + return 1 + fi + + _mcc_dotnet_env dotnet publish "$repo_root/MinecraftClient.sln" \ + -f net10.0 \ + -r "$rid" \ + --self-contained=true \ + -c Release \ + -p:UseAppHost=true \ + -p:IncludeNativeLibrariesForSelfExtract=true \ + -p:EnableCompressionInSingleFile=true \ + -p:DebugType=Embedded \ + "${extra_args[@]}" +} mcc-build-clean() { if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then local build_root