fix(lib): resolve root causes of login prompt in claude and TOS/theme onboarding in agy
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
# 구현 계획서 (Refined v2): 신규 격리 에이전트의 /login·TOS/테마 프롬프트 근본 원인 및 조치
|
||||||
|
|
||||||
|
> 본 문서는 Creator의 Challenge Report(대상: Job 4c9ca21f / 본 계획서의 v1)를 반영해 정교화한 버전이다. v1 대비 변경점은 0장에 요약한다.
|
||||||
|
|
||||||
|
## 0. Challenge 반영 변경 이력 (v1 → v2)
|
||||||
|
|
||||||
|
| # | Challenge 지적 사항 | 판정 | v2 조치 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | 심볼릭 링크 기반 시딩은 mutable 디렉터리/preference에 대해 쓰기 격리를 제공하지 못해, 한 세션의 변경이 호스트와 다른 동시 세션에 즉시 전파됨 (race/상태 오염) | **타당함, 수용** | 3장을 "쓰기 격리 재설계"로 전면 확장. 대상을 위험도별로 분리해 단일-정본 preference/state 파일은 `cp`(1회 복사)로, append형 대용량 데이터 디렉터리는 현행 유지+후속 논의 항목으로 분리 |
|
||||||
|
| 2 | `claude`는 `HOME`이 리디렉션되지 않고 `CLAUDE_CONFIG_DIR`만 바뀌므로, macOS Keychain 조회는 항상 실제 `$HOME`을 사용 → `$root/Library/Keychains` 심링크는 `claude`에 한해 아무 효과가 없는 죽은 코드 | **타당함, 수용** | 2.1의 "3중 원인" 서술에서 Keychain 항목 제거, root cause를 2개로 정정. 3장 표에서 `claude`의 Keychain 시딩 항목을 "제거 대상(dead code)"으로 변경. `agy`는 lever가 `home`이라 Keychain 시딩이 실질적으로 유효함을 명시적으로 구분 |
|
||||||
|
|
||||||
|
## 1. 목표
|
||||||
|
새로 생성된 `claude` 격리 세션이 `/login`을 요구하고, `agy` 격리 세션이 TOS/테마 선택 화면을 띄우는 문제의 근본 원인을 분석하고, **동시에 이 과정에서 세션 간 쓰기 격리(write-isolation)를 훼손하지 않도록** `.agents/skills/lib.sh`의 `provision_isolation()`을 재설계한다.
|
||||||
|
|
||||||
|
## 2. 근본 원인 분석 (Root Cause Analysis) — 정정판
|
||||||
|
|
||||||
|
### 2.1 `claude` — `/login` 프롬프트 (정정: 원인은 2개)
|
||||||
|
`claude`의 격리 lever는 `claude_config_dir`(`CLAUDE_CONFIG_DIR=$root`)이며, **`HOME`은 리디렉션되지 않는다** (`isolation_env_prefix()`, lib.sh:1318-1325 — `claude` 분기는 `CLAUDE_CONFIG_DIR`만 설정). 이 사실이 원인 분석의 핵심 제약이다.
|
||||||
|
|
||||||
|
- **원인 A — `session-env`/`sessions`/`cache` 미시딩**: 이 세 디렉터리는 `$root` 바로 아래(`$root/session-env` 등)에 위치하며, 이는 `CLAUDE_CONFIG_DIR`가 지배하는 네임스페이스에 정확히 속한다. 기존 코드는 이 세 항목을 전혀 시딩하지 않았고, CLI는 격리된 `CLAUDE_CONFIG_DIR`를 "낯선 세션"으로 인식해 로그인 플로우를 반복 요구했다.
|
||||||
|
- **원인 B — `.credentials.json` 무조건 링크 버그**: `ln -sfn "$HOME/.claude/.credentials.json" ...`이 존재 확인 없이 실행되어, 이 파일이 없는 환경(실측: 이 머신에서 `.credentials.json`은 부재)에서 깨진 심볼릭 링크를 생성하는 상태 불일치 버그.
|
||||||
|
- **~~Keychain 시딩~~ (v1에서 원인으로 지목했으나 정정)**: `claude` 프로세스는 `HOME`이 그대로이므로 macOS `security`/Security.framework 조회는 항상 실제 `$HOME/Library/Keychains`를 향한다. `$root/Library/Keychains` 심링크는 `CLAUDE_CONFIG_DIR` 네임스페이스 밖에 있어 `claude` 프로세스의 어떤 조회 경로도 거치지 않는다 — **효과 없는 죽은 코드**이며 `/login` 프롬프트 해소에 기여하지 않았다.
|
||||||
|
|
||||||
|
**결론(정정)**: `/login` 반복 프롬프트의 실제 원인은 (a) `session-env`/`sessions`/`cache` 미시딩, (b) 존재하지 않는 credentials 파일에 대한 무조건적 링크 생성, 2가지다. Keychain 시딩은 `claude` 케이스에서는 무관한 항목이었다.
|
||||||
|
|
||||||
|
### 2.2 `agy` — TOS/테마 선택 프롬프트 (변경 없음, Keychain 유효성 근거 보강)
|
||||||
|
`agy`의 lever는 `home`이며, `isolation_env_prefix()`가 `HOME=$root`를 설정해 **`HOME`이 실제로 리디렉션**된다. 따라서 `agy`에서는 `$root/Library/Keychains` 심링크가 실제 Keychain 조회 경로 위에 있어 유효하다 — 이는 `claude`와 정확히 대비되는 지점이며, Challenge #2가 "claude에 한해" 지적한 것과 일치한다.
|
||||||
|
|
||||||
|
Antigravity는 CLI(`~/.gemini/antigravity-cli`, 기시딩)와 **IDE**(`~/.gemini/antigravity-ide`, `com.google.antigravity-ide.plist`, `~/Library/Application Support/Antigravity IDE`)로 나뉜다. 기존 코드가 IDE 전용 상태 저장소(TOS 동의/테마)를 시딩 대상에서 누락한 것이 근본 원인이며, 이 결론은 v1과 동일하게 유지된다.
|
||||||
|
|
||||||
|
## 3. 구현 계획 — 쓰기 격리 재설계 (Write-Isolation Redesign)
|
||||||
|
|
||||||
|
Challenge #1의 핵심은: **디렉터리를 `ln -sfn`으로 연결하면, 그 안에 새로 생성되는 파일/갱신되는 값이 실제로는 호스트의 원본 디렉터리에 그대로 쓰여지고, 동시에 실행 중인 다른 격리 세션에도 즉시 보인다**는 점이다. 이를 단일 정책(전부 `cp -R`)으로 일괄 해결하기보다, 항목별 위험도에 따라 차등 전략을 적용한다.
|
||||||
|
|
||||||
|
### 3.1 위험도 분류 및 전략
|
||||||
|
|
||||||
|
| 위험도 | 대상 | 문제 유형 | 전략 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **높음** — 단일 정본 preference (theme/TOS 동의 플래그 등 전역 상태 1개 값을 담음) | macOS `Library/Preferences/*.plist` (`com.google.antigravity*.plist`, `com.google.GeminiMacOS*.plist`), `agy`의 `~/.gemini/antigravity-ide`(설정 JSON), `claude`의 `settings.json` | 한 세션의 테마/설정 변경이 즉시 호스트 및 다른 모든 동시 세션의 동작을 바꿔버림 — 실사용자 관점의 명백한 버그 | **`ln -sfn` → `cp -a` (프로비저닝 시점 1회 복사, 대상이 root에 이미 없을 때만)**로 전환. 세션별 독립 사본을 갖되, 최초 부팅 상태는 호스트의 기시딩(온보딩 완료) 상태를 그대로 물려받음 |
|
||||||
|
| **중간** — append형 대용량/이력 디렉터리 | `~/.claude/session-env`, `sessions`, `cache`; `agy`의 `conversation_summaries.db`, `jetski_state.pbtxt` | 세션마다 별도 키(세션 ID 등)로 항목이 추가되는 구조로 보이며, 격리 없이도 실제 사용자가 한 머신에서 여러 터미널을 동시에 쓸 때 이미 공유되는 것과 동일한 패턴 | **현행 유지 (심링크)**. 단, 이는 "허용된 기존 동작과의 동등성"에 근거한 잠정 결론이며, 실제로 세션별 격리가 제품 요구사항인지는 4.3의 후속 논의 항목으로 남김 |
|
||||||
|
| **해당 없음(claude)/유효(agy)** — Keychain | `Library/Keychains` | `claude`: 원인 무관 죽은 코드 / `agy`: `HOME` 리디렉션으로 실제 유효 | **`claude` 분기에서 `Library/Keychains` 시딩 블록 제거**(dead code 정리). **`agy` 분기는 유지** — Keychain 자체는 원본 파일을 직접 열람 가능해야 잠금해제/ACL이 성립하므로 복사 대상에서 제외하고 심링크 유지가 맞음 |
|
||||||
|
| **낮음** — 순수 식별자/자격 증명 (거의 재기록되지 않고, 재기록 시 명시적 `/login` 흐름을 통해서만 발생) | `.claude.json`, `.credentials.json`, `.gemini/*` 의 `oauth_creds.json`/`installation_id`/`antigravity-oauth-token` 등 | 에이전트 프로세스 자체가 실행 중 이 파일을 능동적으로 재작성하는 경로가 없음(있다면 그것은 곧 재로그인이 필요하다는 신호이므로 오히려 격리가 무의미) | **현행 유지 (심링크)** |
|
||||||
|
|
||||||
|
### 3.2 갱신된 구현 표
|
||||||
|
|
||||||
|
| 대상 | 항목 | 방식 | 비고 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `claude` | `session-env`, `sessions`, `cache` | 심링크 유지 | 3.1 "중간" 위험도, 현행 유지 |
|
||||||
|
| `claude` | `settings.json` | **`cp -a` 1회 복사로 전환** | 3.1 "높음" |
|
||||||
|
| `claude` | `.credentials.json`, `.claude.json`, `plugins` | 심링크 유지 + `[ -e ... ]` 가드 추가(v1 그대로) | 3.1 "낮음" |
|
||||||
|
| `claude` | `Library/Keychains` | **시딩 블록 제거** | Challenge #2 반영, dead code |
|
||||||
|
| `agy` | `antigravity-ide` 설정 디렉터리, `Library/Preferences/*.plist`, `Library/Application Support/Antigravity*`, `com.google.GeminiMacOS*` | **`cp -a` 1회 복사로 전환** | 3.1 "높음" — TOS/테마 상태가 이 경로들에 있음 |
|
||||||
|
| `agy` | `.gemini/antigravity-cli/*`, `conversation_summaries.db`, `jetski_state.pbtxt` | 심링크 유지 | 3.1 "중간" |
|
||||||
|
| `agy` | `Library/Keychains` | 심링크 유지 | 3.1 "유효(agy)" |
|
||||||
|
| `agy` | `oauth_creds.json`, `google_accounts.json`, `installation_id` 등 자격 증명 | 심링크 유지 | 3.1 "낮음" |
|
||||||
|
| 공통 | `seeded` 누적 가드 | 모든 대입에 `${seeded:+$seeded,}` 일관 적용 | v1과 동일, 변경 없음 |
|
||||||
|
|
||||||
|
`cp -a`로 전환하는 항목은 반드시 **"대상이 `$root`에 이미 존재하지 않을 때만 복사"** 조건을 걸어, 동일 격리 root를 재사용하는 세션 재시작 시 이전 세션에서 쌓인 로컬 변경(테마 등)을 매번 덮어쓰지 않도록 한다 (`[ -e "$root/..." ] || cp -a "$HOME/..." "$root/..."`).
|
||||||
|
|
||||||
|
## 4. 검증 (Verification)
|
||||||
|
|
||||||
|
### 4.1 v1 검증 결과 재확인 (변경 없음)
|
||||||
|
- `bash -n .agents/skills/lib.sh` → 통과 (v1과 동일한 워킹 트리 diff, 변경 없음 확인: `git diff --stat` 여전히 `.agents/skills/lib.sh | 32 insertions(+), 10 deletions(-)`).
|
||||||
|
- `git status --short` → 추적 파일 변경은 `.agents/skills/lib.sh` 하나뿐 (미추적 `.tmp`/`.DS_Store`는 리뷰 대상 아님, v1과 동일).
|
||||||
|
- 실제 파일시스템 대조 결과(v1의 4.3)는 그대로 유효.
|
||||||
|
|
||||||
|
### 4.2 본 v2 계획과 "현재 워킹 트리 diff"의 관계
|
||||||
|
**중요**: 현재 워킹 트리에 반영된 diff는 v1 계획(심링크 전면 적용)과 일치하는 상태이며, **본 v2에서 새로 제안한 `cp -a` 전환 및 `claude` Keychain 제거는 아직 코드에 반영되어 있지 않다.** 즉 v2는 v1 diff에 대한 "PASS 재확인"이 아니라, **추가 구현이 필요한 차기 변경 제안**이다. 따라서 이번 라운드는 계획 문서 갱신에 한정하고, 코드 반영은 별도 Implementer 단계로 넘긴다 (본 Job의 역할은 Planner이며 "직접 코드를 수정하지 말라"는 종전 리뷰 라운드들의 제약과 일관되게, 이번에도 `.agents/skills/lib.sh`에 대한 실제 편집은 수행하지 않았다).
|
||||||
|
|
||||||
|
### 4.3 후속 논의가 필요한 열린 질문
|
||||||
|
- `session-env`/`sessions`/`cache`(그리고 `agy`의 `conversation_summaries.db` 등)를 "중간" 위험도로 분류해 현행 심링크를 유지하기로 했으나, 이는 "실제 제품 요구사항이 세션별 완전 격리인지, 아니면 호스트와의 이력 공유가 의도된 동작인지"에 대한 확인 없이 잠정 판단한 것이다. Creator/제품 오너 확인 후 필요시 이 항목도 3.1 "높음"으로 재분류해 `cp -a`로 전환해야 할 수 있다.
|
||||||
|
|
||||||
|
## 5. 결론
|
||||||
|
Creator의 Challenge 2건은 모두 코드/아키텍처 사실에 부합하는 타당한 지적으로 확인되어 계획에 반영했다: (1) mutable 단일-정본 preference/state에 대한 쓰기 격리 부재는 위험도 기반 `cp -a` 전환으로, (2) `claude`의 Keychain 시딩 무효성은 해당 블록 제거로 각각 대응한다. 현재 워킹 트리 diff는 여전히 v1 설계를 반영한 상태이며 문법/파일시스템 검증은 기존과 동일하게 통과하지만, 본 v2에서 제안한 `cp -a` 전환 및 Keychain 제거는 아직 미구현 상태로, 차기 Implementer 라운드에서 반영이 필요하다.
|
||||||
|
|
||||||
|
**Unanimous PASS (계획 문서 갱신 완료, 코드 반영은 후속 라운드 필요)**
|
||||||
|
|
||||||
|
[VERDICT: PASS]
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Code Review: `.agents/skills/lib.sh` — write-isolation redesign (cp -a) implementing refined plan
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
This diff (`f2e23c1..ea863c0`, 35 insertions/17 deletions) is the **cumulative** diff against the last commit — it folds together the original agy TOS-seeding work already reviewed three times (`b803c6e5`, `afd375ef`, `8f8b6d63`), *and* implements the write-isolation redesign proposed in the Planner's refined plan (Job `fd3ba323`), which itself responded to a Creator challenge about two issues:
|
||||||
|
1. Symlinking mutable single-source-of-truth preference/state files causes cross-session/host state corruption.
|
||||||
|
2. `claude`'s `Library/Keychains` symlink was dead code, since `claude`'s isolation lever only redirects `CLAUDE_CONFIG_DIR`, never `HOME`.
|
||||||
|
|
||||||
|
`git diff -- .agents/skills/lib.sh` matches this brief byte-for-byte (confirmed blob hashes: HEAD `f2e23c1`, working tree `ea863c0`, matching the diff's `index` line exactly).
|
||||||
|
|
||||||
|
## "No other files changed" check
|
||||||
|
`git status --short`:
|
||||||
|
- `M .agents/skills/lib.sh` — the only tracked-file modification.
|
||||||
|
- `?? .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job.<pid>_<n>.tmp`, `?? .DS_Store` — untracked, non-code artifacts (same benign job-runner scratch pattern observed in every prior review pass; `.DS_Store` is a macOS Finder metadata file, not a code change).
|
||||||
|
|
||||||
|
Confirmed: **no files other than `.agents/skills/lib.sh` contain reviewable changes.**
|
||||||
|
|
||||||
|
## Verification performed
|
||||||
|
- `bash -n .agents/skills/lib.sh` — syntax OK.
|
||||||
|
- Read the full resulting `claude` and `agy` case-arms post-diff to confirm final state, not just the patch in isolation.
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
### 1. `claude`'s `Library/Keychains` symlink block was fully removed — correct
|
||||||
|
The diff's `claude` arm hunk (`@@ -1184,9 +1184,15 @@`) diffs against the pre-`b2e3ec97` baseline, which never had a `Library/Keychains` block for `claude` in committed history — that block only ever existed in the interim uncommitted state reviewed under `b2e3ec97`/`fd3ba323`. Reading the current file directly (lines 1185-1196) confirms the `claude` arm now ends after `cache` seeding with no Darwin/Keychains block at all. This is exactly the fix recommended in the `fd3ba323` refined plan: `claude`'s lever (`isolation_lever()` → `claude_config_dir`) never redirects `HOME`, so `security`/Keychain Services lookups by a spawned `claude` process always resolve against the real `$HOME/Library/Keychains` regardless of `$root` — the removed block was inert. Its removal is a correct dead-code cleanup, not a functional regression (it never had a documented behavior originally).
|
||||||
|
|
||||||
|
### 2. `cp -a` write-isolation conversion — correctly scoped and idempotent
|
||||||
|
Nine call sites were converted from `ln -sfn` to a guarded `cp -a`:
|
||||||
|
- `claude`: `settings.json`
|
||||||
|
- `agy`: `.gemini/antigravity-ide`, three `Library/Preferences/$plist` entries, `Library/Application Support/{Antigravity, Antigravity IDE, com.google.GeminiMacOS}`, and the four XDG config/data variants.
|
||||||
|
|
||||||
|
Each follows the pattern `if [ ! -e/-d/-f "$root/<target>" ]; then cp -a "$HOME/<source>" "$root/<target>"; fi`, which is correctly idempotent: a `root` that was already provisioned (e.g., across a session restart reusing the same isolation directory) will **not** be re-copied, preserving any local mutations (theme changes, TOS-ack state) made during a prior isolated session's lifetime rather than clobbering them with the host's current state on every restart. This matches the refined plan's explicit requirement (`fd3ba323` §3.2: "대상이 `$root`에 이미 존재하지 않을 때만 복사"). `seeded` is still recorded unconditionally whenever the source exists, independent of whether the copy actually ran this time — correct, since the purpose of `seeded` is to report availability, not to log a fresh-copy event.
|
||||||
|
|
||||||
|
`cp -a` is valid on both BSD/macOS and GNU coreutils `cp`, recursively copies directories, and preserves symlinks-within (does not dereference), so nested references inside a copied tree (if any) remain intact and point at their original absolute targets — no unintended dereferencing side effects.
|
||||||
|
|
||||||
|
### 3. Risk-tiering matches the plan; remaining symlinked items are consistent with the "medium/low risk" bucket
|
||||||
|
Items still using `ln -sfn` after this diff — `claude`'s `session-env`/`sessions`/`cache`/`plugins`/`.credentials.json`/`.claude.json`; `agy`'s `.gemini/*` credential files, `.gemini/antigravity`, `.gemini/config`, `Library/Keychains` (agy only — correctly retained since `agy`'s lever is `home` and Keychain access there is real), and `Library/Group Containers` — all correspond to the plan's "medium risk" (append-style history/cache, treated as parity with normal multi-terminal shared-machine behavior) or "low risk" (credential identifiers not rewritten by the running process itself) tiers. No high-risk single-source preference/theme file was left as a live symlink; no low/medium-risk item was unnecessarily converted to `cp -a`. The classification from the accepted plan was applied consistently.
|
||||||
|
|
||||||
|
### 4. Minor, non-blocking observation: `Library/Application Support` `mkdir -p` hoisted outside the per-target `if` blocks
|
||||||
|
The parent `mkdir -p "$root/Library/Application Support"` now runs unconditionally before the three Antigravity/Antigravity-IDE/GeminiMacOS checks, rather than only inside the first `if` block as before. This creates an empty `Library/Application Support` directory even when none of the three source directories exist on the host. Harmless (idempotent `mkdir -p`, no functional impact), and arguably cleaner since the same parent is now shared by three sibling `if` blocks instead of being created redundantly inside just one of them.
|
||||||
|
|
||||||
|
### 5. Known, plan-acknowledged open question (not a defect in this diff)
|
||||||
|
The `fd3ba323` plan explicitly flagged as an open item whether "medium risk" append-style dirs (`session-env`, `sessions`, `cache`, `conversation_summaries.db`, etc.) should eventually also move to `cp -a` once product intent on cross-session history sharing is confirmed. This diff does not resolve that question — appropriately, since it wasn't in scope for the accepted plan's first implementation pass. Not counted against this diff.
|
||||||
|
|
||||||
|
No lint tool (shellcheck) is available in this environment; manual read-through found no quoting, unbound-variable, or subshell issues. All space-containing paths (`"Library/Application Support"`, `"Application Support/Antigravity IDE"`) remain correctly double-quoted throughout the new `cp -a` call sites.
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
This diff is a faithful, correctly-scoped implementation of the accepted `fd3ba323` refined plan: it removes the dead `claude` Keychain symlink, converts every identified high-risk single-source preference/theme/TOS file or directory to a guarded, idempotent one-time `cp -a`, and leaves append-style/low-mutation items on the existing symlink strategy per the plan's risk tiering. Syntax is valid, no other files were touched, and the one cosmetic observation (hoisted `mkdir -p`) does not affect behavior.
|
||||||
|
|
||||||
|
[VERDICT: PASS]
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# Code Review: `.agents/skills/lib.sh` — claude `/login` + agy TOS/theme seeding fix (cp -a migration)
|
||||||
|
|
||||||
|
**Job ID**: aab82a5b
|
||||||
|
**Reviewer**: cline
|
||||||
|
**Diff reviewed**: `f2e23c1..ea863c0` (working tree, `git diff HEAD`)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This diff (`git diff HEAD -- .agents/skills/lib.sh`) modifies only the `provision_isolation()` function's `claude)` and `agy)` case arms. It is the next iteration of the agy TOS-seeding fix (commit `6692c27`) and adds a parallel fix for the claude `/login` prompt. Two themes of change:
|
||||||
|
|
||||||
|
1. **claude arm** — add seeding for `~/.claude/session-env`, `sessions`, `cache`; make `.credentials.json` symlink guarded by an existence check; switch `settings.json` from a symlink to an idempotent `cp -a` copy (so the isolated agent can write its own settings without mutating the host file).
|
||||||
|
2. **agy arm** — extend the `antigravity-cli` file list (`conversation_summaries.db`, `jetski_state.pbtxt`); add `~/.gemini/antigravity-ide` (cp -a); add `com.google.antigravity-ide.plist` + `com.google.GeminiMacOS.launcher.plist` to the plist loop; switch all Preferences plists, `Application Support/Antigravity`, and the new `Application Support/Antigravity IDE` + `com.google.GeminiMacOS` from symlinks to idempotent `cp -a` copies; hoist `mkdir -p "$root/Library/Application Support"` before the conditional blocks; add the Linux XDG `cp -a` migration; fix the carried-forward `seeded="$seeded,..."` guard inconsistency to `${seeded:+$seeded,}` everywhere.
|
||||||
|
|
||||||
|
## "No other files changed" check
|
||||||
|
|
||||||
|
`git status --short`:
|
||||||
|
- `M .agents/skills/lib.sh` — the only tracked-file modification.
|
||||||
|
- `?? .DS_Store` — macOS Finder metadata, untracked, not a code change (pre-existing, not created by this diff).
|
||||||
|
- `?? .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job.<pid>_<n>.tmp` — untracked, transient atomic-write temp copy of the job-runner orchestrator script (same artifact observed in prior review passes), not a code change.
|
||||||
|
|
||||||
|
**Confirmed: no files other than `.agents/skills/lib.sh` contain reviewable changes.**
|
||||||
|
|
||||||
|
## Lint / Syntax
|
||||||
|
|
||||||
|
- `bash -n .agents/skills/lib.sh` → **SYNTAX OK**.
|
||||||
|
- `shellcheck` not installed; manual review found no quoting, unbound-variable, or word-splitting issues in the new lines.
|
||||||
|
- The shebang is `#!/usr/bin/env bash`; no new bash-specific constructs beyond what the file already uses.
|
||||||
|
|
||||||
|
## Hunk 1 — `claude` arm
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- `.credentials.json`: was unconditional `ln -sfn` (would fail/produce dangling link if source absent); now guarded `if [ -e ... ]` then `ln -sfn`. Correctness improvement.
|
||||||
|
- `settings.json`: was `ln -sfn` (host-mutating risk if isolated claude writes settings); now `if [ ! -e "$root/settings.json" ]; then cp -a ...; fi` — idempotent physical copy. The isolated agent can now write its own settings without mutating the host's `~/.claude/settings.json`.
|
||||||
|
- `plugins`, `session-env`, `sessions`, `cache`: new guarded `ln -sfn` symlinks. All use the `${seeded:+$seeded,}` guard consistently.
|
||||||
|
- The two pre-existing `seeded="$seeded,..."` lines (old `settings.json`, `plugins`) are converted to `${seeded:+$seeded,}` — this **fixes the carried-forward cosmetic nit** from prior reviews (leading-comma risk in the seeded log string).
|
||||||
|
|
||||||
|
### Correctness & scoping
|
||||||
|
- Correctly scoped to the `claude)` arm. `claude` uses the `claude_config_dir` isolation lever (`isolation_env_prefix` → `CLAUDE_CONFIG_DIR=<root>`), so claude reads config from `$root` directly — the new `session-env`, `sessions`, `cache` symlinks at `$root/...` are the right target.
|
||||||
|
- All new source paths guarded with `[ -d ... ]` / `[ -e ... ]` before linking; `settings.json` copy guarded with `[ ! -e "$root/settings.json" ]` for idempotency.
|
||||||
|
|
||||||
|
### Live path verification (this machine = macOS Darwin)
|
||||||
|
| Path | Exists? |
|
||||||
|
|---|---|
|
||||||
|
| `~/.claude.json` | ✅ |
|
||||||
|
| `~/.claude/.credentials.json` | absent (so the new guard correctly skips it — old code would have created a dangling symlink) |
|
||||||
|
| `~/.claude/settings.json` | ✅ |
|
||||||
|
| `~/.claude/plugins` | ✅ |
|
||||||
|
| `~/.claude/session-env` | ✅ |
|
||||||
|
| `~/.claude/sessions` | ✅ |
|
||||||
|
| `~/.claude/cache` | ✅ |
|
||||||
|
|
||||||
|
### Functional smoke test
|
||||||
|
Ran `provision_isolation claude <tmp_root>` against live state. Result:
|
||||||
|
- rc=0.
|
||||||
|
- Seeded list: `.claude.json,settings.json,plugins,session-env,sessions,cache` (note: no `.credentials.json` because source is absent — guard works; no leading comma).
|
||||||
|
- `$root/settings.json` is a **regular file** (`-rw-------`, cp -a copy), not a symlink — as intended.
|
||||||
|
- `$root/.claude.json`, `plugins`, `session-env`, `sessions`, `cache` are correct symlinks.
|
||||||
|
- Idempotency: re-running against the same root returns rc=0; `settings.json` is not re-copied (guard works).
|
||||||
|
|
||||||
|
### Existing test impact
|
||||||
|
The only `provision_isolation` test, `test_comp_create_isolation_folder_setup` (tests/test_tier2_component.py:99-116), asserts `cred_sym.is_symlink()` for `.credentials.json`. The test's `mam_sandbox` fixture (conftest.py:33) sets `HOME=tmp_path`, and the test creates `tmp_path/.claude/.credentials.json`, so the new `if [ -e ... ]` guard passes and the symlink is still created — the test's `is_symlink()` assertion still holds. The test does not assert on `settings.json`, so the symlink→cp change is invisible to it. **No test breakage.** (Note: the test suite cannot be executed here — pytest is not installed and conftest hardcodes a Linux `src_skills` path `/home/godopu16/...` — but the logic analysis confirms no regression.)
|
||||||
|
|
||||||
|
## Hunk 2 — `agy` arm
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- `antigravity-cli` file list: added `conversation_summaries.db`, `jetski_state.pbtxt` (both exist on this machine). Additive, guarded by `[ -e ... ]`.
|
||||||
|
- `~/.gemini/antigravity-ide`: new block, `cp -a` (not symlink) with `[ ! -d ... ]` idempotency guard. Physical copy so the isolated IDE can write its own state.
|
||||||
|
- Plist loop: added `com.google.antigravity-ide.plist`, `com.google.GeminiMacOS.launcher.plist`; switched all plists from `ln -sfn` to idempotent `cp -a`; converted `seeded="$seeded,..."` → `${seeded:+$seeded,}`.
|
||||||
|
- `mkdir -p "$root/Library/Application Support"` hoisted before the conditional blocks (was inside each `if`), so the new `Antigravity IDE` and `com.google.GeminiMacOS` blocks can copy without each repeating the mkdir.
|
||||||
|
- `Application Support/Antigravity`: symlink → idempotent `cp -a`.
|
||||||
|
- `Application Support/Antigravity IDE`: new block, `cp -a`.
|
||||||
|
- `Application Support/com.google.GeminiMacOS`: new block, `cp -a`.
|
||||||
|
- `Group Containers/group.com.google.gemini`: kept as symlink (shared live IPC container), only the `seeded` guard was fixed to `${seeded:+$seeded,}`.
|
||||||
|
- Linux XDG branch: all four `ln -sfn` → idempotent `cp -a` with `[ ! -d ... ]` guards; comment updated to note write isolation.
|
||||||
|
|
||||||
|
### Correctness & scoping
|
||||||
|
- Correctly scoped to the `agy)` arm; `agy` uses the `home` isolation lever, so `$root/.gemini/...` and `$root/Library/...` are the right targets.
|
||||||
|
- The `cp -a` migration is the right call for dirs the isolated agent will **write to** (TOS acceptance, theme selection, conversation history) — a symlink would funnel those writes back to the host, defeating isolation and potentially corrupting the host's Antigravity state. Keychains and Group Containers stay symlinked because those are read-only credential/IPC lookups that must stay live.
|
||||||
|
- All new source paths guarded; all `cp -a` targets guarded with `[ ! -d/-f/-e ... ]` for idempotency.
|
||||||
|
- The hoisted `mkdir -p "$root/Library/Application Support"` is safe — `mkdir -p` is a no-op if the dir already exists.
|
||||||
|
|
||||||
|
### Live path verification (this machine = macOS Darwin)
|
||||||
|
All newly-referenced source paths exist:
|
||||||
|
| Path | Exists? |
|
||||||
|
|---|---|
|
||||||
|
| `~/.gemini/antigravity-ide` | ✅ dir |
|
||||||
|
| `~/.gemini/antigravity-cli/conversation_summaries.db` | ✅ |
|
||||||
|
| `~/.gemini/antigravity-cli/jetski_state.pbtxt` | ✅ |
|
||||||
|
| `~/Library/Preferences/com.google.antigravity-ide.plist` | ✅ |
|
||||||
|
| `~/Library/Preferences/com.google.GeminiMacOS.launcher.plist` | ✅ |
|
||||||
|
| `~/Library/Application Support/Antigravity IDE` | ✅ dir |
|
||||||
|
| `~/Library/Application Support/com.google.GeminiMacOS` | ✅ dir |
|
||||||
|
|
||||||
|
### Functional smoke test
|
||||||
|
Ran `provision_isolation agy <tmp_root>` against live state. Result:
|
||||||
|
- rc=0.
|
||||||
|
- Seeded list (18 entries): `.gemini/antigravity-cli/{antigravity-oauth-token,installation_id,settings.json,conversation_summaries.db,jetski_state.pbtxt}`, `.gemini/antigravity`, `.gemini/antigravity-ide`, `.gemini/config`, `Library/Keychains`, `Library/Preferences/{com.google.antigravity.plist,com.google.antigravity-ide.plist,com.google.GeminiMacOS.plist,com.google.GeminiMacOS.shareddata.plist,com.google.GeminiMacOS.launcher.plist}`, `Library/Application Support/{Antigravity,Antigravity IDE,com.google.GeminiMacOS}`, `Library/Group Containers/group.com.google.gemini`. No leading comma.
|
||||||
|
- `$root/.gemini/antigravity-ide`, `$root/Library/Application Support/Antigravity`, `Antigravity IDE`, `com.google.GeminiMacOS` are **physical directory copies** (not symlinks) — as intended.
|
||||||
|
- `$root/.gemini/antigravity`, `.gemini/config`, `Library/Keychains`, `Group Containers/...` remain symlinks — as intended.
|
||||||
|
- Idempotency: re-running against the same root returns rc=0; no re-copy.
|
||||||
|
|
||||||
|
### `cp -a` socket warning (benign, expected)
|
||||||
|
`~/Library/Application Support/Antigravity IDE/1.10-main.sock` is a Unix socket (live IDE IPC handle). `cp -a` **skips sockets by design**, prints `cp: ... is a socket (not copied).` to stderr, and **returns exit code 0** — verified by reproducing with a synthetic socket. The dir copy still contains every regular file/subdir. This is harmless: the socket is a transient runtime handle the isolated agy/IDE would recreate on its own; copying it would be meaningless. The function returns 0 and all real config/data is seeded. Not a defect.
|
||||||
|
|
||||||
|
## Regression / loss check
|
||||||
|
- **No functionality removed.** The claude `.credentials.json` change is a strict improvement (guard prevents dangling symlinks). The `settings.json` symlink→cp and agy symlink→cp migrations are intentional behavior changes that improve write isolation — the isolated agent can now write its own TOS/theme/settings state without mutating the host.
|
||||||
|
- The prior-review cosmetic nit (Darwin `seeded="$seeded,..."` missing the `${seeded:+$seeded,}` guard) is **fully resolved** — every `seeded` assignment in both arms now uses the guard.
|
||||||
|
- The `cp -a` idempotency guards (`[ ! -e/-d/-f "$root/..." ]`) make re-provisioning to the same root safe (verified: re-run returns rc=0, no re-copy, no error).
|
||||||
|
- No imports/variables orphaned by these changes.
|
||||||
|
- **Pre-existing test-coverage gap (not introduced by this diff):** no test exercises the `agy` arm of `provision_isolation`, and the claude-arm test does not cover the new `session-env`/`sessions`/`cache` symlinks or the `settings.json` cp behavior. Flagging for awareness, not a blocker — adding agy-arm coverage would be a worthwhile follow-up but is out of scope for this review.
|
||||||
|
|
||||||
|
## Design assessment
|
||||||
|
This is the right level of change — a targeted bug fix, not a redesign:
|
||||||
|
- The root-cause analysis (claude prompts `/login` because session-env/sessions/cache weren't seeded; agy prompts TOS/theme because writable state dirs were symlinks back to host) is addressed at the correct layer (the seeding function), not by patching around the prompts downstream.
|
||||||
|
- The symlink-vs-copy distinction is applied correctly: read-only credential/IPC lookups (Keychains, Group Containers, `.gemini/antigravity`, `.gemini/config`) stay symlinked; writable state dirs (settings, Application Support, antigravity-ide, XDG dirs, plists) become copies. This matches the isolation intent.
|
||||||
|
- No re-planning/rework needed.
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
Both hunks are correctly scoped (only `.agents/skills/lib.sh`, only `provision_isolation`), syntactically valid (`bash -n` clean), and functionally verified against live machine state with smoke tests (rc=0, correct symlink/copy layout, idempotent re-runs). The diff fixes the carried-forward `seeded` guard nit, improves the claude `.credentials.json` guard, and migrates writable state dirs from host-mutating symlinks to isolated `cp -a` copies — directly addressing the stated root causes for both the claude `/login` and agy TOS/theme prompts. The only stderr noise (`cp -a` socket-skip warning) is benign, expected `cp` behavior with exit code 0. No regressions, no loss, no test breakage. No escalation needed.
|
||||||
|
|
||||||
|
[VERDICT: PASS]
|
||||||
+35
-17
@@ -1184,9 +1184,15 @@ provision_isolation() {
|
|||||||
case "$agent" in
|
case "$agent" in
|
||||||
claude)
|
claude)
|
||||||
if [ -e "$HOME/.claude.json" ]; then ln -sfn "$HOME/.claude.json" "$root/.claude.json"; seeded=".claude.json"; fi
|
if [ -e "$HOME/.claude.json" ]; then ln -sfn "$HOME/.claude.json" "$root/.claude.json"; seeded=".claude.json"; fi
|
||||||
ln -sfn "$HOME/.claude/.credentials.json" "$root/.credentials.json"; seeded="${seeded:+$seeded,}.credentials.json"
|
if [ -e "$HOME/.claude/.credentials.json" ]; then ln -sfn "$HOME/.claude/.credentials.json" "$root/.credentials.json"; seeded="${seeded:+$seeded,}.credentials.json"; fi
|
||||||
if [ -e "$HOME/.claude/settings.json" ]; then ln -sfn "$HOME/.claude/settings.json" "$root/settings.json"; seeded="$seeded,settings.json"; fi
|
if [ -e "$HOME/.claude/settings.json" ]; then
|
||||||
if [ -d "$HOME/.claude/plugins" ]; then ln -sfn "$HOME/.claude/plugins" "$root/plugins"; seeded="$seeded,plugins"; fi
|
if [ ! -e "$root/settings.json" ]; then cp -a "$HOME/.claude/settings.json" "$root/settings.json"; fi
|
||||||
|
seeded="${seeded:+$seeded,}settings.json"
|
||||||
|
fi
|
||||||
|
if [ -d "$HOME/.claude/plugins" ]; then ln -sfn "$HOME/.claude/plugins" "$root/plugins"; seeded="${seeded:+$seeded,}plugins"; fi
|
||||||
|
if [ -d "$HOME/.claude/session-env" ]; then ln -sfn "$HOME/.claude/session-env" "$root/session-env"; seeded="${seeded:+$seeded,}session-env"; fi
|
||||||
|
if [ -d "$HOME/.claude/sessions" ]; then ln -sfn "$HOME/.claude/sessions" "$root/sessions"; seeded="${seeded:+$seeded,}sessions"; fi
|
||||||
|
if [ -d "$HOME/.claude/cache" ]; then ln -sfn "$HOME/.claude/cache" "$root/cache"; seeded="${seeded:+$seeded,}cache"; fi
|
||||||
;;
|
;;
|
||||||
cline)
|
cline)
|
||||||
# CLI 가 --data-dir <root> 를 읽을 때 최상위 루트 하위에서 설정을 찾으므로 다이렉트 맵핑
|
# CLI 가 --data-dir <root> 를 읽을 때 최상위 루트 하위에서 설정을 찾으므로 다이렉트 맵핑
|
||||||
@@ -1214,19 +1220,23 @@ provision_isolation() {
|
|||||||
for f in oauth_creds.json google_accounts.json installation_id settings.json state.json; do
|
for f in oauth_creds.json google_accounts.json installation_id settings.json state.json; do
|
||||||
if [ -e "$HOME/.gemini/$f" ]; then ln -sfn "$HOME/.gemini/$f" "$root/.gemini/$f"; seeded="${seeded:+$seeded,}.gemini/$f"; fi
|
if [ -e "$HOME/.gemini/$f" ]; then ln -sfn "$HOME/.gemini/$f" "$root/.gemini/$f"; seeded="${seeded:+$seeded,}.gemini/$f"; fi
|
||||||
done
|
done
|
||||||
for f in antigravity-oauth-token installation_id settings.json; do
|
for f in antigravity-oauth-token installation_id settings.json conversation_summaries.db jetski_state.pbtxt; do
|
||||||
if [ -e "$HOME/.gemini/antigravity-cli/$f" ]; then ln -sfn "$HOME/.gemini/antigravity-cli/$f" "$root/.gemini/antigravity-cli/$f"; seeded="${seeded:+$seeded,}.gemini/antigravity-cli/$f"; fi
|
if [ -e "$HOME/.gemini/antigravity-cli/$f" ]; then ln -sfn "$HOME/.gemini/antigravity-cli/$f" "$root/.gemini/antigravity-cli/$f"; seeded="${seeded:+$seeded,}.gemini/antigravity-cli/$f"; fi
|
||||||
done
|
done
|
||||||
if [ -d "$HOME/.gemini/antigravity" ]; then
|
if [ -d "$HOME/.gemini/antigravity" ]; then
|
||||||
ln -sfn "$HOME/.gemini/antigravity" "$root/.gemini/antigravity"
|
ln -sfn "$HOME/.gemini/antigravity" "$root/.gemini/antigravity"
|
||||||
seeded="${seeded:+$seeded,}.gemini/antigravity"
|
seeded="${seeded:+$seeded,}.gemini/antigravity"
|
||||||
fi
|
fi
|
||||||
|
if [ -d "$HOME/.gemini/antigravity-ide" ]; then
|
||||||
|
if [ ! -d "$root/.gemini/antigravity-ide" ]; then cp -a "$HOME/.gemini/antigravity-ide" "$root/.gemini/antigravity-ide"; fi
|
||||||
|
seeded="${seeded:+$seeded,}.gemini/antigravity-ide"
|
||||||
|
fi
|
||||||
if [ -d "$HOME/.gemini/config" ]; then
|
if [ -d "$HOME/.gemini/config" ]; then
|
||||||
ln -sfn "$HOME/.gemini/config" "$root/.gemini/config"
|
ln -sfn "$HOME/.gemini/config" "$root/.gemini/config"
|
||||||
seeded="${seeded:+$seeded,}.gemini/config"
|
seeded="${seeded:+$seeded,}.gemini/config"
|
||||||
fi
|
fi
|
||||||
# On macOS, seed ~/Library/Keychains to allow isolated agy to query Keychain Access credentials
|
# On macOS, seed ~/Library/Keychains to allow isolated agy to query Keychain Access credentials
|
||||||
# Also seed Preferences and Application Support for Antigravity settings/TOS
|
# Also seed Preferences and Application Support for Antigravity settings/TOS/Theme (using cp -a for write isolation)
|
||||||
if [ "$(uname)" = "Darwin" ]; then
|
if [ "$(uname)" = "Darwin" ]; then
|
||||||
mkdir -p "$root/Library"
|
mkdir -p "$root/Library"
|
||||||
if [ -d "$HOME/Library/Keychains" ]; then
|
if [ -d "$HOME/Library/Keychains" ]; then
|
||||||
@@ -1234,42 +1244,50 @@ provision_isolation() {
|
|||||||
seeded="${seeded:+$seeded,}Library/Keychains"
|
seeded="${seeded:+$seeded,}Library/Keychains"
|
||||||
fi
|
fi
|
||||||
mkdir -p "$root/Library/Preferences"
|
mkdir -p "$root/Library/Preferences"
|
||||||
for plist in com.google.antigravity.plist com.google.GeminiMacOS.plist com.google.GeminiMacOS.shareddata.plist; do
|
for plist in com.google.antigravity.plist com.google.antigravity-ide.plist com.google.GeminiMacOS.plist com.google.GeminiMacOS.shareddata.plist com.google.GeminiMacOS.launcher.plist; do
|
||||||
if [ -f "$HOME/Library/Preferences/$plist" ]; then
|
if [ -f "$HOME/Library/Preferences/$plist" ]; then
|
||||||
ln -sfn "$HOME/Library/Preferences/$plist" "$root/Library/Preferences/$plist"
|
if [ ! -f "$root/Library/Preferences/$plist" ]; then cp -a "$HOME/Library/Preferences/$plist" "$root/Library/Preferences/$plist"; fi
|
||||||
seeded="$seeded,Library/Preferences/$plist"
|
seeded="${seeded:+$seeded,}Library/Preferences/$plist"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
mkdir -p "$root/Library/Application Support"
|
||||||
if [ -d "$HOME/Library/Application Support/Antigravity" ]; then
|
if [ -d "$HOME/Library/Application Support/Antigravity" ]; then
|
||||||
mkdir -p "$root/Library/Application Support"
|
if [ ! -d "$root/Library/Application Support/Antigravity" ]; then cp -a "$HOME/Library/Application Support/Antigravity" "$root/Library/Application Support/Antigravity"; fi
|
||||||
ln -sfn "$HOME/Library/Application Support/Antigravity" "$root/Library/Application Support/Antigravity"
|
seeded="${seeded:+$seeded,}Library/Application Support/Antigravity"
|
||||||
seeded="$seeded,Library/Application Support/Antigravity"
|
fi
|
||||||
|
if [ -d "$HOME/Library/Application Support/Antigravity IDE" ]; then
|
||||||
|
if [ ! -d "$root/Library/Application Support/Antigravity IDE" ]; then cp -a "$HOME/Library/Application Support/Antigravity IDE" "$root/Library/Application Support/Antigravity IDE"; fi
|
||||||
|
seeded="${seeded:+$seeded,}Library/Application Support/Antigravity IDE"
|
||||||
|
fi
|
||||||
|
if [ -d "$HOME/Library/Application Support/com.google.GeminiMacOS" ]; then
|
||||||
|
if [ ! -d "$root/Library/Application Support/com.google.GeminiMacOS" ]; then cp -a "$HOME/Library/Application Support/com.google.GeminiMacOS" "$root/Library/Application Support/com.google.GeminiMacOS"; fi
|
||||||
|
seeded="${seeded:+$seeded,}Library/Application Support/com.google.GeminiMacOS"
|
||||||
fi
|
fi
|
||||||
if [ -d "$HOME/Library/Group Containers/group.com.google.gemini" ]; then
|
if [ -d "$HOME/Library/Group Containers/group.com.google.gemini" ]; then
|
||||||
mkdir -p "$root/Library/Group Containers"
|
mkdir -p "$root/Library/Group Containers"
|
||||||
ln -sfn "$HOME/Library/Group Containers/group.com.google.gemini" "$root/Library/Group Containers/group.com.google.gemini"
|
ln -sfn "$HOME/Library/Group Containers/group.com.google.gemini" "$root/Library/Group Containers/group.com.google.gemini"
|
||||||
seeded="$seeded,Library/Group Containers/group.com.google.gemini"
|
seeded="${seeded:+$seeded,}Library/Group Containers/group.com.google.gemini"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# On Linux/Unix, seed XDG config and local data dirs for Antigravity settings/TOS
|
# On Linux/Unix, seed XDG config and local data dirs for Antigravity settings/TOS (using cp -a for write isolation)
|
||||||
local xdg_config="${XDG_CONFIG_HOME:-$HOME/.config}"
|
local xdg_config="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||||
local xdg_data="${XDG_DATA_HOME:-$HOME/.local/share}"
|
local xdg_data="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||||
if [ -d "$xdg_config/Antigravity" ]; then
|
if [ -d "$xdg_config/Antigravity" ]; then
|
||||||
mkdir -p "$root/.config"
|
mkdir -p "$root/.config"
|
||||||
ln -sfn "$xdg_config/Antigravity" "$root/.config/Antigravity"
|
if [ ! -d "$root/.config/Antigravity" ]; then cp -a "$xdg_config/Antigravity" "$root/.config/Antigravity"; fi
|
||||||
seeded="${seeded:+$seeded,}.config/Antigravity"
|
seeded="${seeded:+$seeded,}.config/Antigravity"
|
||||||
elif [ -d "$xdg_config/antigravity" ]; then
|
elif [ -d "$xdg_config/antigravity" ]; then
|
||||||
mkdir -p "$root/.config"
|
mkdir -p "$root/.config"
|
||||||
ln -sfn "$xdg_config/antigravity" "$root/.config/antigravity"
|
if [ ! -d "$root/.config/antigravity" ]; then cp -a "$xdg_config/antigravity" "$root/.config/antigravity"; fi
|
||||||
seeded="${seeded:+$seeded,}.config/antigravity"
|
seeded="${seeded:+$seeded,}.config/antigravity"
|
||||||
fi
|
fi
|
||||||
if [ -d "$xdg_data/Antigravity" ]; then
|
if [ -d "$xdg_data/Antigravity" ]; then
|
||||||
mkdir -p "$root/.local/share"
|
mkdir -p "$root/.local/share"
|
||||||
ln -sfn "$xdg_data/Antigravity" "$root/.local/share/Antigravity"
|
if [ ! -d "$root/.local/share/Antigravity" ]; then cp -a "$xdg_data/Antigravity" "$root/.local/share/Antigravity"; fi
|
||||||
seeded="${seeded:+$seeded,}.local/share/Antigravity"
|
seeded="${seeded:+$seeded,}.local/share/Antigravity"
|
||||||
elif [ -d "$xdg_data/antigravity" ]; then
|
elif [ -d "$xdg_data/antigravity" ]; then
|
||||||
mkdir -p "$root/.local/share"
|
mkdir -p "$root/.local/share"
|
||||||
ln -sfn "$xdg_data/antigravity" "$root/.local/share/antigravity"
|
if [ ! -d "$root/.local/share/antigravity" ]; then cp -a "$xdg_data/antigravity" "$root/.local/share/antigravity"; fi
|
||||||
seeded="${seeded:+$seeded,}.local/share/antigravity"
|
seeded="${seeded:+$seeded,}.local/share/antigravity"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user