Skip to content

Pi profile support, Codex family history, and Keycloak broker design - #3

Open
brettheap wants to merge 10 commits into
mainfrom
feat/pi-profile-support
Open

Pi profile support, Codex family history, and Keycloak broker design#3
brettheap wants to merge 10 commits into
mainfrom
feat/pi-profile-support

Conversation

@brettheap

@brettheap brettheap commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Six commits on top of main spanning the multi-account profile work and a new credential-broker design doc. Fast-forward over origin/main (no divergence).

Changes

  • Isolated Pi account profiles and proxying Pi Claude profiles through the Claude CLI.
  • Codex per-family conversation historysessions, archived_sessions, history.jsonl, and session_index.jsonl link to a per-family state dir so another login in the same trust family can resume the same conversations; auth/config/caches/SQLite stay profile-local. Adds setup-multi-cli-codex-profiles.ps1 to sync registry-owned Multi-CLI profiles with the Windows Codex Desktop store.
  • Claude family-isolation docs — clarify using a unique family per personal login.
  • Onboarding company-name default — company name defaults to the login email domain's first label (you@acme.comacme), GitHub org defaults to the company name; both editable.
  • Keycloak-gated credential broker design (docs/keycloak-credential-broker.md) — an optional shared Keycloak identity server plus a Python broker that gates the existing SOPS registries: Keycloak proves identity, grant files stay authoritative for authorization, SOPS stays the at-rest store, and the broker holds one age identity per tenant so no decryption key reaches a workstation or agent. Cross-linked from ai-credential-ownership.md.

Notes

  • Docs-and-scripts only; no bench images or runtime code paths changed.
  • The Keycloak broker doc is a design, not an implementation — no broker code or Keycloak install repo ships in this PR.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 20, 2026 01:23
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds isolated, multi-account Pi Coding Agent “profiles” aligned to existing provider identity manifests, introducing a canonical ppi launcher that sets PI_CODING_AGENT_DIR and integrates the new ~/.pi-profiles home into setup flows and container mounts.

Changes:

  • Add Pi profile composition + setup scripts (compose-pi-profiles.py, setup-pi-profiles.sh) and a pi-profile/ppi launcher.
  • Mount and surface ~/.pi-profiles in Wave/devBench container tooling and base images, and update setup UI/interactive setup messaging.
  • Add encrypted Pi credential escrow helper and accompanying credential-manager tests.

Reviewed changes

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

Show a summary per file
File Description
scripts/wave-container-shell.sh Mount ~/.pi-profiles and install pi-profile/ppi launcher inside Wave containers.
scripts/setup-windows-tools.sh Improve Windows/WSL Pi installation flow and messaging for ppi.
scripts/setup-ui/src/utils/installers.ts Update setup UI post-install guidance to use ppi login PROFILE.
scripts/setup-pi-profiles.sh New host-side Pi profile tree setup and launcher symlinks.
scripts/setup-ai-profiles.sh Generate pi-profiles.json from provider manifests and run Pi profile setup.
scripts/pi-credential-escrow New encrypted escrow flow for Pi auth.json (backup/restore/check).
scripts/interactive-setup.sh Detect Pi creds across standard + isolated homes; guide login via ppi.
scripts/compose-pi-profiles.py Compose Pi profiles from provider manifests and retained launcher metadata.
README.md Update Pi Terminal setup description to reference isolated ppi usage.
docs/pi-multi-account-profiles.md New documentation for Pi multi-account profiles and escrow workflow.
docs/newBench.md Add ~/.pi-profiles to standard devcontainer mount template.
docs/multi-provider-profiles.md Document ppi alongside other provider profile launchers.
docs/MOUNTS-README.md Document ~/.pi-profiles mount and purpose.
docker-compose.mounts.yml Add standard mount for ~/.pi-profiles.
base-image/install-ai-clis.sh Install Pi Coding Agent CLI in the base image.
base-image/files/pi-profile New pi-profile launcher script with ppi alias behavior.
base-image/Dockerfile Ship pi-profile and symlink ppi in the base image.
apps/credential-manager/test_pi_profiles.py Add tests covering composition/idempotency/isolation and escrow round-trip behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +54
if ls "$config_dir"/{claude,openai,gemini,grok,glm}-profiles.json >/dev/null 2>&1; then
pi_profile_roots=(
--profile-root "claude=${CLAUDE_PROFILES_HOME:-$HOME/.claude-profiles}/profiles"
--profile-root "openai=${CODEX_PROFILES_HOME:-$HOME/.chatgpt-profiles}/profiles"
--profile-root "gemini=${GEMINI_PROFILES_HOME:-$HOME/.gemini-profiles}/profiles"
--profile-root "grok=${GROK_PROFILES_HOME:-$HOME/.grok-profiles}/profiles"
--profile-root "glm=${GLM_PROFILES_HOME:-$HOME/.glm-profiles}/profiles"
)
python3 "$repo_dir/scripts/compose-pi-profiles.py" \
--config-dir "$config_dir" \
"${pi_profile_roots[@]}" \
--output "$config_dir/pi-profiles.json"
fi
Comment on lines +19 to +21
command -v jq >/dev/null 2>&1 || { echo "jq is required." >&2; exit 1; }
jq -e '.version == 1 and (.profiles | type == "array") and all(.profiles[]; (.name|length)>0 and (.email|length)>0 and (.family|length)>0 and ((.providers//[])|type=="array"))' "$manifest" >/dev/null

Comment on lines +52 to +53
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/pi-profile"
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/ppi"
Comment on lines +31 to +38
def add_profile(profiles: dict[str, dict], provider: str, raw: dict, source: pathlib.Path) -> None:
if not isinstance(raw, dict) or any(not raw.get(field) for field in ("name", "email", "family")):
raise ValueError(f"invalid {provider} profile in {source}")
normalized = {
"email": raw["email"],
"family": raw["family"],
"aliases": sorted(raw.get("aliases") or []),
}
Comment on lines 2394 to 2404
pi)
echo -e "${CYAN}▶ Setting up Pi Coding Agent authentication...${NC}"
echo -e "${DIM}Pi uses /login inside its interactive terminal.${NC}"
read -p "Launch Pi now? [Y/n]: " launch_pi
echo -e "${DIM}Pi credentials are isolated by canonical profile through ppi.${NC}"
read -p "Launch a Pi profile login now? [Y/n]: " launch_pi
if [[ ! $launch_pi =~ ^[Nn] ]]; then
echo -e "${YELLOW}Run /login, choose a provider, then exit Pi when finished.${NC}"
pi
read -r -p "Pi profile name or alias: " pi_profile
echo -e "${YELLOW}Run /login, choose a provider, verify the matching identity, then exit Pi.${NC}"
ppi login "$pi_profile"
else
echo -e "${YELLOW}Skipped. Run 'pi' and enter /login anytime.${NC}"
echo -e "${YELLOW}Skipped. Run 'ppi login PROFILE' anytime.${NC}"
fi
pi_cli)
if command -v pi &>/dev/null; then
[[ -f "$HOME/.pi/agent/auth.json" ]] && echo "installed" || echo "needs creds"
if [[ -f "$HOME/.pi/agent/auth.json" ]] || find "$HOME/.pi-profiles/profiles" -mindepth 3 -maxdepth 3 -name auth.json -print -quit 2>/dev/null | grep -q .; then
Comment on lines +158 to +163
elif command -v npm >/dev/null 2>&1; then
echo " Installing Pi Terminal in WSL/Linux for ppi and devBench use..."
wsl_npm="$(command -v npm)"
[[ -x /usr/bin/npm ]] && wsl_npm=/usr/bin/npm
if "$wsl_npm" install -g --ignore-scripts "$PI_NPM_PACKAGE"; then
echo " ✓ Pi Terminal installed for WSL/Linux"
)
' "$1" >/dev/null
}
json_hash() { jq -S -c . "$1" | sha256sum | awk '{print $1}'; }
Copilot AI review requested due to automatic review settings July 20, 2026 01:35
@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8528a21a98

ℹ️ 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".

Comment thread base-image/files/pi-profile Outdated
Comment on lines +17 to +21
jq -r --arg name "$normalized" '
.profiles[]
| select(((.name | ascii_downcase) == $name) or any(.aliases[]?; ascii_downcase == $name))
| [.name,.email,.family,((.providers // []) | join(","))] | @tsv
' "$manifest" | head -n 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore metadata fallback for container-mounted profiles

In Wave/devBench containers I checked docker-compose.mounts.yml and scripts/wave-container-shell.sh: they bind-mount ~/.pi-profiles but not ~/.config/workbenches/pi-profiles.json. This launcher only reads $manifest for profile lookup, so ppi team001, ppi status team001, and ppi list fail inside those containers with a missing manifest even though setup-pi-profiles.sh wrote .profile.json under the mounted profile tree. Please fall back to $base/profiles/*/.profile.json as the Claude/Codex launchers do, or mount the manifest as well.

Useful? React with 👍 / 👎.

pi
read -r -p "Pi profile name or alias: " pi_profile
echo -e "${YELLOW}Run /login, choose a provider, verify the matching identity, then exit Pi.${NC}"
ppi login "$pi_profile"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid invoking ppi before it exists

When a user selects Pi CLI in interactive setup without already having run the AI profile setup, this branch installs only the npm pi binary and then calls ppi, but this flow never creates the ppi symlink or Pi profile manifest. Accepting the prompt therefore fails instead of launching the newly installed CLI; provision ppi before this prompt, detect command -v ppi, or fall back to standard pi login when no profiles are configured.

Useful? React with 👍 / 👎.

Comment on lines +32 to +33
local_file="${PI_PROFILES_HOME:-$HOME/.pi-profiles}/profiles/$profile/agent/auth.json"
credential_ref="ai/secrets/pi/$profile.auth.sops.yaml"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Sanitize profile before deriving escrow paths

When ai/source.json contains an authorized profile name with / or .., this interpolates it directly into both the local credential path and repository secret path, so backup/restore can read or write outside $PI_PROFILES_HOME/profiles or ai/secrets/pi. Restrict Pi profile names to a safe basename, or resolve and verify these paths before using them.

Useful? React with 👍 / 👎.

Comment thread scripts/wave-container-shell.sh Outdated
if [[ "$check_only" == true ]]; then
docker exec --user "$container_user" --workdir "$workdir" "$container" "$shell_path" -lc \
'printf "%s\n" "wave-container-shell-ok"; whoami; pwd; command -v claude-profile; command -v pclaude; command -v codex-profile; command -v pcodex; command -v pgemini; command -v pgrok; command -v pglm; test -d "$HOME/.claude-profiles"; test -d "$HOME/.chatgpt-profiles"; test -d "$HOME/.gemini-profiles"; test -d "$HOME/.grok-profiles"; test -d "$HOME/.glm-profiles"'
'printf "%s\n" "wave-container-shell-ok"; whoami; pwd; command -v claude-profile; command -v pclaude; command -v codex-profile; command -v pcodex; command -v ppi; command -v pgemini; command -v pgrok; command -v pglm; test -d "$HOME/.claude-profiles"; test -d "$HOME/.chatgpt-profiles"; test -d "$HOME/.pi-profiles"; test -d "$HOME/.gemini-profiles"; test -d "$HOME/.grok-profiles"; test -d "$HOME/.glm-profiles"'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recreate Wave containers for the new Pi profile mount

For an existing Wave-created container from before this change, container_missing_required_mounts still omits /home/${container_user}/.pi-profiles, so the script does not call recreate_with_compose; this newly added check can fail and normal shells will not see host Pi profile credentials. Add the Pi profile directory to the required mount list so existing containers pick up the new mount.

Useful? React with 👍 / 👎.

Comment on lines +39 to +41
existing = profiles.get(raw["name"])
if existing is None:
profiles[raw["name"]] = {"name": raw["name"], **normalized, "providers": [provider]}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject ambiguous names in the unified ppi namespace

When Pi profiles are composed from separate provider manifests, two different profile names can legally share an alias because those aliases only need to be unique within each provider. This code only keys by raw profile name, so the emitted pi-profiles.json can contain profiles like alpha and zeta both aliasing team; pi-profile then resolves ppi team by the first matching row, causing login/logout/run to target whichever profile sorts first. Please track case-insensitive names and aliases across the unified Pi manifest and reject collisions.

Useful? React with 👍 / 👎.

fi
fi

[ "$windows_pi" = true ] && return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fail when the required WSL Pi install is missing

In WSL, if a Windows pi.cmd already exists but the WSL/Linux npm install above fails or no WSL npm is available, this fallback still returns success with no Linux pi on PATH. The new ppi launcher and devBench containers invoke the Linux CLI, not the Windows shim, so setup can report Pi usable while isolated profile logins still fail; return an error or explicitly report a Windows-only install when WSL Pi could not be installed.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

scripts/setup-ai-profiles.sh:54

  • The Pi profile composition block only runs when all five provider manifests exist because ls file1 file2 ... returns non-zero if any file is missing. This prevents generating pi-profiles.json when only a subset of provider manifests is present (which should be supported).
if ls "$config_dir"/{claude,openai,gemini,grok,glm}-profiles.json >/dev/null 2>&1; then
  pi_profile_roots=(
    --profile-root "claude=${CLAUDE_PROFILES_HOME:-$HOME/.claude-profiles}/profiles"
    --profile-root "openai=${CODEX_PROFILES_HOME:-$HOME/.chatgpt-profiles}/profiles"
    --profile-root "gemini=${GEMINI_PROFILES_HOME:-$HOME/.gemini-profiles}/profiles"
    --profile-root "grok=${GROK_PROFILES_HOME:-$HOME/.grok-profiles}/profiles"
    --profile-root "glm=${GLM_PROFILES_HOME:-$HOME/.glm-profiles}/profiles"
  )
  python3 "$repo_dir/scripts/compose-pi-profiles.py" \
    --config-dir "$config_dir" \
    "${pi_profile_roots[@]}" \
    --output "$config_dir/pi-profiles.json"
fi

scripts/setup-pi-profiles.sh:71

  • This script creates ~/.local/bin/ppi and ~/.local/bin/pi-profile symlinks pointing to $repo_dir/scripts/pi-profile, but that file does not exist in the repository. The resulting launchers will be dangling symlinks.
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/pi-profile"
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/ppi"

scripts/interactive-setup.sh:2401

  • The credential-setup flow unconditionally runs ppi login ..., but items_needing_creds+=("pi") is added based on the presence of pi (or ~/.npm-global/bin/pi), not ppi. On a fresh install where pi exists but ppi hasn’t been installed yet, this will fail with ppi: command not found.
                    if [[ ! $launch_pi =~ ^[Nn] ]]; then
                        read -r -p "Pi profile name or alias: " pi_profile
                        echo -e "${YELLOW}Run /login, choose a provider, verify the matching identity, then exit Pi.${NC}"
                        ppi login "$pi_profile"

scripts/setup-windows-tools.sh:162

  • wsl_npm is assigned without local, which leaks a global variable out of this function and can accidentally affect other logic in this script.
        echo "  Installing Pi Terminal in WSL/Linux for ppi and devBench use..."
        wsl_npm="$(command -v npm)"
        [[ -x /usr/bin/npm ]] && wsl_npm=/usr/bin/npm
        if "$wsl_npm" install -g --ignore-scripts "$PI_NPM_PACKAGE"; then

Comment on lines +23 to +24
[[ -n "$profile" ]] || { echo "--profile is required" >&2; exit 2; }
[[ -r "$identity_file" ]] || { echo "Recovery identity is not readable" >&2; exit 1; }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a7af38966c

ℹ️ 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".

Comment thread base-image/files/pi-profile Outdated
Comment on lines +18 to +22
jq -r --arg name "$normalized" '
.profiles[]
| select(((.name | ascii_downcase) == $name) or any(.aliases[]?; ascii_downcase == $name))
| [.name,.email,.family,((.providers // []) | join(","))] | @tsv
' "$manifest" | head -n 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add a metadata fallback for mounted Pi profiles

In a devBench/Wave container, only ~/.pi-profiles is mounted (checked docker-compose.mounts.yml:45-46 and scripts/wave-container-shell.sh:177-178); ~/.config/workbenches/pi-profiles.json is not. Because ppi always queries $manifest here and setup-pi-profiles.sh stores the same metadata in each profile’s .profile.json, ppi team001, ppi login team001, and alias resolution fail with jq: Could not open file ... inside the advertised container flow unless the user manually mounts the config directory. Please fall back to the mounted .pi-profiles/profiles/*/.profile.json metadata like the other profile launchers.

Useful? React with 👍 / 👎.

pi
read -r -p "Pi profile name or alias: " pi_profile
echo -e "${YELLOW}Run /login, choose a provider, verify the matching identity, then exit Pi.${NC}"
ppi login "$pi_profile"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check that the ppi launcher exists before invoking it

In the fresh Pi CLI path, line 1908 adds pi to items_needing_creds as soon as the pi binary exists, but no code in that install branch creates ppi—that only happens when setup-pi-profiles.sh runs with a manifest. On a workstation where the user selected only Pi Terminal or has no profile manifest yet, answering yes reaches this line and aborts with ppi: command not found (or a missing manifest) instead of launching the previously working pi login flow. Please guard this with command -v ppi/profile setup or fall back to pi.

Useful? React with 👍 / 👎.

fi
fi

[ "$windows_pi" = true ] && return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not hide failed WSL Pi installs behind Windows Pi

When Windows Pi is already installed but WSL/Linux pi is missing, this fallback returns success even after the WSL npm install path above fails or is unavailable. This change makes WSL/Linux Pi the runtime used by ppi and devBench, so setup-windows-tools.sh pi_terminal can report success while leaving the actual ppi runtime unusable in WSL; please return failure unless command -v pi succeeds in WSL or the WSL install completed.

Useful? React with 👍 / 👎.

Comment thread scripts/wave-container-shell.sh Outdated
Comment on lines +323 to +324
docker exec --user "$container_user" --workdir "$workdir" "$container" "$shell_path" -lc \
'printf "%s\n" "wave-container-shell-ok"; whoami; pwd; command -v claude-profile; command -v pclaude; command -v codex-profile; command -v pcodex; command -v pgemini; command -v pgrok; command -v pglm; test -d "$HOME/.claude-profiles"; test -d "$HOME/.chatgpt-profiles"; test -d "$HOME/.gemini-profiles"; test -d "$HOME/.grok-profiles"; test -d "$HOME/.glm-profiles"'
'printf "%s\n" "wave-container-shell-ok"; whoami; pwd; command -v claude-profile; command -v pclaude; command -v codex-profile; command -v pcodex; command -v ppi; command -v pgemini; command -v pgrok; command -v pglm; test -d "$HOME/.claude-profiles"; test -d "$HOME/.chatgpt-profiles"; test -d "$HOME/.pi-profiles"; test -d "$HOME/.gemini-profiles"; test -d "$HOME/.grok-profiles"; test -d "$HOME/.glm-profiles"'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recreate stale Wave containers for the new Pi mount

The new --check assertion requires $HOME/.pi-profiles, but the stale-container detection list was not updated to require that mount before reusing an existing devcontainer. For users who already created a Wave container before this change, wave-container-shell.sh --check now fails here and normal launches still reuse a container without the new Pi profile mount; add .pi-profiles to the required mounts that trigger recreation.

Useful? React with 👍 / 👎.

Comment on lines +32 to +34
local_file="${PI_PROFILES_HOME:-$HOME/.pi-profiles}/profiles/$profile/agent/auth.json"
credential_ref="ai/secrets/pi/$profile.auth.sops.yaml"
secret_file="$repo_dir/$credential_ref"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject unsafe profile names before building escrow paths

profile is accepted from any matching entry in ai/source.json, but this code interpolates it directly into both the local auth path and the repository secret path. A malformed registry profile name containing / or .. (the source contract only requires a non-empty string) can make backup/restore read or write outside ~/.pi-profiles/profiles and ai/secrets/pi; validate the profile slug or use a declared, normalized credentialRef before constructing these paths.

Useful? React with 👍 / 👎.

[[ "$(stat -c '%a' "$identity_file" 2>/dev/null || stat -f '%Lp' "$identity_file")" == 600 ]] || { echo "Recovery identity must have mode 0600" >&2; exit 1; }
command -v jq >/dev/null && command -v sops >/dev/null || { echo "jq and sops are required" >&2; exit 1; }

jq -e --arg profile "$profile" '[.profiles[]?[]? | select(.name == $profile)] | length > 0' "$repo_dir/ai/source.json" >/dev/null || {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the registry membership check boolean

This guard does not actually reject unknown profiles because jq -e exits successfully for the numeric value 0; with a missing profile the expression still returns 0, so backup/restore proceeds and can create ai/secrets/pi/NAME.auth.sops.yaml for a profile that is not in the registry. Pipe the count to > 0 (or use any(...)) so the check fails before touching local or escrowed credentials.

Useful? React with 👍 / 👎.

Comment on lines +39 to +41
existing = profiles.get(raw["name"])
if existing is None:
profiles[raw["name"]] = {"name": raw["name"], **normalized, "providers": [provider]}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject ambiguous Pi names and aliases while composing

The composed manifest only keys profiles by the exact name here and never checks case-insensitive collisions between names and aliases. Since ppi resolves either a name or any alias and then takes the first match, cases like one profile aliasing another profile's name, or two providers contributing the same alias for different profile names, will route ppi login ALIAS to the wrong isolated Pi home; reject those collisions during composition.

Useful? React with 👍 / 👎.

Comment on lines +52 to +56
if jq -e 'index("claude") != null' <<<"$providers" >/dev/null; then
settings_tmp="$(mktemp "$agent_dir/.settings.XXXXXX.tmp")"
jq '
.packages = (((.packages // []) | map(select(. != "npm:pi-claude-cli" and . != "npm:@ramarivera/pi-claude-cli" and . != "npm:@ramarivera/pi-claude-cli@0.3.1"))) + ["npm:@ramarivera/pi-claude-cli@0.3.1"])
| .defaultProvider = "pi-claude-cli"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear stale Claude defaults when provider membership changes

If a profile previously had the claude provider, this block writes pi-claude-cli as the default provider, but when a later manifest removes claude the setup simply skips the block and leaves those old settings in place. In that scenario ppi PROFILE still defaults to the Claude proxy even though ppi no longer exports CLAUDE_CONFIG_DIR for the profile, which can break Pi startup or fall back to an unintended Claude account; remove or recompute the Claude package/defaults when providers no longer contains claude.

Useful? React with 👍 / 👎.

@sourcery-ai

sourcery-ai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds isolated, per-identity Pi Coding Agent profiles driven by canonical provider manifests, with a new ppi launcher, container/home directory mounts, and encrypted credential escrow, while keeping the legacy ~/.pi/agent untouched and updating setup flows and docs accordingly.

Sequence diagram for ppi login and Pi credential escrow

sequenceDiagram
  actor User
  participant ppi
  participant pi
  participant pi_credential_escrow
  participant Tenant_Credentials_repo

  User->>ppi: ppi login PROFILE
  ppi->>ppi: set PI_CODING_AGENT_DIR
  ppi->>pi: start Pi terminal
  User->>pi: /login
  pi-->>User: provider auth flow
  User-->>pi: complete authentication

  User->>pi_credential_escrow: pi-credential-escrow backup --repo PATH --profile PROFILE --identity-file tenant-recovery.agekey
  pi_credential_escrow->>Tenant_Credentials_repo: write ai/secrets/pi/PROFILE.auth.sops.yaml
  pi_credential_escrow->>Tenant_Credentials_repo: read back and verify auth hash
  pi_credential_escrow-->>User: backup verified
Loading

File-Level Changes

Change Details Files
Introduce composition and setup pipeline for Pi profiles derived from existing provider manifests and on-disk profiles.
  • Add compose-pi-profiles.py to merge Claude/OpenAI/Gemini/Grok/GLM manifests and profile roots into a versioned pi-profiles.json, enforcing identity consistency across providers.
  • Add setup-pi-profiles.sh to materialize ~/.pi-profiles trees, create per-profile agent homes with strict permissions, and seed/patch Pi settings.json (including npm command and Claude-backed defaults).
  • Extend scripts/setup-ai-profiles.sh to generate pi-profiles.json when provider manifests exist and to invoke the Pi profile setup script when the manifest is present.
scripts/compose-pi-profiles.py
scripts/setup-pi-profiles.sh
scripts/setup-ai-profiles.sh
Add canonical ppi/pi-profile launchers that route Pi to isolated per-profile homes and integrate with containers and setup flows.
  • Introduce scripts/pi-profile and a corresponding base-image/files/pi-profile launcher installed as pi-profile/ppi in images and containers, wiring PI_CODING_AGENT_DIR to ~/.pi-profiles/profiles/NAME/agent.
  • Update wave-container-shell.sh and docker-compose.mounts.yml (plus newBench and mounts docs) to mount ~/.pi-profiles into containers and verify ppi presence and profile directories in health checks.
  • Adjust interactive setup (shell and UI) to reference ppi login PROFILE instead of bare pi, and to treat profiles with Pi auth under ~/.pi-profiles as satisfying the Pi credential requirement.
scripts/pi-profile
base-image/files/pi-profile
scripts/wave-container-shell.sh
docker-compose.mounts.yml
docs/MOUNTS-README.md
docs/newBench.md
scripts/interactive-setup.sh
scripts/setup-ui/src/utils/installers.ts
Refine Pi CLI installation on Windows/WSL and ensure Pi Coding Agent is reliably installed for Linux environments and base images.
  • Change setup-windows-tools.sh to detect existing Windows Pi separately, prefer /usr/bin/npm when installing Pi in WSL/Linux, and avoid failing when Windows Pi is present but WSL/npm is absent.
  • Update interactive-setup.sh to attempt Pi Coding Agent installation via /usr/bin/npm before a generic npm, and to treat a locally installed pi under ~/.npm-global/bin as satisfying presence checks.
  • Extend install-ai-clis.sh and base-image/Dockerfile to install the Pi Coding Agent and the pi-profile/ppi launchers into the base image.
scripts/setup-windows-tools.sh
scripts/interactive-setup.sh
base-image/install-ai-clis.sh
base-image/Dockerfile
Document and wire the new Pi harness profile model and usage.
  • Add pi-multi-account-profiles.md describing ppi usage, identity alignment with other providers, Claude-backed Pi behavior, npm command recording, and escrow workflows.
  • Update multi-provider profile docs and README tables to include the Pi harness/ppi entry and describe isolated usage instead of direct pi + /login flows.
  • Clarify mounts documentation to include ~/.pi-profiles as a cached host-managed path used by Pi profile launchers.
docs/pi-multi-account-profiles.md
docs/multi-provider-profiles.md
README.md
docs/MOUNTS-README.md
docs/newBench.md
Add credential-manager tests covering Pi profile composition, launcher behavior, and encrypted escrow.
  • Add PiProfilesTest to validate idempotent composition and setup from provider manifests, including alias resolution, provider aggregation, and Claude defaults in Pi settings.
  • Exercise the ppi launcher via a fake pi binary to assert PI_CODING_AGENT_DIR and CLAUDE_CONFIG_DIR wiring and file permission expectations for profile homes and settings.
  • Add escrow round-trip tests ensuring Pi auth is written to, encrypted in, and restored from ai/secrets/pi/PROFILE.auth.sops.yaml using a dedicated harness secret without leaking credentials.
apps/credential-manager/test_pi_profiles.py
scripts/pi-credential-escrow

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="apps/credential-manager/test_pi_profiles.py" line_range="9" />
<code_context>
+class PiProfilesTest(unittest.TestCase):
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests verifying that standard `~/.pi/agent` credentials are preserved and never implicitly copied

The PR’s main requirement—preserving `~/.pi/agent` without implicit copying—is not covered by tests. Please add a test that sets a fake `HOME` with an existing `~/.pi/agent`, runs `setup-pi-profiles.sh` and `ppi` for a profile, and then verifies that:
- The profile’s agent path under `.pi-profiles/profiles/.../agent` differs from `~/.pi/agent`.
- No files from `~/.pi/agent` (e.g., `auth.json`) appear in the profile directory unless the test creates them explicitly.
This ensures the behavior promised in the PR description is actually enforced by tests.

```suggestion
REPO = pathlib.Path(__file__).parents[2]


def _run_cmd(args, cwd=None, env=None):
    completed = subprocess.run(
        args,
        cwd=cwd,
        env=env,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        check=True,
    )
    return completed


class PiProfilesAgentPreservationTest(unittest.TestCase):
    def test_standard_agent_is_not_copied_into_profile(self):
        with tempfile.TemporaryDirectory(dir="/tmp") as temporary:
            root = pathlib.Path(temporary)
            home = root / "home"
            home.mkdir(parents=True)

            # Create standard ~/.pi/agent with a fake auth.json
            pi_dir = home / ".pi"
            agent_dir = pi_dir / "agent"
            agent_dir.mkdir(parents=True)
            (agent_dir / "auth.json").write_text('{"token": "standard-agent-token"}')

            # Set up fake HOME
            env = os.environ.copy()
            env["HOME"] = str(home)

            # Run setup-pi-profiles.sh to initialize profiles support
            setup_script = REPO / "apps" / "credential-manager" / "setup-pi-profiles.sh"
            _run_cmd(["bash", str(setup_script)], cwd=str(REPO), env=env)

            # Run `ppi` once to create a profile and its agent dir
            profile_name = "test-profile"
            ppi_bin = REPO / "apps" / "credential-manager" / "ppi"
            _run_cmd(
                [str(ppi_bin), "profiles", "use", profile_name],
                cwd=str(REPO),
                env=env,
            )

            # Resolve paths
            profile_root = home / ".pi-profiles" / "profiles" / profile_name
            profile_agent_dir = profile_root / "agent"

            # Sanity: profile agent directory should exist
            self.assertTrue(
                profile_agent_dir.exists(),
                "profile agent directory should be created",
            )

            # 1. The profile’s agent path must differ from the standard ~/.pi/agent path
            self.assertNotEqual(
                profile_agent_dir.resolve(),
                agent_dir.resolve(),
                "profile agent directory must not be the same as the standard ~/.pi/agent directory",
            )

            # 2. No files from ~/.pi/agent (e.g., auth.json) should appear in the profile directory
            self.assertFalse(
                (profile_agent_dir / "auth.json").exists(),
                "auth.json from ~/.pi/agent must not be implicitly copied into the profile agent directory",
            )
```
</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.

import unittest


REPO = pathlib.Path(__file__).parents[2]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Add tests verifying that standard ~/.pi/agent credentials are preserved and never implicitly copied

The PR’s main requirement—preserving ~/.pi/agent without implicit copying—is not covered by tests. Please add a test that sets a fake HOME with an existing ~/.pi/agent, runs setup-pi-profiles.sh and ppi for a profile, and then verifies that:

  • The profile’s agent path under .pi-profiles/profiles/.../agent differs from ~/.pi/agent.
  • No files from ~/.pi/agent (e.g., auth.json) appear in the profile directory unless the test creates them explicitly.
    This ensures the behavior promised in the PR description is actually enforced by tests.
Suggested change
REPO = pathlib.Path(__file__).parents[2]
REPO = pathlib.Path(__file__).parents[2]
def _run_cmd(args, cwd=None, env=None):
completed = subprocess.run(
args,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True,
)
return completed
class PiProfilesAgentPreservationTest(unittest.TestCase):
def test_standard_agent_is_not_copied_into_profile(self):
with tempfile.TemporaryDirectory(dir="/tmp") as temporary:
root = pathlib.Path(temporary)
home = root / "home"
home.mkdir(parents=True)
# Create standard ~/.pi/agent with a fake auth.json
pi_dir = home / ".pi"
agent_dir = pi_dir / "agent"
agent_dir.mkdir(parents=True)
(agent_dir / "auth.json").write_text('{"token": "standard-agent-token"}')
# Set up fake HOME
env = os.environ.copy()
env["HOME"] = str(home)
# Run setup-pi-profiles.sh to initialize profiles support
setup_script = REPO / "apps" / "credential-manager" / "setup-pi-profiles.sh"
_run_cmd(["bash", str(setup_script)], cwd=str(REPO), env=env)
# Run `ppi` once to create a profile and its agent dir
profile_name = "test-profile"
ppi_bin = REPO / "apps" / "credential-manager" / "ppi"
_run_cmd(
[str(ppi_bin), "profiles", "use", profile_name],
cwd=str(REPO),
env=env,
)
# Resolve paths
profile_root = home / ".pi-profiles" / "profiles" / profile_name
profile_agent_dir = profile_root / "agent"
# Sanity: profile agent directory should exist
self.assertTrue(
profile_agent_dir.exists(),
"profile agent directory should be created",
)
# 1. The profile’s agent path must differ from the standard ~/.pi/agent path
self.assertNotEqual(
profile_agent_dir.resolve(),
agent_dir.resolve(),
"profile agent directory must not be the same as the standard ~/.pi/agent directory",
)
# 2. No files from ~/.pi/agent (e.g., auth.json) should appear in the profile directory
self.assertFalse(
(profile_agent_dir / "auth.json").exists(),
"auth.json from ~/.pi/agent must not be implicitly copied into the profile agent directory",
)

brettheap and others added 4 commits July 21, 2026 01:05
Codex profiles now link sessions, archived_sessions, history.jsonl, and
session_index.jsonl to a per-family state directory so another login in the
same trust family can resume the same conversations, while auth.json, config,
caches, and SQLite state stay profile-local. Adopting an existing profile
merges its portable state into the family without overwriting rollouts and
retains a .pre-shared-state recovery copy.

Add setup-multi-cli-codex-profiles.ps1 to sync registry-owned Multi-CLI
profiles with the Windows Codex Desktop store for company families.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document that each personal login should use a unique family when its history
must stay separate from other profiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Onboarding now asks for the company login email first and defaults the company
name to the email domain's first label (you@acme.com -> acme); the GitHub org
in turn defaults to the company name. Both remain editable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document an optional shared Keycloak identity server plus a Python broker that
gates the existing SOPS registries: Keycloak proves identity, the grant files
stay authoritative for authorization, SOPS stays the at-rest store, and the
broker holds one age identity per tenant so no decryption key reaches a
workstation or agent. Cross-link it from the credential ownership doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 01:14
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@brettheap brettheap changed the title Add isolated Pi account profiles Pi profile support, Codex family history, and Keycloak broker design Jul 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

scripts/pi-credential-escrow:48

  • json_hash relies on sha256sum, which is not available by default on macOS (the repo already uses a sha256sum/shasum -a 256 fallback elsewhere). This makes backup fail even when jq and sops are installed.
json_hash() { jq -S -c . "$1" | sha256sum | awk '{print $1}'; }

Comment on lines +91 to +97
$metadata = [ordered]@{
name = $profile.name
email = $profile.email
family = $profile.family
aliases = @($profile.aliases)
managedBy = "workBenches"
} | ConvertTo-Json -Depth 4
Comment on lines 274 to 282
pi_cli)
if command -v pi &>/dev/null; then
[[ -f "$HOME/.pi/agent/auth.json" ]] && echo "installed" || echo "needs creds"
if [[ -f "$HOME/.pi/agent/auth.json" ]] || find "$HOME/.pi-profiles/profiles" -mindepth 3 -maxdepth 3 -name auth.json -print -quit 2>/dev/null | grep -q .; then
echo "installed"
else
echo "needs creds"
fi
else
echo "not installed"
Sequence delivery so the riskiest assumptions (porting the escrow logic to
Python, and the end-to-end authenticate -> authorize -> decrypt -> materialize
loop) are proven first. Phase 0 builds the broker core with no Keycloak; phase
1 is the MVP vertical slice (real Keycloak, one tenant, pclaude); phases 2-4
add provider/tenant breadth, unattended agents, and broader cred kinds; phase
5 is hardening. Resolve the lease-semantics and federated-cloud open questions
(opaque TTLs and no federation in v1, respectively).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 01:21
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

scripts/setup-ai-profiles.sh:46

  • The ls "$config_dir"/{claude,openai,gemini,grok,glm}-profiles.json check only succeeds when all five manifests exist. That prevents Pi profile composition when a user has only a subset of providers configured (even though the script already applies each manifest independently above). Switch this to an explicit “any manifest exists” check so compose-pi-profiles.py runs whenever at least one provider manifest is present.
if ls "$config_dir"/{claude,openai,gemini,grok,glm}-profiles.json >/dev/null 2>&1; then
  pi_profile_roots=(
    --profile-root "claude=${CLAUDE_PROFILES_HOME:-$HOME/.claude-profiles}/profiles"
    --profile-root "openai=${CODEX_PROFILES_HOME:-$HOME/.chatgpt-profiles}/profiles"
    --profile-root "gemini=${GEMINI_PROFILES_HOME:-$HOME/.gemini-profiles}/profiles"

scripts/setup-pi-profiles.sh:71

  • These symlinks point to $repo_dir/scripts/pi-profile, but the repository only adds base-image/files/pi-profile in this PR. As-is, setup-pi-profiles.sh will create broken launchers (pi-profile/ppi) on the host and the subsequent ppi ... commands will fail.
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/pi-profile"
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/ppi"

Comment on lines +95 to +98
aliases = @($profile.aliases)
managedBy = "workBenches"
} | ConvertTo-Json -Depth 4
Set-Content -LiteralPath (Join-Path $profileDir ".profile.json") -Value $metadata -Encoding utf8
Copilot AI review requested due to automatic review settings July 21, 2026 03:57
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@brettheap

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

scripts/setup-pi-profiles.sh:71

  • setup-pi-profiles.sh installs the ppi/pi-profile launchers by symlinking to $repo_dir/scripts/pi-profile, but that file doesn't exist in the repo (only base-image/files/pi-profile is present). This leaves users with a broken launcher after running setup.
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/pi-profile"
ln -sfn "$repo_dir/scripts/pi-profile" "$HOME/.local/bin/ppi"

scripts/pi-credential-escrow:48

  • json_hash pipes into sha256sum, but this script otherwise tries to be cross-platform (e.g., stat -f fallback). sha256sum is not available by default on macOS, so backup/restore can fail even when jq/sops are installed.
json_hash() { jq -S -c . "$1" | sha256sum | awk '{print $1}'; }

scripts/interactive-setup.sh:275

  • pi_cli status only checks command -v pi, but other parts of the script treat ~/.npm-global/bin/pi as an installed location. This can incorrectly report "not installed" after a successful npm install when the user's PATH doesn't include the npm-global bin dir yet.
            if command -v pi &>/dev/null; then

Comment thread docker-compose.mounts.yml
Comment on lines +16 to +18
# Zsh history persistence. Mount the named volume as a directory; Docker named
# volumes are directories and cannot safely target the ~/.zsh_history file.
- ${BENCH_NAME:-bench}-zshhistory:/home/${USER}/.workbenches-history
Copilot AI review requested due to automatic review settings July 24, 2026 19:55
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 42 out of 42 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (3)

base-image/files/claude-profile:136

  • Same issue in the list path: find "$base/profiles" ... errors when the directory is missing and will abort claude-profile list under set -e even though listing should be a safe/no-op before setup.
      done < <(find "$base/profiles" -mindepth 2 -type f -name .profile.json -print0)

base-image/files/codex-profile:79

  • Same find/set -e issue in the list path: this will abort codex-profile list if $base/profiles hasn’t been created yet.
      done < <(find "$base/profiles" -mindepth 2 -type f -name .profile.json -print0)

scripts/setup-ai-profiles.sh:54

  • if ls "$config_dir"/{claude,openai,gemini,grok,glm}-profiles.json ... only succeeds when all five manifests exist. compose-pi-profiles.py already tolerates missing provider manifests, so this gate should trigger when any provider manifest exists; otherwise Pi profiles won’t be composed in common partial-provider setups.

fi
fi
done
done < <(find "$base/profiles" -mindepth 2 -type f -name .profile.json -print0)
fi
fi
done
done < <(find "$base/profiles" -mindepth 2 -type f -name .profile.json -print0)
Comment on lines +58 to +60
list)
jq -r '.profiles[] | [.name,.family,.email,((.aliases // [])|join(",")),((.providers // [])|join(","))] | @tsv' "$manifest"
;;
fallback_row="$row"
fi
fi
done < <(find "$base/profiles" -mindepth 2 -type f -name .profile.json -print0)
claude_profile_depth="$candidate_depth"
fi
fi
done < <(find "$claude_profiles_root" -mindepth 1 -type d -name "$profile" -print0)
selected_depth="$depth"
fi
fi
done < <(find "$profile_root" -mindepth 1 -type d -name "$name" -print0)
Copilot AI review requested due to automatic review settings July 25, 2026 05:51
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 45 out of 45 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

scripts/setup-ai-profiles.sh:42

  • setup-ai-profiles.sh only composes Pi profiles when all five provider manifests exist because ls returns non-zero if any path is missing. This means Pi profile generation can be silently skipped for partial setups (e.g., only claude/openai manifests present). Use a glob-aware check that succeeds when any of those manifests exists.

Comment on lines +222 to +234
log_info "Installing Herdr terminal workspace manager..."
herdr_installer="$(mktemp)"
if run_with_timeout "$RELEASE_DOWNLOAD_TIMEOUT" "Herdr installer download" \
curl -fsSL --retry 3 --connect-timeout 10 --max-time 60 \
-o "$herdr_installer" "$HERDR_INSTALL_URL"; then
if ! run_with_timeout "$RELEASE_DOWNLOAD_TIMEOUT" "Herdr install" \
env HERDR_INSTALL_DIR=/usr/local/bin sh "$herdr_installer"; then
log_error "Herdr installation failed (continuing)"
fi
else
log_error "Herdr installer download failed (continuing)"
fi
rm -f "$herdr_installer"
Comment on lines +32 to +34
local_file="${PI_PROFILES_HOME:-$HOME/.pi-profiles}/profiles/$profile/agent/auth.json"
credential_ref="ai/secrets/pi/$profile.auth.sops.yaml"
secret_file="$repo_dir/$credential_ref"
Comment on lines +18 to +22
FAMILIES = ("opensoft", "medx", "personal")


def family_for(profile: dict) -> str:
email = str(profile.get("email", "")).lower()
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.

2 participants