Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ Every plan is automatically saved to `~/.plannotator/history/{project}/{slug}/`

This powers the version history API (`/api/plan/version`, `/api/plan/versions`) and the plan diff system.

History saves independently of the `planSave` user setting (which controls decision snapshots in `~/.plannotator/plans/`). Storage functions live in `packages/shared/storage.ts` (runtime-agnostic, re-exported by `packages/server/storage.ts`). Pi copies the shared files at build time. Slug format: `{sanitized-heading}-YYYY-MM-DD` (heading first for readability).
History saves independently of the `planSave` user setting (which controls decision snapshots in `~/.config/plannotator/data/plans/`). Storage functions live in `packages/shared/storage.ts` (runtime-agnostic, re-exported by `packages/server/storage.ts`). Pi copies the shared files at build time. Slug format: `{sanitized-heading}-YYYY-MM-DD` (heading first for readability).

## Plan Diff

Expand Down
68 changes: 68 additions & 0 deletions XDG_DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# XDG Base Directory Support — Design Decisions

## Problem

Plannotator stored all user data in `~/.plannotator/`. Users who follow the XDG Base Directory Specification wanted support for `~/.config/`, `~/.local/share/`, and `~/.cache/` instead.

## Solution

Add XDG-aware path resolution with **strict backward compatibility**.

### Resolution Priority (highest wins)

1. `PLANNOTATOR_*_DIR` environment variable (explicit override)
2. `XDG_*_HOME` environment variable (XDG spec)
3. `~/.plannotator` (original default — unchanged for existing users)

### Directory Mapping

| Category | Contents | Env Override | XDG Fallback | Default |
|----------|----------|--------------|--------------|---------|
| Config | `config.json` | `PLANNOTATOR_CONFIG_DIR` | `XDG_CONFIG_HOME` | `~/.plannotator` |
| Data | `plans/`, `history/`, `hooks/` | `PLANNOTATOR_DATA_DIR` | `XDG_DATA_HOME` | `~/.plannotator` |
| Cache | `drafts/`, `sessions/`, `pastes/`, logs, schemas | `PLANNOTATOR_CACHE_DIR` | `XDG_CACHE_HOME` | `~/.plannotator` |

### Migration Strategy

- **Reads**: Try resolved path first; if missing, fall back to `~/.plannotator/`
- **Writes**: Always write to the resolved path
- **Cache**: No fallback (reconstructible)
- **Indefinite**: Legacy fallback stays forever; no forced migration

This means:
- Existing users with `~/.plannotator/` see **zero change** unless they set XDG vars
- New users who set XDG vars get **proper spec compliance**
- Mixed-state users (some data in old path, some in new) see **merged views** where applicable (e.g., archive listing)

### Install Scripts

The bash, PowerShell, and CMD installers now resolve the config path dynamically:
1. Check `PLANNOTATOR_CONFIG_DIR`
2. Check `XDG_CONFIG_HOME`
3. Fall back to `~/.plannotator`

This ensures `verifyAttestation` config is read from the correct location regardless of the user's path setup.

### Tests

- `improvement-hooks.test.ts`: Validates both new-path reads and legacy-path fallback
- `test-sessions.sh`: Uses default `~/.plannotator/sessions` (no hardcoded XDG paths)

### Upstream PR Notes

- **No breaking changes**: Default behavior is identical to before
- **Opt-in only**: Users must set XDG env vars to activate new paths
- **No forced migration**: Old data is never moved or deleted
- **Windows**: Continues using `~/.plannotator` by default (cross-platform consistency)

## Personal Preference vs. Default

If you want data under `~/.config/plannotator/data/` instead of `~/.local/share/plannotator/`, set:

```bash
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_DATA_HOME="$HOME/.config" # non-standard but valid
export XDG_CACHE_HOME="$HOME/.cache"
```

This is the author's personal preference but is **not** the default, to respect standard XDG conventions for other users.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ If you are upgrading from an older OpenCode install, see the [OpenCode 0.19.1 mi

## Plan saving

Approved and denied plans are saved to `~/.plannotator/plans/` by default. You can change the save directory or disable saving in the Plannotator UI settings (gear icon).
Approved and denied plans are saved to `~/.config/plannotator/data/plans/` by default. You can change the save directory or disable saving in the Plannotator UI settings (gear icon).

## Remote mode

Expand Down
4 changes: 2 additions & 2 deletions apps/marketing/src/content/docs/guides/self-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ chmod +x plannotator-paste-*
./plannotator-paste-darwin-arm64 # or whichever matches your platform
```

Pastes stored to `~/.plannotator/pastes/` by default.
Pastes stored to `~/.cache/plannotator/pastes/` by default.

### Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `PASTE_PORT` | `19433` | Server port |
| `PASTE_DATA_DIR` | `~/.plannotator/pastes` | Storage directory |
| `PASTE_DATA_DIR` | `~/.cache/plannotator/pastes` | Storage directory |
| `PASTE_TTL_DAYS` | `7` | Auto-delete after N days |
| `PASTE_MAX_SIZE` | `524288` | Max payload size (512KB) |
| `PASTE_ALLOWED_ORIGINS` | (see defaults) | CORS allowed origins |
Expand Down
19 changes: 11 additions & 8 deletions apps/marketing/src/content/docs/guides/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ Stale sessions from crashed processes are cleaned up automatically. You can also

## Where does Plannotator store data?

All local data lives under `~/.plannotator/`:

| Directory | What's in it |
|-----------|-------------|
| `plans/` | Snapshots of approved and denied plans. Controlled by the "Save plans" toggle in Settings. |
| `history/` | Automatic version history for every plan, organized by project and heading. Powers the plan diff and version browser. |
| `drafts/` | Auto-saved annotation drafts. If a server crashes mid-review, your in-progress annotations are recovered on the next session. |
| `sessions/` | Temporary session files for active servers. Cleaned up automatically when a server exits. |
All local data lives under `~/.plannotator/` by default. When `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, or `XDG_CACHE_HOME` are set, Plannotator respects those locations instead.

| Directory | Default | XDG override | What's in it |
|-----------|---------|--------------|-------------|
| **Config** | `~/.plannotator/` | `$XDG_CONFIG_HOME/plannotator/` | `config.json` — user settings. |
| **Data** | `~/.plannotator/` | `$XDG_DATA_HOME/plannotator/` | `plans/`, `history/`, `hooks/` |
| **Cache** | `~/.plannotator/` | `$XDG_CACHE_HOME/plannotator/` | `drafts/`, `sessions/`, `pastes/` |

You can also override any path via `PLANNOTATOR_CONFIG_DIR`, `PLANNOTATOR_DATA_DIR`, or `PLANNOTATOR_CACHE_DIR`.

Legacy data in `~/.plannotator/` is still readable for backward compatibility but new data is written to the resolved path.

Plan saving is enabled by default. You can change the save directory or disable it entirely in the Plannotator UI settings (gear icon).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ All Plannotator environment variables and their defaults.
| `PLANNOTATOR_SHARE` | enabled | Set to `disabled` to turn off sharing. Hides share UI and import options. |
| `PLANNOTATOR_SHARE_URL` | `https://share.plannotator.ai` | Base URL for share links. Set this when self-hosting the share portal. |
| `PLANNOTATOR_PLAN_TIMEOUT_SECONDS` | `345600` | OpenCode only. `submit_plan` wait timeout in seconds. Set `0` to disable timeout. |
| `PLANNOTATOR_CONFIG_DIR` | `~/.plannotator` | Override the config directory. Falls back to `XDG_CONFIG_HOME/plannotator` when `XDG_CONFIG_HOME` is set. |
| `PLANNOTATOR_DATA_DIR` | `~/.plannotator` | Override the data directory (plans, history, hooks). Falls back to `XDG_DATA_HOME/plannotator` when `XDG_DATA_HOME` is set. |
| `PLANNOTATOR_CACHE_DIR` | `~/.plannotator` | Override the cache directory (drafts, sessions, pastes, logs). Falls back to `XDG_CACHE_HOME/plannotator` when `XDG_CACHE_HOME` is set. |

## Annotation variables

Expand Down
11 changes: 9 additions & 2 deletions apps/paste-service/targets/bun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import { handleRequest } from "../core/handler";
import { corsHeaders, getAllowedOrigins } from "../core/cors";
import { FsPasteStore } from "../stores/fs";

function getCacheDir(): string {
const dir =
process.env.PLANNOTATOR_CACHE_DIR ||
(process.env.XDG_CACHE_HOME && join(process.env.XDG_CACHE_HOME, "plannotator")) ||
join(homedir(), ".cache", "plannotator");
return dir;
}

const port = parseInt(process.env.PASTE_PORT || "19433", 10);
const dataDir =
process.env.PASTE_DATA_DIR || join(homedir(), ".plannotator", "pastes");
const dataDir = process.env.PASTE_DATA_DIR || join(getCacheDir(), "pastes");
const ttlDays = parseInt(process.env.PASTE_TTL_DAYS || "7", 10);
const ttlSeconds = ttlDays * 24 * 60 * 60;
const maxSize = parseInt(process.env.PASTE_MAX_SIZE || "524288", 10);
Expand Down
11 changes: 10 additions & 1 deletion apps/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ import { createCookieProxy } from "./cookie-proxy";
import { PanelManager } from "./panel-manager";
import { setActiveProxyPort, registerEditorAnnotationCommand } from "./editor-annotations";

const IPC_REGISTRY = path.join(os.homedir(), ".plannotator", "vscode-ipc.json");
function getCacheDir(): string {
const dir =
process.env.PLANNOTATOR_CACHE_DIR ||
(process.env.XDG_CACHE_HOME && path.join(process.env.XDG_CACHE_HOME, "plannotator")) ||
path.join(os.homedir(), ".cache", "plannotator");
fs.mkdirSync(dir, { recursive: true });
return dir;
}

const IPC_REGISTRY = path.join(getCacheDir(), "vscode-ipc.json");

function readIpcRegistry(): Record<string, number> {
try {
Expand Down
Loading