# Phase 1: Project Setup and Configuration

## Goal

Establish the Python package structure, configuration system, and CLI entry points so subsequent phases have a runnable skeleton to build on.

## Research Notes

### Package structure
- Using `src/unfamiliar/` layout per decisions.md. Prevents accidental working-directory imports.
- Entry point via pyproject.toml `[project.scripts]`: `unfamiliar = "unfamiliar.cli:app"`
- Typer app with `@app.command()` decorators for each CLI command.

### Configuration
- `~/.unfamiliar/config.toml` for user settings. TOML parsing via `tomllib` (stdlib 3.11+) with `tomli` fallback for 3.10.
- API key resolution order: provider env var → `UNFAMILIAR_API_KEY` → config file (strict permissions only).
- Pydantic model for config validation.

### Key dependencies (runtime)
- `langchain-core`, `langchain-anthropic` — LLM provider abstraction
- `langgraph` — pipeline orchestration and checkpointing
- `textual` — TUI framework (includes Rich)
- `typer[all]` — CLI framework
- `pydantic` / `pydantic-settings` — data validation and config
- `tree-sitter` — multi-language parsing (TS/JS adapters)
- `detect-secrets` — secret detection
- `pip-audit` — Python CVE scanning

### Key dependencies (dev)
- `pytest`, `pytest-cov`, `pytest-asyncio` — testing
- `hypothesis` — property-based testing
- `ruff` — linting and formatting
- `mypy` — type checking

### Directory structure target

```
src/unfamiliar/
├── __init__.py              # Package version
├── __main__.py              # python -m unfamiliar support
├── cli.py                   # Typer app and commands
├── config.py                # Configuration loading and validation
├── constants.py             # Enums and constants (categories, statuses, etc.)
├── models/                  # Pydantic domain models
│   └── __init__.py
├── db/                      # SQLite repository layer
│   └── __init__.py
├── analysis/                # Static analysis pipeline
│   ├── __init__.py
│   ├── adapters/            # Language-specific adapters
│   │   └── __init__.py
│   ├── graph/               # Import graph and module clustering
│   │   └── __init__.py
│   └── security/            # Secret detection, CVE scanning
│       └── __init__.py
├── llm/                     # LLM integration
│   ├── __init__.py
│   ├── providers/           # Provider abstraction
│   │   └── __init__.py
│   ├── briefing/            # Briefing generation
│   │   └── __init__.py
│   └── missions/            # Mission generation and validation
│       └── __init__.py
├── campaign/                # Campaign lifecycle orchestration
│   └── __init__.py
├── tui/                     # Textual TUI application
│   ├── __init__.py
│   ├── screens/             # Screen definitions
│   │   └── __init__.py
│   ├── widgets/             # Custom widgets
│   │   └── __init__.py
│   └── themes/              # Theme packages
│       ├── __init__.py
│       └── film_noir/       # Film Noir theme files
│           └── __init__.py
└── standards/               # Built-in coding standards
    └── __init__.py
```

## Tasks

### Task 1: Create pyproject.toml and package structure

**Files:** `pyproject.toml`, `src/unfamiliar/__init__.py`, `src/unfamiliar/__main__.py`
**Action:**
- Create `pyproject.toml` with:
  - Project metadata (name, version 0.1.0, description, Python >=3.10)
  - Runtime dependencies (langchain-core, langchain-anthropic, langgraph, textual, typer[all], pydantic, pydantic-settings, tree-sitter, detect-secrets, pip-audit)
  - Dev dependencies group (pytest, pytest-cov, pytest-asyncio, hypothesis, ruff, mypy)
  - Script entry point: `unfamiliar = "unfamiliar.cli:app"`
  - Ruff configuration (line length 88, target Python 3.10)
  - Mypy configuration (strict mode)
- Create `src/unfamiliar/__init__.py` with `__version__ = "0.1.0"`
- Create `src/unfamiliar/__main__.py` that calls the Typer app
**Verify:** `pip install -e ".[dev]"` succeeds. `unfamiliar --help` shows help text.
**Done when:** Package installs in dev mode and CLI entry point responds.

### Task 2: Create application directory scaffolding

**Files:** All `__init__.py` files in the directory structure above
**Action:**
- Create every directory and `__init__.py` from the structure in Research Notes
- Each `__init__.py` gets a one-line module docstring describing the subpackage's purpose
- No implementation code — just the skeleton with docstrings
**Verify:** All directories exist. `python -c "import unfamiliar"` succeeds.
**Done when:** Full directory tree is in place with docstrings. No import errors.

### Task 3: Domain enums and constants

**Files:** `src/unfamiliar/constants.py`
**Action:**
- Define string enums for all domain constants from the requirements:
  - `InvestigationCategory` — architecture, domain_business_logic, data_flow, dependencies, conventions, testing, technical_debt
  - `CampaignStatus` — initializing, active, closed
  - `MissionStatus` — locked, available, in_progress, completed
  - `MissionDifficulty` — quick, medium, deep_dive (with `time_minutes` property mapping: quick=15, medium=30, deep_dive=45)
  - `CompletionMethod` — self_report (MVP only; annotation and field_report reserved for v1.1)
  - `CampaignMode` — onboarding (MVP default; assessment reserved for v1.1)
  - `CharacterArchetype` — detective, forensic_analyst, fixer (all defined, but unused in MVP)
  - `Theme` — film_noir
- Add `DEFAULT_TOKEN_BUDGET = 50_000` and other configurable defaults
**Verify:** All enums importable. `MissionDifficulty.quick.time_minutes == 15`.
**Done when:** Every enum value from requirements is represented. Forward-compatible v1.1 values are defined but clearly marked.

### Task 4: Configuration system

**Files:** `src/unfamiliar/config.py`
**Action:**
- Define `UnfamiliarConfig` Pydantic model with fields:
  - `provider: str` (default "anthropic")
  - `token_budget: int` (default 50_000)
  - `theme: str` (default "film_noir")
  - `api_keys: dict[str, str]` (optional, loaded from config file only if permissions are strict)
- Implement `load_config(config_path: Path | None = None) -> UnfamiliarConfig`:
  - Default path: `~/.unfamiliar/config.toml`
  - Create directory and default config if they don't exist
  - Parse TOML, validate with Pydantic
- Implement `resolve_api_key(provider: str, config: UnfamiliarConfig) -> str`:
  - Check provider-specific env var (e.g., `ANTHROPIC_API_KEY`)
  - Check `UNFAMILIAR_API_KEY` generic fallback
  - Check config file `api_keys` section (only if file permissions are owner-only/600)
  - Raise clear error if no key found
- Implement `get_campaign_dir(repo_path: Path) -> Path`:
  - Returns `~/.unfamiliar/campaigns/<repo-name>-<hash>/`
  - Hash = first 6 chars of SHA-256 of absolute repo path
  - Handle hash collisions by appending `-1`, `-2`, etc.
**Verify:** Config loads from default path. API key resolution follows the documented precedence. Permission check rejects group/world-readable files. Campaign directory naming matches spec.
**Done when:** Config system handles all documented scenarios: missing file (creates default), env vars, permission checking, campaign directory naming with collision handling.

### Task 5: CLI entry points

**Files:** `src/unfamiliar/cli.py`
**Action:**
- Create Typer app with `help` text describing Unfamiliar
- Add `--version` callback flag
- Add skeleton commands (print placeholder messages, no implementation):
  - `investigate(repo_path: Path)` — "Start a new investigation or resume an existing one"
  - `campaigns()` — "List all saved campaigns"
  - `resume()` — "Resume the most recent active campaign"
  - `reconnect(campaign: str, new_path: Path)` — "Re-link an orphaned campaign"
  - `upgrade()` — "Upgrade an older campaign to the current schema"
- Each command imports and validates config on entry (call `load_config()`)
- Commands that need a repo path validate it exists and is a git repository
**Verify:** `unfamiliar --version` prints version. Each command responds with placeholder text. `unfamiliar investigate /nonexistent` shows a clear error.
**Done when:** All five commands exist with help text, argument validation, and placeholder output. Config loads on every command invocation.

## Dependencies

None — this is the first phase.

## Security Checklist

- API key resolution never logs or prints key values
- Config file permission check enforces owner-only (mode 600) before reading API keys section
- No secrets stored in version control (config.toml is in ~/.unfamiliar/, not the project directory)

## Issues Discovered During Verification Stage

### Linting Issues (Fixed)
1. **E501** — Several docstrings exceeded 88 characters. Fixed by rewording.
2. **UP045** — Used `Optional[X]` instead of `X | None`. Fixed to use modern syntax.
3. **B008** — Typer function calls in argument defaults. Added `# noqa: B008` (standard Typer pattern).
4. **B904** — Missing `from None` on exception re-raises. Fixed by adding `from None`.
5. **SIM102** — Nested if statements in config.py. Combined into single condition with `and`.
6. **I001** — Import block unsorted in config.py. Fixed import order.

### Type Checking Issues (Fixed)
1. **no-any-return** — StrEnum backport's `__str__` returned `Any`. Fixed by wrapping `self.value` in `str()`.
2. **unused-ignore** — tomli import had unnecessary type ignore comment. Removed.

### Pre-existing Code
The project already had skeleton code from a previous session. Code was reviewed, CLI commands were completed, and all verification criteria were met.
