Skip to content

feat(workspace): relocate per-project local state to ~/.grepai/#259

Open
kryptt wants to merge 1 commit into
yoanbernabeu:mainfrom
kryptt:pr/workspace-state-dir
Open

feat(workspace): relocate per-project local state to ~/.grepai/#259
kryptt wants to merge 1 commit into
yoanbernabeu:mainfrom
kryptt:pr/workspace-state-dir

Conversation

@kryptt

@kryptt kryptt commented May 28, 2026

Copy link
Copy Markdown

Problem

When a workspace indexes a read-only project root — typical examples are `/usr/share/emacs/<X.Y>/`, `/opt/foo/`, or any directory owned by root and read-only for the user — grepai's attempt to `mkdir <project_root>/.grepai/` fails with `EACCES`. The symbol index and RPG index silently fail to persist; the watcher logs a warning per project on every run.

In my own workspace this affected `emacs-source` (`/usr/share/emacs/30.2`) and `site-lisp` (`/usr/share/emacs/site-lisp`). The PostgreSQL vector store still landed chunks fine, but the per-project local state was unreachable.

Solution

Adds a workspace-level `local_state_dir` option:

```yaml
workspaces:
emacs:
local_state_dir: ~/grepai-state # or any absolute path
projects:
- name: emacs-source
path: /usr/share/emacs/30.2
```

  • When set: each project's local state lives at `<local_state_dir>//`. A leading `~/` is expanded against the user's home.
  • When unset (the new default): local state lives under `~/.grepai/workspaces///`. Workspace projects no longer touch the project root at all.

Two helpers — `Workspace.ResolvedLocalStateDir()` and `Workspace.ProjectStateDir(projectName)` — encapsulate the expansion + join logic and are useful for downstream consumers (e.g. future MCP tooling that wants to read the symbol index).

The workspace runner in `cli/watch.go` `MkdirAll`s the project state dir and feeds it to the GOB symbol + RPG stores instead of `config.GetSymbolIndexPath(project.Path)` / `config.GetRPGIndexPath(project.Path)`. Those helpers are unchanged and still target `<project_root>/.grepai/` for the single-project flow where it makes sense.

Single-project mode (`grepai watch` without `--workspace`) and per-project `.grepai/config.yaml` are unaffected — only the workspace runner changes location.

Behavior change

Before: workspace projects created (or attempted to create) `<project_root>/.grepai/`.
After: workspace projects use `~/.grepai/workspaces///` by default.

Existing workspace users will see their per-project state move to a new location on first run after upgrade. Vector stores are unaffected (those go to PostgreSQL/Qdrant); only the GOB symbol/RPG indexes relocate. Each will rebuild on the next `grepai watch --workspace …`.

If preserving in-place behavior is preferred, the field can default to empty-with-fallback-to-project-root instead; happy to adjust based on review.

Test plan

Four new tests in `config/workspace_test.go`:

Test Asserts
`TestWorkspace_ResolvedLocalStateDir_Default` unset → `~/.grepai/workspaces//`
`TestWorkspace_ResolvedLocalStateDir_TildeExpansion` `~/foo` expands to `$HOME/foo`
`TestWorkspace_ResolvedLocalStateDir_AbsolutePassthrough` `/var/lib/...` passes through verbatim
`TestWorkspace_ProjectStateDir` composes resolved base with project name

All existing tests continue to pass (`go test ./...`).

When a workspace indexes a read-only project root — typical examples
are /usr/share/emacs/<X.Y>/, /opt/foo/, anything owned by root — the
old behaviour of mkdir <project_root>/.grepai/ failed with EACCES and
the symbol / RPG indexes silently failed to persist.

Adds Workspace.LocalStateDir (yaml: local_state_dir). When set, the
workspace stores per-project local state under that directory. A
leading "~/" is expanded to the user's home. When unset, the default
is ~/.grepai/workspaces/<workspace-name>/, so workspace projects no
longer write into their project roots at all by default.

Two helpers — Workspace.ResolvedLocalStateDir and ProjectStateDir —
encapsulate the expansion+join logic. The workspace runner in
cli/watch.go now MkdirAll's the project state dir and feeds it to the
GOB symbol and RPG stores instead of GetSymbolIndexPath / GetRPGIndexPath,
which still target <project_root>/.grepai/ for the single-project mode
where it makes sense.

Single-project mode and the per-project `.grepai/config.yaml` flow are
unchanged.
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.

1 participant