feat: automate mac mcp cad host bootstrap #17

Merged
electron-rare merged 1 commits from feat/mac-mcp-cad-host-bootstrap into main 2026-03-09 05:12:52 +00:00
electron-rare commented 2026-03-09 05:09:04 +00:00 (Migrated from github.com)

Summary

  • stabilize the local Mac MCP bootstrap and companion repo resolution
  • prefer host KiCad, FreeCAD, and stable OpenSCAD runtimes for local CAD/MCP flows
  • add an autonomous next-lots runner that refreshes plan/todo docs from real validations

Validation

  • bash tools/run_autonomous_next_lots.sh run
  • bash tools/hw/cad_stack.sh doctor
  • .venv/bin/python tools/freecad_mcp_smoke.py --quick --json
  • .venv/bin/python tools/openscad_mcp_smoke.py --quick --json
## Summary - stabilize the local Mac MCP bootstrap and companion repo resolution - prefer host KiCad, FreeCAD, and stable OpenSCAD runtimes for local CAD/MCP flows - add an autonomous next-lots runner that refreshes plan/todo docs from real validations ## Validation - bash tools/run_autonomous_next_lots.sh run - bash tools/hw/cad_stack.sh doctor - .venv/bin/python tools/freecad_mcp_smoke.py --quick --json - .venv/bin/python tools/openscad_mcp_smoke.py --quick --json
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-03-09 05:13:56 +00:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

This PR improves local macOS MCP/CAD bootstrap by introducing more robust companion-repo resolution, preferring host CAD runtimes when available, and adding an “autonomous next lots” runner that updates plan/todo docs based on local validations.

Changes:

  • Switch validate-specs MCP launch to a bash wrapper that auto-selects a suitable Python runtime.
  • Centralize/standardize mascarade companion repo resolution across multiple MCP/CAD launchers.
  • Add an autonomous runner that detects “lots” from git status, runs validations, and regenerates plan/todo docs.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tools/validate_specs_mcp_smoke.py Launch validate-specs MCP via new bash wrapper.
tools/run_validate_specs_mcp.sh New runtime-detecting launcher for validate-specs MCP.
tools/run_knowledge_base_mcp.sh Resolve companion repo via shared resolver; export MASCARADE_DIR.
tools/run_github_dispatch_mcp.sh Resolve companion repo via shared resolver; export MASCARADE_DIR.
tools/run_autonomous_next_lots.sh New wrapper to run the autonomous lots runner.
tools/mcp_smoke_common.py Improve default mascarade dir resolution logic.
tools/lib/runtime_home.sh Add kill_life_resolve_mascarade_dir helper for companion repo discovery.
tools/hw/run_kicad_mcp.sh Prefer host KiCad Python bits; resolve companion repo via shared resolver.
tools/hw/cad_stack.sh Prefer host KiCad/FreeCAD/OpenSCAD when available; container fallback remains.
tools/bootstrap_mac_mcp.sh Use shared companion repo resolver; validate-specs now launched via bash wrapper.
tools/autonomous_next_lots.py New autonomous planner/validator that can rewrite plan/todo docs.
mcp.json Update validate-specs MCP command to use bash wrapper.
docs/plans/README.md Add plan 18 entry.
docs/plans/18_plan_enchainement_autonome_des_lots_utiles.md New autogenerated plan document with current run state.
docs/plans/18_todo_enchainement_autonome_des_lots_utiles.md New autogenerated todo document with validation summaries.
docs/MCP_SETUP.md Update docs to reflect new validate-specs launcher and host-first CAD detection.

You can also share your feedback on Copilot code review. Take the survey.

## Pull request overview This PR improves local macOS MCP/CAD bootstrap by introducing more robust companion-repo resolution, preferring host CAD runtimes when available, and adding an “autonomous next lots” runner that updates plan/todo docs based on local validations. **Changes:** - Switch `validate-specs` MCP launch to a bash wrapper that auto-selects a suitable Python runtime. - Centralize/standardize `mascarade` companion repo resolution across multiple MCP/CAD launchers. - Add an autonomous runner that detects “lots” from `git status`, runs validations, and regenerates plan/todo docs. ### Reviewed changes Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments. <details> <summary>Show a summary per file</summary> | File | Description | | ---- | ----------- | | tools/validate_specs_mcp_smoke.py | Launch validate-specs MCP via new bash wrapper. | | tools/run_validate_specs_mcp.sh | New runtime-detecting launcher for validate-specs MCP. | | tools/run_knowledge_base_mcp.sh | Resolve companion repo via shared resolver; export `MASCARADE_DIR`. | | tools/run_github_dispatch_mcp.sh | Resolve companion repo via shared resolver; export `MASCARADE_DIR`. | | tools/run_autonomous_next_lots.sh | New wrapper to run the autonomous lots runner. | | tools/mcp_smoke_common.py | Improve default `mascarade` dir resolution logic. | | tools/lib/runtime_home.sh | Add `kill_life_resolve_mascarade_dir` helper for companion repo discovery. | | tools/hw/run_kicad_mcp.sh | Prefer host KiCad Python bits; resolve companion repo via shared resolver. | | tools/hw/cad_stack.sh | Prefer host KiCad/FreeCAD/OpenSCAD when available; container fallback remains. | | tools/bootstrap_mac_mcp.sh | Use shared companion repo resolver; validate-specs now launched via bash wrapper. | | tools/autonomous_next_lots.py | New autonomous planner/validator that can rewrite plan/todo docs. | | mcp.json | Update validate-specs MCP command to use bash wrapper. | | docs/plans/README.md | Add plan 18 entry. | | docs/plans/18_plan_enchainement_autonome_des_lots_utiles.md | New autogenerated plan document with current run state. | | docs/plans/18_todo_enchainement_autonome_des_lots_utiles.md | New autogenerated todo document with validation summaries. | | docs/MCP_SETUP.md | Update docs to reflect new validate-specs launcher and host-first CAD detection. | </details> --- You can also share your feedback on Copilot code review. [Take the survey](https://www.surveymonkey.com/r/XP6L3XJ).
@@ -0,0 +41,4 @@
- `tools/autonomous_next_lots.py`
- `tools/run_autonomous_next_lots.sh`
- `tools/run_validate_specs_mcp.sh`
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:55 +00:00

This generated plan includes ephemeral local Git state (branch header, dirty path count/list, timestamps). As a tracked doc, this will churn frequently and may not be meaningful to other contributors. Consider keeping the runbook content versioned, but moving per-run state into untracked artifacts (or only writing it when explicitly requested).

This generated plan includes ephemeral local Git state (branch header, dirty path count/list, timestamps). As a tracked doc, this will churn frequently and may not be meaningful to other contributors. Consider keeping the runbook content versioned, but moving per-run state into untracked artifacts (or only writing it when explicitly requested).
@@ -0,0 +8,4 @@
- done: lot detecte (Stabiliser les launchers MCP, le bootstrap Mac, la resolution du repo compagnon et la doc operateur associee.)
- done: `bash tools/bootstrap_mac_mcp.sh codex`
resume: codex mcp add openscad --env MASCARADE_DIR=/Users/electron/mascarade-main -- bash /Users/electron/Kill_LIFE/tools/run_openscad_mcp.sh | codex mcp add huggingface --url https://huggingface.co/mcp --bearer-token-env-var HUGGINGFACE_API_KEY | codex mcp add playwright -- npx -y @playwright/mcp@latest
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:54 +00:00

This generated TODO file includes user-specific absolute paths in the validation summaries (e.g., /Users/.../Kill_LIFE/...). Committing machine-local paths makes the doc noisy and can leak local environment details. Regenerate with sanitized/relative paths (or avoid embedding raw command output in tracked docs).

  resume: codex mcp add openscad --env MASCARADE_DIR=$MASCARADE_DIR -- bash tools/run_openscad_mcp.sh | codex mcp add huggingface --url https://huggingface.co/mcp --bearer-token-env-var HUGGINGFACE_API_KEY | codex mcp add playwright -- npx -y @playwright/mcp@latest
This generated TODO file includes user-specific absolute paths in the validation summaries (e.g., `/Users/.../Kill_LIFE/...`). Committing machine-local paths makes the doc noisy and can leak local environment details. Regenerate with sanitized/relative paths (or avoid embedding raw command output in tracked docs). ```suggestion resume: codex mcp add openscad --env MASCARADE_DIR=$MASCARADE_DIR -- bash tools/run_openscad_mcp.sh | codex mcp add huggingface --url https://huggingface.co/mcp --bearer-token-env-var HUGGINGFACE_API_KEY | codex mcp add playwright -- npx -y @playwright/mcp@latest ```
@@ -0,0 +215,4 @@
lines = [line.rstrip() for line in text.splitlines() if line.strip()]
if not lines:
return ""
return " | ".join(lines[-3:])
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:56 +00:00

output_tail (and the subsequent embedding of command outputs into the generated docs) can capture machine-specific absolute paths and other environment details. This is already visible in the committed docs/plans/18_* artifacts (e.g., /Users/... paths in command summaries), which creates noisy diffs and can unintentionally leak local filesystem layout into the repo. Consider redacting absolute paths (e.g., replace ROOT with . / $REPO), truncating/normalizing outputs more aggressively, and/or not persisting raw validation output into tracked markdown.

def _sanitize_summary_line(line: str) -> str:
    """Redact machine-specific details from a single summary line."""
    # Normalize common absolute paths to stable tokens to avoid leaking local layout.
    repo_root = str(ROOT)
    if repo_root and repo_root in line:
        line = line.replace(repo_root, "$REPO")

    # Also hide the current user's home directory if present.
    home_dir = str(Path.home())
    if home_dir and home_dir in line:
        line = line.replace(home_dir, "$HOME")

    # Truncate very long lines to keep summaries compact and diffs manageable.
    max_len = 200
    if len(line) > max_len:
        line = line[: max_len - 3] + "..."

    return line


def output_tail(text: str) -> str:
    lines = [line.rstrip() for line in text.splitlines() if line.strip()]
    if not lines:
        return ""
    tail = lines[-3:]
    sanitized = [_sanitize_summary_line(line) for line in tail]
    return " | ".join(sanitized)
`output_tail` (and the subsequent embedding of command outputs into the generated docs) can capture machine-specific absolute paths and other environment details. This is already visible in the committed `docs/plans/18_*` artifacts (e.g., `/Users/...` paths in command summaries), which creates noisy diffs and can unintentionally leak local filesystem layout into the repo. Consider redacting absolute paths (e.g., replace `ROOT` with `.` / `$REPO`), truncating/normalizing outputs more aggressively, and/or not persisting raw validation output into tracked markdown. ```suggestion def _sanitize_summary_line(line: str) -> str: """Redact machine-specific details from a single summary line.""" # Normalize common absolute paths to stable tokens to avoid leaking local layout. repo_root = str(ROOT) if repo_root and repo_root in line: line = line.replace(repo_root, "$REPO") # Also hide the current user's home directory if present. home_dir = str(Path.home()) if home_dir and home_dir in line: line = line.replace(home_dir, "$HOME") # Truncate very long lines to keep summaries compact and diffs manageable. max_len = 200 if len(line) > max_len: line = line[: max_len - 3] + "..." return line def output_tail(text: str) -> str: lines = [line.rstrip() for line in text.splitlines() if line.strip()] if not lines: return "" tail = lines[-3:] sanitized = [_sanitize_summary_line(line) for line in tail] return " | ".join(sanitized) ```
@@ -0,0 +410,4 @@
plan_text = render_plan(branch, dirty_paths, ahead, behind, lots, results)
todo_text = render_todo(lots, results)
should_write = not args.no_write and args.mode != "json"
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:56 +00:00

The default status mode rewrites the plan/todo markdown files because should_write is true for any mode except json. This makes simply checking status dirty the working tree (and causes constant timestamp churn). Consider only writing docs in run mode, or gating writes behind an explicit --write flag (keeping status read-only by default).

    should_write = not args.no_write and args.mode == "run"
The default `status` mode rewrites the plan/todo markdown files because `should_write` is true for any mode except `json`. This makes simply checking status dirty the working tree (and causes constant timestamp churn). Consider only writing docs in `run` mode, or gating writes behind an explicit `--write` flag (keeping `status` read-only by default). ```suggestion should_write = not args.no_write and args.mode == "run" ```
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:55 +00:00

kill_life_resolve_mascarade_dir is used in a command-substitution assignment while set -e is active. If resolution fails (e.g., companion repo not present), the script will exit immediately with a non-obvious error and will not reach the explicit Unable to resolve mascarade companion path message. Capture the failure (|| true) and handle it with a clear error message (or wrap the call in ... || { echo ...; exit 1; }).

      "finetune/kicad_mcp_server" \
      || true
`kill_life_resolve_mascarade_dir` is used in a command-substitution assignment while `set -e` is active. If resolution fails (e.g., companion repo not present), the script will exit immediately with a non-obvious error and will not reach the explicit `Unable to resolve mascarade companion path` message. Capture the failure (`|| true`) and handle it with a clear error message (or wrap the call in `... || { echo ...; exit 1; }`). ```suggestion "finetune/kicad_mcp_server" \ || true ```
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:56 +00:00

MASCARADE_DIR is resolved with kill_life_resolve_mascarade_dir using command substitution while set -e is enabled. If the companion repo isn't present, the launcher will exit at this assignment with a hard-to-diagnose error. Consider making the resolution non-fatal (|| true) and then die with a clear message describing expected ../mascarade / ../mascarade-main layout or MASCARADE_DIR override.

if [ -z "${MASCARADE_DIR:-}" ]; then
  RESOLVED_MASCARADE_DIR="$(
    kill_life_resolve_mascarade_dir \
      "$ROOT_DIR" \
      "finetune/kicad_mcp_server" || true
  )"
  if [ -z "$RESOLVED_MASCARADE_DIR" ]; then
    die "Unable to locate mascarade companion repository.

Expected one of:
  - \$ROOT_DIR/../mascarade
  - \$ROOT_DIR/../mascarade-main

You can also override the location explicitly by setting MASCARADE_DIR."
  fi
  MASCARADE_DIR="$RESOLVED_MASCARADE_DIR"
fi
`MASCARADE_DIR` is resolved with `kill_life_resolve_mascarade_dir` using command substitution while `set -e` is enabled. If the companion repo isn't present, the launcher will exit at this assignment with a hard-to-diagnose error. Consider making the resolution non-fatal (`|| true`) and then `die` with a clear message describing expected `../mascarade` / `../mascarade-main` layout or `MASCARADE_DIR` override. ```suggestion if [ -z "${MASCARADE_DIR:-}" ]; then RESOLVED_MASCARADE_DIR="$( kill_life_resolve_mascarade_dir \ "$ROOT_DIR" \ "finetune/kicad_mcp_server" || true )" if [ -z "$RESOLVED_MASCARADE_DIR" ]; then die "Unable to locate mascarade companion repository. Expected one of: - \$ROOT_DIR/../mascarade - \$ROOT_DIR/../mascarade-main You can also override the location explicitly by setting MASCARADE_DIR." fi MASCARADE_DIR="$RESOLVED_MASCARADE_DIR" fi ```
@@ -10,1 +11,4 @@
"core/mascarade/integrations/github_dispatch.py"
)"
MASCARADE_ENV_FILE="${MASCARADE_ENV_FILE:-$MASCARADE_DIR/.env}"
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:55 +00:00

MASCARADE_DIR is resolved via command substitution under set -euo pipefail. If kill_life_resolve_mascarade_dir fails, the script will exit at the assignment without printing a helpful error. Handle the failure explicitly and emit an actionable message (expected repo locations, how to override via MASCARADE_DIR).

if [[ -z "${MASCARADE_DIR:-}" ]]; then
  if ! MASCARADE_DIR="$(
    kill_life_resolve_mascarade_dir \
      "$ROOT_DIR" \
      "core/mascarade/integrations/github_dispatch.py"
  )"; then
    cat >&2 <<EOF
ERROR: Unable to resolve the Mascarade repository directory (MASCARADE_DIR).

The helper 'kill_life_resolve_mascarade_dir' failed while looking for:
  core/mascarade/integrations/github_dispatch.py
relative to:
  $ROOT_DIR

Make sure the Mascarade repository is checked out in an expected location
(for example as a sibling or subdirectory of this project), or override
autodetection by setting the MASCARADE_DIR environment variable explicitly,
e.g.:

  MASCARADE_DIR=/path/to/mascarade \\
    "$0" "$@"

EOF
    exit 1
  fi
fi
`MASCARADE_DIR` is resolved via command substitution under `set -euo pipefail`. If `kill_life_resolve_mascarade_dir` fails, the script will exit at the assignment without printing a helpful error. Handle the failure explicitly and emit an actionable message (expected repo locations, how to override via `MASCARADE_DIR`). ```suggestion if [[ -z "${MASCARADE_DIR:-}" ]]; then if ! MASCARADE_DIR="$( kill_life_resolve_mascarade_dir \ "$ROOT_DIR" \ "core/mascarade/integrations/github_dispatch.py" )"; then cat >&2 <<EOF ERROR: Unable to resolve the Mascarade repository directory (MASCARADE_DIR). The helper 'kill_life_resolve_mascarade_dir' failed while looking for: core/mascarade/integrations/github_dispatch.py relative to: $ROOT_DIR Make sure the Mascarade repository is checked out in an expected location (for example as a sibling or subdirectory of this project), or override autodetection by setting the MASCARADE_DIR environment variable explicitly, e.g.: MASCARADE_DIR=/path/to/mascarade \\ "$0" "$@" EOF exit 1 fi fi ```
@@ -10,1 +11,4 @@
"core/mascarade/integrations/knowledge_base.py"
)"
MASCARADE_ENV_FILE="${MASCARADE_ENV_FILE:-$MASCARADE_DIR/.env}"
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-03-09 05:13:55 +00:00

MASCARADE_DIR is resolved via command substitution under set -euo pipefail. If kill_life_resolve_mascarade_dir fails, the script will exit at the assignment with no actionable error. Consider handling the failure explicitly (e.g., MASCARADE_DIR="$(... || true)" followed by a check that prints guidance about expected locations / MASCARADE_DIR).

    "core/mascarade/integrations/knowledge_base.py" \
    || true
)"
if [[ -z "$MASCARADE_DIR" || ! -d "$MASCARADE_DIR" ]]; then
  echo "Failed to resolve MASCARADE_DIR via kill_life_resolve_mascarade_dir." >&2
  echo "ROOT_DIR=$ROOT_DIR" >&2
  echo "Ensure Mascarade core is available (e.g., at \$ROOT_DIR/core/mascarade) and retry." >&2
  exit 1
fi
`MASCARADE_DIR` is resolved via command substitution under `set -euo pipefail`. If `kill_life_resolve_mascarade_dir` fails, the script will exit at the assignment with no actionable error. Consider handling the failure explicitly (e.g., `MASCARADE_DIR="$(... || true)"` followed by a check that prints guidance about expected locations / `MASCARADE_DIR`). ```suggestion "core/mascarade/integrations/knowledge_base.py" \ || true )" if [[ -z "$MASCARADE_DIR" || ! -d "$MASCARADE_DIR" ]]; then echo "Failed to resolve MASCARADE_DIR via kill_life_resolve_mascarade_dir." >&2 echo "ROOT_DIR=$ROOT_DIR" >&2 echo "Ensure Mascarade core is available (e.g., at \$ROOT_DIR/core/mascarade) and retry." >&2 exit 1 fi ```
chatgpt-codex-connector[bot] (Migrated from github.com) reviewed 2026-03-09 05:14:14 +00:00
chatgpt-codex-connector[bot] (Migrated from github.com) left a comment

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: caea73e0f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

### 💡 Codex Review Here are some automated review suggestions for this pull request. **Reviewed commit:** `caea73e0f8` <details> <summary>ℹ️ About Codex in GitHub</summary> <br/> [Your team has set up Codex to review pull requests in this repo](http://chatgpt.com/codex/settings/general). Reviews are triggered when you - Open a pull request for review - Mark a draft as ready - Comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". </details>
chatgpt-codex-connector[bot] (Migrated from github.com) commented 2026-03-09 05:14:14 +00:00

P2 Badge Preserve bootstrap error reporting on missing companion

Because this resolver call is also unguarded under set -e, bootstrap aborts before the explicit fallback check/reporting below can run. When no companion repo is present, bash tools/bootstrap_mac_mcp.sh codex exits silently instead of emitting the expected Unable to resolve mascarade companion path message, making setup failures much harder to triage.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Preserve bootstrap error reporting on missing companion** Because this resolver call is also unguarded under `set -e`, bootstrap aborts before the explicit fallback check/reporting below can run. When no companion repo is present, `bash tools/bootstrap_mac_mcp.sh codex` exits silently instead of emitting the expected `Unable to resolve mascarade companion path` message, making setup failures much harder to triage. Useful? React with 👍 / 👎.
chatgpt-codex-connector[bot] (Migrated from github.com) commented 2026-03-09 05:14:14 +00:00

P2 Badge Guard companion resolution before doctor mode

This command substitution is executed under set -e, so if no valid mascarade candidate is found the script exits immediately before it can reach the --doctor output path. In environments where the companion repo is absent or renamed, tools/hw/run_kicad_mcp.sh --doctor now fails with exit code 1 and no diagnostics, which breaks the intended troubleshooting flow.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Guard companion resolution before doctor mode** This command substitution is executed under `set -e`, so if no valid `mascarade` candidate is found the script exits immediately before it can reach the `--doctor` output path. In environments where the companion repo is absent or renamed, `tools/hw/run_kicad_mcp.sh --doctor` now fails with exit code 1 and no diagnostics, which breaks the intended troubleshooting flow. Useful? React with 👍 / 👎.
Sign in to join this conversation.