Config: .env = projects root only; derive each project's forge from git

Reduce .env to just REPOS_HOST_PATH (the projects root); runtime bootstrap moves to compose/defaults. Projects are the subdirectories of the single root — removed the project-directories feature (store table, service methods, /api/config/project-dirs, Settings section). Each project's forge is derived from its git remote (matched to a configured forge, else the bare host) and shown as a pill next to its name (State.Forge via scanner ForgeFor + svc.ForgeDisplay). Removed first-run .env seeding; forges + identity are managed in Settings. Added forge.HostOf. AGENT.md updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-22 05:52:03 -04:00
parent e30c3b632a
commit 32ae17cc9f
15 changed files with 175 additions and 434 deletions
-92
View File
@@ -28,14 +28,6 @@ type Forge struct {
CreatedAt time.Time `json:"createdAt"`
}
// ProjectDir is a directory (inside the container) to scan for repositories.
type ProjectDir struct {
ID int64 `json:"id"`
Path string `json:"path"`
Enabled bool `json:"enabled"`
CreatedAt time.Time `json:"createdAt"`
}
// Store wraps the SQLite connection.
type Store struct {
db *sql.DB
@@ -69,12 +61,6 @@ CREATE TABLE IF NOT EXISTS forges (
token TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS project_dirs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT NOT NULL UNIQUE,
enabled INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
@@ -142,73 +128,6 @@ func (s *Store) DeleteForge(ctx context.Context, id int64) error {
return err
}
// --- project dirs ----------------------------------------------------------
func (s *Store) ListProjectDirs(ctx context.Context) ([]ProjectDir, error) {
rows, err := s.db.QueryContext(ctx, `SELECT id, path, enabled, created_at FROM project_dirs ORDER BY path`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []ProjectDir
for rows.Next() {
var d ProjectDir
var created string
var enabled int
if err := rows.Scan(&d.ID, &d.Path, &enabled, &created); err != nil {
return nil, err
}
d.Enabled = enabled != 0
d.CreatedAt, _ = time.Parse(time.RFC3339, created)
out = append(out, d)
}
return out, rows.Err()
}
// EnabledRoots returns the paths of enabled project directories.
func (s *Store) EnabledRoots(ctx context.Context) ([]string, error) {
rows, err := s.db.QueryContext(ctx, `SELECT path FROM project_dirs WHERE enabled=1 ORDER BY path`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []string
for rows.Next() {
var p string
if err := rows.Scan(&p); err != nil {
return nil, err
}
out = append(out, p)
}
return out, rows.Err()
}
func (s *Store) AddProjectDir(ctx context.Context, path string) (ProjectDir, error) {
d := ProjectDir{Path: path, Enabled: true, CreatedAt: time.Now()}
res, err := s.db.ExecContext(ctx,
`INSERT INTO project_dirs (path, enabled, created_at) VALUES (?,1,?)`,
path, d.CreatedAt.Format(time.RFC3339))
if err != nil {
return ProjectDir{}, err
}
d.ID, _ = res.LastInsertId()
return d, nil
}
func (s *Store) SetProjectDirEnabled(ctx context.Context, id int64, enabled bool) error {
v := 0
if enabled {
v = 1
}
_, err := s.db.ExecContext(ctx, `UPDATE project_dirs SET enabled=? WHERE id=?`, v, id)
return err
}
func (s *Store) DeleteProjectDir(ctx context.Context, id int64) error {
_, err := s.db.ExecContext(ctx, `DELETE FROM project_dirs WHERE id=?`, id)
return err
}
// --- settings (kv) ---------------------------------------------------------
func (s *Store) GetSetting(ctx context.Context, key, def string) (string, error) {
@@ -229,14 +148,3 @@ func (s *Store) SetSetting(ctx context.Context, key, value string) error {
ON CONFLICT(key) DO UPDATE SET value=excluded.value`, key, value)
return err
}
// IsEmpty reports whether the store has no forges and no project dirs (used to
// decide whether to seed from .env on first run).
func (s *Store) IsEmpty(ctx context.Context) (bool, error) {
var n int
if err := s.db.QueryRowContext(ctx,
`SELECT (SELECT COUNT(*) FROM forges) + (SELECT COUNT(*) FROM project_dirs)`).Scan(&n); err != nil {
return false, err
}
return n == 0, nil
}