diff --git a/HANDOFF.md b/HANDOFF.md index c22d907..fb653c7 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -34,6 +34,7 @@ P0~P5 직렬. P6~P9 P5 이후 병렬 가능. - **2026-05-07 fb-26 (progress.rs)** — `Aborted` unconditional writeln (TTY duplicate) + `Completed` TTY no summary fixed; `KEBAB_PROGRESS=plain` env + quiet suppression added - **2026-05-07 fb-28 (main.rs)** — `--readonly` (KEBAB_READONLY) blocks Ingest/IngestFile/IngestStdin/Reset; `--quiet` suppresses progress stderr; error.v1 code: "readonly_mode" +- **2026-05-07 macOS XDG path collision (config 사라지는 버그)** — `dirs` crate 가 macOS 에서 `config_dir()` 과 `data_dir()` 둘 다 `~/Library/Application Support/` 반환 → `reset --data-only` 가 config 파일까지 삭제. Fix: `~/.config`, `~/.local/share`, `~/.cache` 직접 사용. 새 경로: config `~/.config/kebab/`, data `~/.local/share/kebab/`, cache `~/.cache/kebab/`. `Config::load(None)` 이 macOS legacy path 에서 자동 마이그레이션. 자세한 내용: `tasks/HOTFIXES.md`. - **2026-05-07 P9 post-도그푸딩 (p9-fb-31)** — `kebab ingest-file ` + `kebab ingest-stdin --title ` 두 신규 subcommand + MCP tool `ingest_file` / `ingest_stdin` (4 → 6 tool). agent 가 fetch 한 web markdown / 외부 file 을 KB 에 즉시 저장. workspace 외부 file 은 `/_external/.` 로 copy (deterministic 명명 → idempotent). `_external/` 디렉토리 첫 생성 시 `.kebabignore` 자동 append (walk 무한 루프 방지). stdin 은 markdown 전용 + flag (`--title`, `--source-uri`) → frontmatter 자동 prepend. .kebabignore 매치 시 stderr warn 후 진행 (explicit ingest = bypass intent). fb-30 의 v1 read-only MCP 정책 변경 — 첫 mutation tool 도입. spec: `tasks/p9/p9-fb-31-single-file-stdin-ingest.md`. design: `docs/superpowers/specs/2026-05-07-p9-fb-31-single-file-stdin-ingest-design.md`. - **2026-05-07 P9 post-도그푸딩 (p9-fb-30)** — `kebab mcp` 신규 subcommand + new crate `kebab-mcp` (lib only) — stdio JSON-RPC server. 4 read-only tool (`search` / `ask` / `schema` / `doctor`) 가 `kebab-app` facade 위에 build. rmcp 1.6 SDK 채택, manual `tools/list` + `tools/call` dispatch (rmcp 의 `#[tool_router]` 매크로 대신). `error_classify` 모듈을 `kebab-cli` → `kebab-app::error_wire` 로 promotion (UI crate 끼리 import 회피, facade 룰 준수). `ErrorV1` 에 `schema_version: String` 필드 추가 — kebab-mcp 의 직접 serialize 경로에서도 wire 정합. `KebabAppState` 가 `(Config, Option)` carry — doctor tool 의 path-aware behavior 위해. ask + search arm 의 `tokio::task::spawn_blocking` wrap — `OllamaLanguageModel` 의 reqwest blocking client 가 async 안에서 panic 회피. capability flag `mcp_server` `false` → `true`. agent integration MVP 완성 — Claude Code / Cursor / OpenAI Agents 등 host-agnostic 사용 가능. spec: `tasks/p9/p9-fb-30-mcp-server.md`. design: `docs/superpowers/specs/2026-05-07-p9-fb-30-mcp-server-design.md`. - **P3-5 / P4-3 `--config` 누락** — `kebab-cli` 가 `--config ` 를 honor 하려면 `kebab_app::*_with_config` companion 을 호출해야 함. 두 번 같은 모양으로 회귀했음. diff --git a/crates/kebab-config/src/lib.rs b/crates/kebab-config/src/lib.rs index dd9b5d1..bbcd825 100644 --- a/crates/kebab-config/src/lib.rs +++ b/crates/kebab-config/src/lib.rs @@ -393,6 +393,25 @@ impl Config { if p.exists() { Self::from_file(&p)? } else { + // macOS migration: if the new XDG path is absent but the + // old ~/Library/Application Support/kebab/config.toml exists, + // copy it to the new location so the user doesn't lose settings. + if let Some(legacy) = Self::macos_legacy_config_path() { + if legacy.exists() && !p.exists() { + if let Some(parent) = p.parent() { + let _ = std::fs::create_dir_all(parent); + } + if std::fs::copy(&legacy, &p).is_ok() { + eprintln!( + "kebab: migrated config {} → {}", + legacy.display(), + p.display() + ); + return Self::from_file(&p) + .map(|c| c.apply_env(&std::env::vars().collect())); + } + } + } Self::defaults() } } @@ -634,8 +653,11 @@ impl Config { return PathBuf::from(custom).join("kebab").join("config.toml"); } } - match dirs::config_dir() { - Some(d) => d.join("kebab").join("config.toml"), + // Always use XDG-standard ~/.config regardless of platform. + // macOS dirs::config_dir() returns ~/Library/Application Support which + // collides with data_dir() — DataOnly reset would delete config too. + match dirs::home_dir() { + Some(h) => h.join(".config").join("kebab").join("config.toml"), None => PathBuf::from("./kebab/config.toml"), } } @@ -647,8 +669,9 @@ impl Config { return PathBuf::from(custom).join("kebab"); } } - match dirs::data_dir() { - Some(d) => d.join("kebab"), + // Always use XDG-standard ~/.local/share regardless of platform. + match dirs::home_dir() { + Some(h) => h.join(".local").join("share").join("kebab"), None => PathBuf::from("./kebab-data"), } } @@ -660,8 +683,9 @@ impl Config { return PathBuf::from(custom).join("kebab"); } } - match dirs::cache_dir() { - Some(d) => d.join("kebab"), + // Always use XDG-standard ~/.cache regardless of platform. + match dirs::home_dir() { + Some(h) => h.join(".cache").join("kebab"), None => PathBuf::from("./kebab-cache"), } } @@ -680,6 +704,25 @@ impl Config { } PathBuf::from("./kebab-state") } + + /// macOS legacy config path: `~/Library/Application Support/kebab/config.toml`. + /// Returns `None` on non-macOS or when home dir is unavailable. + /// Used for one-time migration to the XDG-standard location. + fn macos_legacy_config_path() -> Option { + #[cfg(target_os = "macos")] + { + dirs::home_dir().map(|h| { + h.join("Library") + .join("Application Support") + .join("kebab") + .join("config.toml") + }) + } + #[cfg(not(target_os = "macos"))] + { + None + } + } } /// Parse a permissive boolean — `1` / `true` / `yes` (case-insensitive) diff --git a/tasks/HOTFIXES.md b/tasks/HOTFIXES.md index 8e68858..21eca2e 100644 --- a/tasks/HOTFIXES.md +++ b/tasks/HOTFIXES.md @@ -14,6 +14,20 @@ historical contract that was implemented; this file accumulates the deltas so phase 5+ readers can find the live behavior without diffing git history. +## 2026-05-07 (2) + +### macOS XDG path collision: `data_dir` == `config_dir` → DataOnly reset deletes config + +- **File**: `crates/kebab-config/src/lib.rs` +- **Root cause**: `dirs` crate 가 macOS 에서 `config_dir()` 과 `data_dir()` 모두 `~/Library/Application Support/` 반환. `ResetScope::DataOnly` 가 `data_dir` 을 삭제하면 config 파일까지 함께 삭제됨. +- **Fix**: `xdg_config_path`, `xdg_data_dir`, `xdg_cache_dir` 의 `dirs` fallback 제거 → `$HOME/.config`, `$HOME/.local/share`, `$HOME/.cache` 직접 사용 (XDG 표준, 플랫폼 무관). +- **Migration**: `Config::load(None)` 에서 새 경로 없고 macOS legacy (`~/Library/Application Support/kebab/config.toml`) 있으면 자동 copy + stderr 안내. +- **New paths** (macOS): + - config: `~/.config/kebab/config.toml` (was `~/Library/Application Support/kebab/config.toml`) + - data: `~/.local/share/kebab/` (was `~/Library/Application Support/kebab/`) + - cache: `~/.cache/kebab/` (was `~/Library/Caches/kebab/`) + - state: `~/.local/state/kebab/` (unchanged) + ## 2026-05-07 ### fb-26: ingest 로그 `Aborted` 무조건 writeln + `Completed` TTY 요약 없음