diff --git a/CHANGELOG.md b/CHANGELOG.md index f614c05..fd69aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ it reaches 1.0. ## [Unreleased] +### Fixed + +- Reject invalid `lore catalog --kind` overrides before importing entries. + ### Documentation - Document the `Stacked on: #N` PR body convention for non-main-based PRs (CONTRIBUTING.md, PR template). diff --git a/docs/generated/cli.md b/docs/generated/cli.md index 3cd41fd..08721f9 100644 --- a/docs/generated/cli.md +++ b/docs/generated/cli.md @@ -730,6 +730,10 @@ client's official CLI so you keep full control. Flags: --run shell out to each detected client's CLI (prompts per command) --run --yes shell out without prompting + --update re-register clients whose configured path differs from the + running binary (#61) + --force re-register every detected client unconditionally; implies + --update --print-config print only the JSON snippet for manual paste (no detection) --skill (not yet implemented) install Claude Code skill @@ -737,9 +741,11 @@ Flags: | flag | type | default | description | | --- | --- | --- | --- | +| `--force` | bool | `false` | re-register every detected client unconditionally (implies --update) | | `--print-config` | bool | `false` | print JSON snippet for manual paste (no detection output) | | `--run` | bool | `false` | shell out to each detected client's CLI (prompts per command) | | `--skill` | bool | `false` | install Claude Code skill (not yet implemented) | +| `--update` | bool | `false` | re-register clients whose configured path differs from the running binary | | `--yes` | bool | `false` | skip per-command confirmation prompts (combine with --run) | ## `guild mcp serve` diff --git a/internal/lore/catalog.go b/internal/lore/catalog.go index ddfb13c..e108012 100644 --- a/internal/lore/catalog.go +++ b/internal/lore/catalog.go @@ -62,6 +62,10 @@ func Catalog(ctx context.Context, db *sql.DB, p *CatalogParams) (*CatalogResult, if strings.TrimSpace(p.Dir) == "" { return nil, fmt.Errorf("lore: catalog: dir required") } + if p.Kind != "" && !isValidKind(p.Kind) { + return nil, fmt.Errorf("%w: %q (valid: idea, research, decision, observation, principle)", + ErrInvalidKind, string(p.Kind)) + } info, err := os.Stat(p.Dir) if err != nil { diff --git a/internal/lore/catalog_test.go b/internal/lore/catalog_test.go index 02d7a89..5455cd5 100644 --- a/internal/lore/catalog_test.go +++ b/internal/lore/catalog_test.go @@ -2,8 +2,10 @@ package lore import ( "context" + "errors" "os" "path/filepath" + "strings" "testing" ) @@ -146,6 +148,38 @@ func TestCatalog_KindInference(t *testing.T) { } } +func TestCatalog_InvalidKindOverride(t *testing.T) { + ctx := context.Background() + db := openTestDB(t, "catalog-bad-kind") + + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, "note.md"), []byte("# Note\n\nSome content here."), 0o600); err != nil { + t.Fatalf("write note.md: %v", err) + } + + _, err := Catalog(ctx, db, &CatalogParams{ + Dir: dir, + ProjectID: "catalog-bad-kind", + Kind: Kind("not-a-real-kind"), + }) + if !errors.Is(err, ErrInvalidKind) { + t.Fatalf("Catalog error = %v; want ErrInvalidKind", err) + } + if !strings.Contains(err.Error(), "valid: idea, research, decision, observation, principle") { + t.Fatalf("Catalog error = %q; want valid kind list", err.Error()) + } + + var count int + if err := db.QueryRowContext(ctx, + `SELECT COUNT(*) FROM entries WHERE project_id = 'catalog-bad-kind'`, + ).Scan(&count); err != nil { + t.Fatalf("count entries: %v", err) + } + if count != 0 { + t.Errorf("entries in DB = %d; want 0", count) + } +} + func TestCatalog_SkipsNonMD(t *testing.T) { ctx := context.Background() db := openTestDB(t, "catalog-ext")