diff --git a/INSTALL.md b/INSTALL.md index 541785d..7e5ef74 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -215,6 +215,10 @@ Use `simplicio deliver certify` before declaring done. ### OpenAI Codex / ChatGPT Same workflow — Simplicio commands work from any terminal-based AI agent. +The shell and PowerShell installers detect the `codex` CLI and install or +update the Simplicio Codex plugin automatically. When Codex is absent, the +installer leaves the plugin step untouched; set `SIMPLICIO_SKIP_CODEX_PLUGIN=1` +only to opt out explicitly. ## Fontes Python no Runtime diff --git a/README.md b/README.md index c6211b7..3d8c413 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,11 @@ codex plugin marketplace add wesleysimplicio/simplicio --ref master codex plugin add simplicio@simplicio-codex ``` +The shell and PowerShell installers also detect the `codex` CLI and install +or update this plugin automatically. If Codex is not present, that step is +skipped. Set `SIMPLICIO_SKIP_CODEX_PLUGIN=1` only when you explicitly want +to disable the automatic plugin installation. + The official installers download one canonical asset from the latest Runtime release, verify its SHA256 checksum and Ed25519 signature, validate the Runtime release contract, and register MCP hosts to launch the installed binary diff --git a/install.ps1 b/install.ps1 index 25aacb9..b0f8bab 100755 --- a/install.ps1 +++ b/install.ps1 @@ -16,6 +16,7 @@ # SIMPLICIO_ALLOW_UNVERIFIED - "1" to proceed even if no checksum is # published for this target (default: refuse) # SIMPLICIO_BUNDLE_DIR - Runtime report directory (default: ~/.simplicio) +# SIMPLICIO_SKIP_CODEX_PLUGIN - "1" to skip automatic Codex plugin installation # # Asset naming follows distribution/targets.json (the canonical target # triplet table for the whole ecosystem) — target "windows-x64", asset @@ -44,6 +45,9 @@ $PinnedPublicKey = ([string]$Ed25519PublicKey).Trim() $script:Ed25519VerifyError = "" $SimplicioMcpUrl = if ($env:SIMPLICIO_MCP_URL) { $env:SIMPLICIO_MCP_URL } else { "http://127.0.0.1:8787/mcp" } +$CodexPluginMarketplace = if ($env:SIMPLICIO_CODEX_MARKETPLACE) { $env:SIMPLICIO_CODEX_MARKETPLACE } else { "simplicio-codex" } +$CodexPluginSource = if ($env:SIMPLICIO_CODEX_PLUGIN_SOURCE) { $env:SIMPLICIO_CODEX_PLUGIN_SOURCE } else { "wesleysimplicio/simplicio" } +$CodexPluginRef = if ($env:SIMPLICIO_CODEX_PLUGIN_REF) { $env:SIMPLICIO_CODEX_PLUGIN_REF } else { "master" } if ($env:SIMPLICIO_BIN_DIR) { $InstallDir = $env:SIMPLICIO_BIN_DIR } else { @@ -197,6 +201,37 @@ function Test-McpToolSurface([string]$BinaryPath) { } } +function Install-CodexPlugin { + if ($env:SIMPLICIO_SKIP_CODEX_PLUGIN -eq "1") { return } + $codex = Get-Command codex -ErrorAction SilentlyContinue + if ($null -eq $codex) { + Write-Host " - Codex not detected; Simplicio plugin was not installed" + return + } + + Write-Host "==> Codex detected; installing Simplicio plugin..." + & $codex.Source plugin marketplace add $CodexPluginSource --ref $CodexPluginRef --json | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Warning "Could not update the Codex marketplace; trying the configured marketplace" + } + + & $codex.Source plugin add "simplicio@$CodexPluginMarketplace" --json | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host " ✓ Simplicio plugin installed in Codex" + return + } + + if ($CodexPluginMarketplace -ne "simplicio") { + & $codex.Source plugin add "simplicio@simplicio" --json | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host " ✓ Simplicio plugin installed in Codex (existing marketplace)" + return + } + } + + Write-Warning "Codex detected, but the Simplicio plugin could not be installed automatically" + Write-Warning "Install manually: codex plugin marketplace add $CodexPluginSource --ref $CodexPluginRef; codex plugin add simplicio@$CodexPluginMarketplace" +} # ─── -Doctor: idempotent, read-only health check ─────────────────────────── if ($Doctor) { Write-Host "==> simplicio doctor" @@ -462,6 +497,7 @@ if (Test-McpToolSurface $DestPath) { Write-Error "Runtime installed, but automatic MCP/hooks registration failed: $DestPath mcp register --binary $DestPath --json" exit 1 } +Install-CodexPlugin Report-LoginState Write-Host " ✓ Direct MCP: $DestPath serve --mcp --stdio; SIMPLICIO_MCP_URL=$SimplicioMcpUrl" diff --git a/install.sh b/install.sh index 1346c54..ff7ed73 100755 --- a/install.sh +++ b/install.sh @@ -19,6 +19,7 @@ # SIMPLICIO_ALLOW_UNVERIFIED - "1" to proceed even if no checksum is # published for this target (default: refuse) # SIMPLICIO_BUNDLE_DIR - bundle report directory (default: ~/.simplicio) +# SIMPLICIO_SKIP_CODEX_PLUGIN - "1" to skip automatic Codex plugin installation # # Asset naming follows distribution/targets.json (the canonical target # triplet table for the whole ecosystem): id "macos-arm64" -> asset @@ -224,6 +225,39 @@ verify_mcp_tools() { SIMPLICIO_MCP_URL="$SIMPLICIO_MCP_URL" "$binary_path" mcp register --binary "$binary_path" --json >/dev/null 2>&1 } +CODEX_PLUGIN_MARKETPLACE="${SIMPLICIO_CODEX_MARKETPLACE:-simplicio-codex}" +CODEX_PLUGIN_SOURCE="${SIMPLICIO_CODEX_PLUGIN_SOURCE:-wesleysimplicio/simplicio}" +CODEX_PLUGIN_REF="${SIMPLICIO_CODEX_PLUGIN_REF:-master}" + +install_codex_plugin() { + if [ "${SIMPLICIO_SKIP_CODEX_PLUGIN:-}" = "1" ]; then + return 0 + fi + if ! command -v codex >/dev/null 2>&1; then + info "Codex não detectado; plugin Simplicio não será instalado" + return 0 + fi + + info "Codex detectado; instalando o plugin Simplicio..." + if ! codex plugin marketplace add "$CODEX_PLUGIN_SOURCE" --ref "$CODEX_PLUGIN_REF" --json >/dev/null 2>&1; then + warn "não foi possível atualizar o marketplace Codex; tentando o marketplace já configurado" + fi + + if codex plugin add "simplicio@$CODEX_PLUGIN_MARKETPLACE" --json >/dev/null 2>&1; then + ok "plugin Simplicio instalado no Codex" + return 0 + fi + + if [ "$CODEX_PLUGIN_MARKETPLACE" != "simplicio" ] && codex plugin add "simplicio@simplicio" --json >/dev/null 2>&1; then + ok "plugin Simplicio instalado no Codex (marketplace já configurado)" + return 0 + fi + + warn "Codex detectado, mas o plugin Simplicio não pôde ser instalado automaticamente" + warn "instale manualmente: codex plugin marketplace add $CODEX_PLUGIN_SOURCE --ref $CODEX_PLUGIN_REF && codex plugin add simplicio@$CODEX_PLUGIN_MARKETPLACE" + return 0 +} + # ─── --doctor: idempotent, read-only health check ────────────────────────── run_doctor() { info "simplicio doctor" @@ -531,6 +565,7 @@ if verify_mcp_tools "$DEST_PATH"; then else err "o Runtime foi instalado, mas o registro automático de MCP/hooks falhou: $DEST_PATH mcp register --binary $DEST_PATH --json" fi +install_codex_plugin report_login_state ok "MCP direto: $DEST_PATH serve --mcp --stdio; SIMPLICIO_MCP_URL=${SIMPLICIO_MCP_URL}" diff --git a/plugins/simplicio/bin/simplicio-mcp-bootstrap.js b/plugins/simplicio/bin/simplicio-mcp-bootstrap.js index 02b80dc..1072eb2 100644 --- a/plugins/simplicio/bin/simplicio-mcp-bootstrap.js +++ b/plugins/simplicio/bin/simplicio-mcp-bootstrap.js @@ -250,7 +250,7 @@ function runOfficialInstaller(installerPath, home, logPath) { USERPROFILE: isolatedHome, SIMPLICIO_BIN_DIR: managedBinDir, SIMPLICIO_BUNDLE_DIR: path.join(isolatedHome, ".simplicio"), - SIMPLICIO_INSTALL_CODEX: "0", + SIMPLICIO_SKIP_CODEX_PLUGIN: "1", SIMPLICIO_VERSION: POLICY.runtimeVersion }; delete env.SIMPLICIO_ALLOW_UNVERIFIED; diff --git a/plugins/simplicio/tests/bootstrap.test.js b/plugins/simplicio/tests/bootstrap.test.js index af89389..9b7625b 100644 --- a/plugins/simplicio/tests/bootstrap.test.js +++ b/plugins/simplicio/tests/bootstrap.test.js @@ -32,6 +32,11 @@ test("installer policy is pinned to immutable content", () => { assert.match(bootstrap.POLICY.installers.posix.sha256, /^[0-9a-f]{64}$/); assert.match(bootstrap.POLICY.installers.win32.sha256, /^[0-9a-f]{64}$/); }); +test("official installer bootstrap skips recursive Codex plugin installation", () => { + const source = fs.readFileSync(bootstrapPath, "utf8"); + assert.match(source, /SIMPLICIO_SKIP_CODEX_PLUGIN: "1"/); + assert.doesNotMatch(source, /SIMPLICIO_INSTALL_CODEX/); +}); test("installer download follows redirects and fails closed on HTTP errors", async () => { await assert.rejects( diff --git a/tests/install_error_registry.json b/tests/install_error_registry.json index 74598d5..12bde45 100644 --- a/tests/install_error_registry.json +++ b/tests/install_error_registry.json @@ -100,7 +100,7 @@ "root_cause": "The public installers required an opt-in environment variable and downloaded a second hook artifact.", "guard": "Every installer delegates automatic host and Runtime-owned hook registration to `simplicio mcp register --binary --json`; no Codex opt-in remains.", "regression_tests": [ - "tests/test_codex_integration_cli.py::test_installers_are_opt_in_and_do_not_use_mutable_master_hook_ref" + "tests/test_codex_integration_cli.py::test_installers_auto_install_codex_plugin_when_cli_is_present" ], "fixed_commit": "7abc0f0428d65718cda709b57c51bcabb28b92ae" }, @@ -315,7 +315,7 @@ "root_cause": "Host registration was split across installer-specific paths instead of the installed Runtime.", "guard": "SH, PS1, and PyPI all invoke the installed Runtime registrar and fail clearly when its JSON status is not passed.", "regression_tests": [ - "tests/test_codex_install_contract.py::test_plain_install_registers_all_detected_hosts_without_codex_opt_in", + "tests/test_codex_install_contract.py::test_plain_install_registers_hosts_and_installs_codex_plugin_when_available", "tests/unit/test_pypi_installer.py::test_install_registers_detected_hosts_with_installed_binary" ], "fixed_commit": "7abc0f0428d65718cda709b57c51bcabb28b92ae" diff --git a/tests/test_codex_install_contract.py b/tests/test_codex_install_contract.py index 4f5f8a2..41ba3e9 100644 --- a/tests/test_codex_install_contract.py +++ b/tests/test_codex_install_contract.py @@ -38,18 +38,22 @@ def test_installers_configure_direct_stdio_and_hooks(): assert "--binary" in text assert "--json" in text -def test_plain_install_registers_all_detected_hosts_without_codex_opt_in(): +def test_plain_install_registers_hosts_and_installs_codex_plugin_when_available(): shell = (ROOT / "install.sh").read_text(encoding="utf-8") powershell = (ROOT / "install.ps1").read_text(encoding="utf-8") - assert "SIMPLICIO_INSTALL_CODEX" not in shell + assert "SIMPLICIO_SKIP_CODEX_PLUGIN" in shell assert "SIMPLICIO_CODEX_HOOK_REF" not in shell - assert "if require_active_login; then" not in shell + assert "command -v codex" in shell + assert 'plugin marketplace add "$CODEX_PLUGIN_SOURCE"' in shell + assert 'plugin add "simplicio@$CODEX_PLUGIN_MARKETPLACE"' in shell assert 'mcp register --binary "$binary_path" --json' in shell - assert "SIMPLICIO_INSTALL_CODEX" not in powershell + assert "SIMPLICIO_SKIP_CODEX_PLUGIN" in powershell assert "SIMPLICIO_CODEX_HOOK_REF" not in powershell - assert "if (Require-ActiveLogin)" not in powershell + assert "Get-Command codex" in powershell + assert "plugin marketplace add $CodexPluginSource" in powershell + assert 'plugin add "simplicio@$CodexPluginMarketplace"' in powershell assert "mcp register --binary $BinaryPath --json" in powershell diff --git a/tests/test_codex_integration_cli.py b/tests/test_codex_integration_cli.py index 450047f..be8f36c 100644 --- a/tests/test_codex_integration_cli.py +++ b/tests/test_codex_integration_cli.py @@ -50,11 +50,17 @@ def test_windows_binary_path_is_toml_safe_and_uses_forward_slashes(): assert tomllib.loads(config)["mcp_servers"]["simplicio"]["command"] == expected -def test_installers_are_opt_in_and_do_not_use_mutable_master_hook_ref(): +def test_installers_auto_install_codex_plugin_when_cli_is_present(): shell = (ROOT / "install.sh").read_text(encoding="utf-8") powershell = (ROOT / "install.ps1").read_text(encoding="utf-8") - assert "SIMPLICIO_INSTALL_CODEX" not in shell - assert "SIMPLICIO_INSTALL_CODEX" not in powershell + assert "SIMPLICIO_SKIP_CODEX_PLUGIN" in shell + assert "SIMPLICIO_SKIP_CODEX_PLUGIN" in powershell + assert "command -v codex" in shell + assert "Get-Command codex" in powershell + assert 'plugin marketplace add "$CODEX_PLUGIN_SOURCE"' in shell + assert 'plugin add "simplicio@$CODEX_PLUGIN_MARKETPLACE"' in shell + assert "plugin marketplace add $CodexPluginSource" in powershell + assert 'plugin add "simplicio@$CodexPluginMarketplace"' in powershell assert "SIMPLICIO_CODEX_HOOK_REF" not in shell assert "SIMPLICIO_CODEX_HOOK_REF" not in powershell assert 'mcp register --binary "$binary_path" --json' in shell