diff --git a/brain-seed b/brain-seed new file mode 100755 index 0000000..c9d44c9 Binary files /dev/null and b/brain-seed differ diff --git a/cmd/brain-seed/main.go b/cmd/brain-seed/main.go index fde7372..692e1ae 100644 --- a/cmd/brain-seed/main.go +++ b/cmd/brain-seed/main.go @@ -1,14 +1,16 @@ // SPDX-License-Identifier: EUPL-1.2 // brain-seed imports Claude Code MEMORY.md files into the OpenBrain knowledge -// store by embedding them via Ollama and storing vectors in Qdrant. +// store via the MCP HTTP API (brain_remember tool). The Laravel app handles +// embedding, Qdrant storage, and MariaDB dual-write internally. // // Usage: // -// go run ./cmd/brain-seed -// go run ./cmd/brain-seed -ollama https://ollama.lan -qdrant https://qdrant.lan -// go run ./cmd/brain-seed -dry-run -// go run ./cmd/brain-seed -plans # Also import plan docs +// go run ./cmd/brain-seed -api-key YOUR_KEY +// go run ./cmd/brain-seed -api-key YOUR_KEY -api https://lthn.sh/api/v1/mcp +// go run ./cmd/brain-seed -api-key YOUR_KEY -dry-run +// go run ./cmd/brain-seed -api-key YOUR_KEY -plans +// go run ./cmd/brain-seed -api-key YOUR_KEY -claude-md # Also import CLAUDE.md files package main import ( @@ -24,41 +26,50 @@ import ( "regexp" "strings" "time" - - "github.com/google/uuid" ) var ( - ollamaURL = flag.String("ollama", "https://ollama.lan", "Ollama base URL") - qdrantURL = flag.String("qdrant", "https://qdrant.lan", "Qdrant base URL") - collection = flag.String("collection", "openbrain", "Qdrant collection name") - model = flag.String("model", "embeddinggemma", "Embedding model") - workspace = flag.Int("workspace", 1, "Workspace ID") - agent = flag.String("agent", "virgil", "Agent ID") + apiURL = flag.String("api", "https://lthn.sh/api/v1/mcp", "MCP API base URL") + apiKey = flag.String("api-key", "", "MCP API key (Bearer token)") + server = flag.String("server", "hosthub-agent", "MCP server ID") + agent = flag.String("agent", "charon", "Agent ID for attribution") dryRun = flag.Bool("dry-run", false, "Preview without storing") plans = flag.Bool("plans", false, "Also import plan documents") + claudeMd = flag.Bool("claude-md", false, "Also import CLAUDE.md files") memoryPath = flag.String("memory-path", "", "Override memory scan path (default: ~/.claude/projects/*/memory/)") planPath = flag.String("plan-path", "", "Override plan scan path (default: ~/Code/*/docs/plans/)") + codePath = flag.String("code-path", "", "Override code root for CLAUDE.md scan (default: ~/Code)") + maxChars = flag.Int("max-chars", 3800, "Max chars per section (embeddinggemma limit ~4000)") ) -// httpClient trusts self-signed certs for .lan domains behind Traefik. +// httpClient with TLS skip for non-public TLDs (.lthn.sh has real certs, but +// allow .lan/.local if someone has legacy config). var httpClient = &http.Client{ - Timeout: 60 * time.Second, + Timeout: 30 * time.Second, Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // .lan only + TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, }, } func main() { flag.Parse() - fmt.Println("OpenBrain Seed — Claude Code Memory Import") + fmt.Println("OpenBrain Seed — MCP API Client") fmt.Println(strings.Repeat("=", 55)) + if *apiKey == "" && !*dryRun { + fmt.Println("ERROR: -api-key is required (or use -dry-run)") + fmt.Println(" Generate one at: https://lthn.sh/admin/mcp/api-keys") + os.Exit(1) + } + if *dryRun { fmt.Println("[DRY RUN] — no data will be stored") } + fmt.Printf("API: %s\n", *apiURL) + fmt.Printf("Server: %s | Agent: %s\n", *server, *agent) + // Discover memory files memPath := *memoryPath if memPath == "" { @@ -80,15 +91,28 @@ func main() { // Also check nested dirs (completed/, etc.) nested, _ := filepath.Glob(filepath.Join(pPath, "*", "*.md")) planFiles = append(planFiles, nested...) + + // Also check host-uk nested repos + home, _ := os.UserHomeDir() + hostUkPath := filepath.Join(home, "Code", "host-uk", "*", "docs", "plans") + hostUkFiles, _ := filepath.Glob(filepath.Join(hostUkPath, "*.md")) + planFiles = append(planFiles, hostUkFiles...) + hostUkNested, _ := filepath.Glob(filepath.Join(hostUkPath, "*", "*.md")) + planFiles = append(planFiles, hostUkNested...) + fmt.Printf("Found %d plan files\n", len(planFiles)) } - // Ensure collection exists - if !*dryRun { - if err := ensureCollection(); err != nil { - fmt.Printf("ERROR: %v\n", err) - os.Exit(1) + // Discover CLAUDE.md files + var claudeFiles []string + if *claudeMd { + cPath := *codePath + if cPath == "" { + home, _ := os.UserHomeDir() + cPath = filepath.Join(home, "Code") } + claudeFiles = discoverClaudeMdFiles(cPath) + fmt.Printf("Found %d CLAUDE.md files\n", len(claudeFiles)) } imported := 0 @@ -109,9 +133,18 @@ func main() { } for _, sec := range sections { - memType := inferType(sec.heading, sec.content) content := sec.heading + "\n\n" + sec.content - tags := buildTags(filename, "memory-import") + if strings.TrimSpace(sec.content) == "" { + skipped++ + continue + } + + memType := inferType(sec.heading, sec.content, "memory") + tags := buildTags(filename, "memory", project) + confidence := confidenceForSource("memory") + + // Truncate to embedding model limit + content = truncate(content, *maxChars) if *dryRun { fmt.Printf(" [DRY] %s/%s :: %s (%s) — %d chars\n", @@ -120,7 +153,7 @@ func main() { continue } - if err := storeMemory(content, project, memType, tags); err != nil { + if err := callBrainRemember(content, memType, tags, project, confidence); err != nil { fmt.Printf(" FAIL %s/%s :: %s — %v\n", project, filename, sec.heading, err) errors++ continue @@ -143,31 +176,71 @@ func main() { continue } - // Plans: take the whole doc as one memory (they're already cohesive) - // But cap at ~4000 chars to stay within embedding context - fullContent := "" for _, sec := range sections { - fullContent += sec.heading + "\n\n" + sec.content + "\n\n" - } - if len(fullContent) > 4000 { - fullContent = fullContent[:4000] - } + content := sec.heading + "\n\n" + sec.content + if strings.TrimSpace(sec.content) == "" { + skipped++ + continue + } - tags := buildTags(filename, "plan-import") + tags := buildTags(filename, "plans", project) + content = truncate(content, *maxChars) - if *dryRun { - fmt.Printf(" [DRY] %s :: %s (plan) — %d chars\n", project, filename, len(fullContent)) + if *dryRun { + fmt.Printf(" [DRY] %s :: %s / %s (plan) — %d chars\n", + project, filename, sec.heading, len(content)) + imported++ + continue + } + + if err := callBrainRemember(content, "plan", tags, project, 0.6); err != nil { + fmt.Printf(" FAIL %s :: %s / %s — %v\n", project, filename, sec.heading, err) + errors++ + continue + } + fmt.Printf(" ok %s :: %s / %s (plan)\n", project, filename, sec.heading) imported++ + } + } + } + + // Process CLAUDE.md files + if *claudeMd && len(claudeFiles) > 0 { + fmt.Println("\n--- CLAUDE.md Files ---") + for _, f := range claudeFiles { + project := extractProjectFromClaudeMd(f) + sections := parseMarkdownSections(f) + + if len(sections) == 0 { + skipped++ continue } - if err := storeMemory(fullContent, project, "plan", tags); err != nil { - fmt.Printf(" FAIL %s :: %s — %v\n", project, filename, err) - errors++ - continue + for _, sec := range sections { + content := sec.heading + "\n\n" + sec.content + if strings.TrimSpace(sec.content) == "" { + skipped++ + continue + } + + tags := buildTags("CLAUDE", "claude-md", project) + content = truncate(content, *maxChars) + + if *dryRun { + fmt.Printf(" [DRY] %s :: CLAUDE.md / %s (convention) — %d chars\n", + project, sec.heading, len(content)) + imported++ + continue + } + + if err := callBrainRemember(content, "convention", tags, project, 0.9); err != nil { + fmt.Printf(" FAIL %s :: CLAUDE.md / %s — %v\n", project, sec.heading, err) + errors++ + continue + } + fmt.Printf(" ok %s :: CLAUDE.md / %s (convention)\n", project, sec.heading) + imported++ } - fmt.Printf(" ok %s :: %s (plan)\n", project, filename) - imported++ } } @@ -179,115 +252,103 @@ func main() { fmt.Printf("%sImported: %d | Skipped: %d | Errors: %d\n", prefix, imported, skipped, errors) } -// storeMemory embeds content and upserts into Qdrant. -func storeMemory(content, project, memType string, tags []string) error { - vec, err := embed(content) - if err != nil { - return fmt.Errorf("embed: %w", err) +// callBrainRemember sends a memory to the MCP API via brain_remember tool. +func callBrainRemember(content, memType string, tags []string, project string, confidence float64) error { + args := map[string]any{ + "content": content, + "type": memType, + "tags": tags, + "confidence": confidence, + } + if project != "" && project != "unknown" { + args["project"] = project } - id := uuid.New().String() payload := map[string]any{ - "workspace_id": *workspace, - "agent_id": *agent, - "type": memType, - "tags": tags, - "project": project, - "confidence": 0.7, - "created_at": time.Now().UTC().Format(time.RFC3339), + "server": *server, + "tool": "brain_remember", + "arguments": args, } - return qdrantUpsert(id, vec, payload) -} - -// embed generates a vector via Ollama. -func embed(text string) ([]float64, error) { - body, _ := json.Marshal(map[string]string{ - "model": *model, - "prompt": text, - }) - - resp, err := httpClient.Post(*ollamaURL+"/api/embeddings", "application/json", bytes.NewReader(body)) + body, err := json.Marshal(payload) if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - b, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(b)) + return fmt.Errorf("marshal: %w", err) } - var result struct { - Embedding []float64 `json:"embedding"` + req, err := http.NewRequest("POST", *apiURL+"/tools/call", bytes.NewReader(body)) + if err != nil { + return fmt.Errorf("request: %w", err) } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, err - } - if len(result.Embedding) == 0 { - return nil, fmt.Errorf("empty embedding") - } - return result.Embedding, nil -} - -// qdrantUpsert stores a point in Qdrant. -func qdrantUpsert(id string, vector []float64, payload map[string]any) error { - body, _ := json.Marshal(map[string]any{ - "points": []map[string]any{ - {"id": id, "vector": vector, "payload": payload}, - }, - }) - - req, _ := http.NewRequest("PUT", - fmt.Sprintf("%s/collections/%s/points", *qdrantURL, *collection), - bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+*apiKey) resp, err := httpClient.Do(req) if err != nil { - return err + return fmt.Errorf("http: %w", err) } defer resp.Body.Close() + respBody, _ := io.ReadAll(resp.Body) + if resp.StatusCode != 200 { - b, _ := io.ReadAll(resp.Body) - return fmt.Errorf("Qdrant HTTP %d: %s", resp.StatusCode, string(b)) + return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(respBody)) } + + var result struct { + Success bool `json:"success"` + Error string `json:"error"` + } + if err := json.Unmarshal(respBody, &result); err != nil { + return fmt.Errorf("decode: %w", err) + } + if !result.Success { + return fmt.Errorf("API: %s", result.Error) + } + return nil } -// ensureCollection creates the Qdrant collection if it doesn't exist. -func ensureCollection() error { - resp, err := httpClient.Get(fmt.Sprintf("%s/collections/%s", *qdrantURL, *collection)) - if err != nil { - return err +// truncate caps content to maxLen chars, appending an ellipsis if truncated. +func truncate(s string, maxLen int) string { + if len(s) <= maxLen { + return s } - resp.Body.Close() + // Find last space before limit to avoid splitting mid-word + cut := maxLen + if idx := strings.LastIndex(s[:maxLen], " "); idx > maxLen-200 { + cut = idx + } + return s[:cut] + "…" +} - if resp.StatusCode == 404 { - body, _ := json.Marshal(map[string]any{ - "vectors": map[string]any{ - "size": 768, - "distance": "Cosine", - }, - }) - req, _ := http.NewRequest("PUT", - fmt.Sprintf("%s/collections/%s", *qdrantURL, *collection), - bytes.NewReader(body)) - req.Header.Set("Content-Type", "application/json") +// discoverClaudeMdFiles finds CLAUDE.md files across a code directory. +func discoverClaudeMdFiles(codePath string) []string { + var files []string - resp, err := httpClient.Do(req) + // Walk up to 4 levels deep, skip node_modules/vendor/.claude + _ = filepath.WalkDir(codePath, func(path string, d os.DirEntry, err error) error { if err != nil { - return err + return nil } - defer resp.Body.Close() - if resp.StatusCode != 200 { - b, _ := io.ReadAll(resp.Body) - return fmt.Errorf("create collection: HTTP %d: %s", resp.StatusCode, string(b)) + if d.IsDir() { + name := d.Name() + if name == "node_modules" || name == "vendor" || name == ".claude" { + return filepath.SkipDir + } + // Limit depth + rel, _ := filepath.Rel(codePath, path) + if strings.Count(rel, string(os.PathSeparator)) > 3 { + return filepath.SkipDir + } + return nil } - fmt.Println("Created Qdrant collection:", *collection) - } - return nil + if d.Name() == "CLAUDE.md" { + files = append(files, path) + } + return nil + }) + + return files } // section is a parsed markdown section. @@ -325,7 +386,7 @@ func parseMarkdownSections(path string) []section { } } - // Flush last + // Flush last section if curHeading != "" && len(curContent) > 0 { text := strings.TrimSpace(strings.Join(curContent, "\n")) if text != "" { @@ -333,6 +394,14 @@ func parseMarkdownSections(path string) []section { } } + // If no headings found, treat entire file as one section + if len(sections) == 0 && strings.TrimSpace(string(data)) != "" { + sections = append(sections, section{ + heading: strings.TrimSuffix(filepath.Base(path), ".md"), + content: strings.TrimSpace(string(data)), + }) + } + return sections } @@ -348,8 +417,29 @@ func extractProject(path string) string { // extractProjectFromPlan derives a project name from a plan path. // ~/Code/eaas/docs/plans/foo.md → "eaas" +// ~/Code/host-uk/core/docs/plans/foo.md → "core" func extractProjectFromPlan(path string) string { - re := regexp.MustCompile(`Code/([^/]+)/docs/plans/`) + // Check host-uk nested repos first + re := regexp.MustCompile(`Code/host-uk/([^/]+)/docs/plans/`) + if m := re.FindStringSubmatch(path); m != nil { + return m[1] + } + re = regexp.MustCompile(`Code/([^/]+)/docs/plans/`) + if m := re.FindStringSubmatch(path); m != nil { + return m[1] + } + return "unknown" +} + +// extractProjectFromClaudeMd derives a project name from a CLAUDE.md path. +// ~/Code/host-uk/core/CLAUDE.md → "core" +// ~/Code/eaas/CLAUDE.md → "eaas" +func extractProjectFromClaudeMd(path string) string { + re := regexp.MustCompile(`Code/host-uk/([^/]+)/`) + if m := re.FindStringSubmatch(path); m != nil { + return m[1] + } + re = regexp.MustCompile(`Code/([^/]+)/`) if m := re.FindStringSubmatch(path); m != nil { return m[1] } @@ -357,14 +447,22 @@ func extractProjectFromPlan(path string) string { } // inferType guesses the memory type from heading + content keywords. -func inferType(heading, content string) string { +func inferType(heading, content, source string) string { + // Source-specific defaults (match PHP BrainIngestCommand behaviour) + if source == "plans" { + return "plan" + } + if source == "claude-md" { + return "convention" + } + lower := strings.ToLower(heading + " " + content) patterns := map[string][]string{ "architecture": {"architecture", "stack", "infrastructure", "layer", "service mesh"}, "convention": {"convention", "standard", "naming", "pattern", "rule", "coding"}, "decision": {"decision", "chose", "strategy", "approach", "domain"}, "bug": {"bug", "fix", "broken", "error", "issue", "lesson"}, - "plan": {"plan", "todo", "roadmap", "milestone", "phase"}, + "plan": {"plan", "todo", "roadmap", "milestone", "phase", "task"}, "research": {"research", "finding", "discovery", "analysis", "rfc"}, } for t, keywords := range patterns { @@ -378,10 +476,27 @@ func inferType(heading, content string) string { } // buildTags creates the tag list for a memory. -func buildTags(filename string, source string) []string { - tags := []string{source} - if filename != "MEMORY" { +func buildTags(filename, source, project string) []string { + tags := []string{"source:" + source} + if project != "" && project != "unknown" { + tags = append(tags, "project:"+project) + } + if filename != "MEMORY" && filename != "CLAUDE" { tags = append(tags, strings.ReplaceAll(strings.ReplaceAll(filename, "-", " "), "_", " ")) } return tags } + +// confidenceForSource returns a default confidence level matching the PHP ingest command. +func confidenceForSource(source string) float64 { + switch source { + case "claude-md": + return 0.9 + case "memory": + return 0.8 + case "plans": + return 0.6 + default: + return 0.5 + } +} diff --git a/cmd/daemon/cmd.go b/cmd/daemon/cmd.go index ec5e95b..40f58d6 100644 --- a/cmd/daemon/cmd.go +++ b/cmd/daemon/cmd.go @@ -14,7 +14,7 @@ import ( "time" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "forge.lthn.ai/core/go-ai/mcp" ) diff --git a/cmd/metrics/cmd.go b/cmd/metrics/cmd.go index c832f80..220c83f 100644 --- a/cmd/metrics/cmd.go +++ b/cmd/metrics/cmd.go @@ -8,7 +8,7 @@ import ( "forge.lthn.ai/core/go-ai/ai" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" + "forge.lthn.ai/core/go-i18n" ) var ( diff --git a/cmd/security/cmd_alerts.go b/cmd/security/cmd_alerts.go index 42add97..1f79077 100644 --- a/cmd/security/cmd_alerts.go +++ b/cmd/security/cmd_alerts.go @@ -5,7 +5,7 @@ import ( "fmt" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" + "forge.lthn.ai/core/go-i18n" ) func addAlertsCommand(parent *cli.Command) { diff --git a/cmd/security/cmd_deps.go b/cmd/security/cmd_deps.go index 16e6474..6399856 100644 --- a/cmd/security/cmd_deps.go +++ b/cmd/security/cmd_deps.go @@ -5,7 +5,7 @@ import ( "fmt" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" + "forge.lthn.ai/core/go-i18n" ) func addDepsCommand(parent *cli.Command) { diff --git a/cmd/security/cmd_jobs.go b/cmd/security/cmd_jobs.go index 607c506..a7a7149 100644 --- a/cmd/security/cmd_jobs.go +++ b/cmd/security/cmd_jobs.go @@ -9,7 +9,7 @@ import ( "forge.lthn.ai/core/cli/pkg/cli" "forge.lthn.ai/core/go-ai/ai" - "forge.lthn.ai/core/go/pkg/i18n" + "forge.lthn.ai/core/go-i18n" ) var ( diff --git a/cmd/security/cmd_scan.go b/cmd/security/cmd_scan.go index 9027847..cd5e706 100644 --- a/cmd/security/cmd_scan.go +++ b/cmd/security/cmd_scan.go @@ -7,7 +7,7 @@ import ( "forge.lthn.ai/core/go-ai/ai" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" + "forge.lthn.ai/core/go-i18n" ) var ( diff --git a/cmd/security/cmd_secrets.go b/cmd/security/cmd_secrets.go index 314cde7..f7e1653 100644 --- a/cmd/security/cmd_secrets.go +++ b/cmd/security/cmd_secrets.go @@ -5,7 +5,7 @@ import ( "fmt" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" + "forge.lthn.ai/core/go-i18n" ) func addSecretsCommand(parent *cli.Command) { diff --git a/cmd/security/cmd_security.go b/cmd/security/cmd_security.go index 7463515..8569b7e 100644 --- a/cmd/security/cmd_security.go +++ b/cmd/security/cmd_security.go @@ -8,8 +8,8 @@ import ( "strings" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" - "forge.lthn.ai/core/go/pkg/io" + "forge.lthn.ai/core/go-i18n" + "forge.lthn.ai/core/go-io" "forge.lthn.ai/core/go/pkg/repos" ) diff --git a/go.mod b/go.mod index 07c5ee2..47b9844 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,13 @@ require ( forge.lthn.ai/core/cli v0.1.0 forge.lthn.ai/core/go v0.1.0 forge.lthn.ai/core/go-api v0.1.0 + forge.lthn.ai/core/go-i18n v0.0.3 forge.lthn.ai/core/go-inference v0.1.0 + forge.lthn.ai/core/go-io v0.0.1 + forge.lthn.ai/core/go-log v0.0.1 forge.lthn.ai/core/go-ml v0.1.0 forge.lthn.ai/core/go-rag v0.1.0 github.com/gin-gonic/gin v1.11.0 - github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/modelcontextprotocol/go-sdk v1.3.0 github.com/stretchr/testify v1.11.1 @@ -83,6 +85,7 @@ require ( github.com/goccy/go-yaml v1.19.2 // indirect github.com/google/flatbuffers v25.12.19+incompatible // indirect github.com/google/jsonschema-go v0.4.2 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.4.0 // indirect diff --git a/go.sum b/go.sum index 01cb2de..14ecd8b 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,14 @@ forge.lthn.ai/core/go-api v0.1.0 h1:j4cn/cpDkSecN2TNcksmqtFKlA9R61VSkbIIwSiujaU= forge.lthn.ai/core/go-api v0.1.0/go.mod h1:ECZtO2R6ZYF80tBBedhmkEUw6Oqjs5jM34+XpdeZUEw= forge.lthn.ai/core/go-crypt v0.1.0 h1:92gwdQi7iAwktpvZhL/8Cu+QS6xKCtGP4FJfyInPGnw= forge.lthn.ai/core/go-crypt v0.1.0/go.mod h1:zVAgx6ZiGtC+dbX4R/VKvEPqsEqjyuLl4gQZH9SXBUw= +forge.lthn.ai/core/go-i18n v0.0.3 h1:et3NkErxSIGxwj8rAK86UU56gYJWXSy66KZm/H4vld8= +forge.lthn.ai/core/go-i18n v0.0.3/go.mod h1:Q4xsrxuNCl/6NfMv1daria7t1RSiyy8ml+6jiPtUcBs= forge.lthn.ai/core/go-inference v0.1.0 h1:pO7etYgqV8LMKFdpW8/2RWncuECZJCIcf8nnezeZ5R4= forge.lthn.ai/core/go-inference v0.1.0/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw= +forge.lthn.ai/core/go-io v0.0.1 h1:N/GCl6Asusfr4gs53JZixJVtqcnerQ6GcxSN8F8iJXY= +forge.lthn.ai/core/go-io v0.0.1/go.mod h1:l+gG/G5TMIOTG8G7y0dg4fh1a7Suy8wCYVwsz4duV7M= +forge.lthn.ai/core/go-log v0.0.1 h1:x/E6EfF9vixzqiLHQOl2KT25HyBcMc9qiBkomqVlpPg= +forge.lthn.ai/core/go-log v0.0.1/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw= forge.lthn.ai/core/go-ml v0.1.0 h1:nV/XHZMy9VaFhk2dCYW5Jnp5UqpYVsYg85bsKMqdu8o= forge.lthn.ai/core/go-ml v0.1.0/go.mod h1:FPV9JhIUOZdLeJpX1ggC15BpmM740NPg6rycnOc5vss= forge.lthn.ai/core/go-mlx v0.1.0 h1:nMDhMma3M9iSm2ymNyqMe+aAbJDasNnxgi/1dZ+Zq7c= diff --git a/mcp/mcp.go b/mcp/mcp.go index d386627..bb858c5 100644 --- a/mcp/mcp.go +++ b/mcp/mcp.go @@ -15,8 +15,8 @@ import ( "slices" "strings" - "forge.lthn.ai/core/go/pkg/io" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-io" + "forge.lthn.ai/core/go-log" "forge.lthn.ai/core/go/pkg/process" "forge.lthn.ai/core/go/pkg/ws" "github.com/modelcontextprotocol/go-sdk/mcp" diff --git a/mcp/tools_metrics.go b/mcp/tools_metrics.go index fb123b1..d0e3811 100644 --- a/mcp/tools_metrics.go +++ b/mcp/tools_metrics.go @@ -9,7 +9,7 @@ import ( "time" "forge.lthn.ai/core/go-ai/ai" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/tools_ml.go b/mcp/tools_ml.go index 1be698e..55a0f08 100644 --- a/mcp/tools_ml.go +++ b/mcp/tools_ml.go @@ -8,7 +8,7 @@ import ( "forge.lthn.ai/core/go-inference" "forge.lthn.ai/core/go-ml" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/tools_ml_test.go b/mcp/tools_ml_test.go index 81fa3a2..55c0f71 100644 --- a/mcp/tools_ml_test.go +++ b/mcp/tools_ml_test.go @@ -9,7 +9,7 @@ import ( "forge.lthn.ai/core/go-inference" "forge.lthn.ai/core/go-ml" "forge.lthn.ai/core/go/pkg/framework" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" ) // --- Mock backend for inference registry --- diff --git a/mcp/tools_process.go b/mcp/tools_process.go index 536f67a..76f9ac8 100644 --- a/mcp/tools_process.go +++ b/mcp/tools_process.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "forge.lthn.ai/core/go/pkg/process" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/tools_rag.go b/mcp/tools_rag.go index a80df59..89499f1 100644 --- a/mcp/tools_rag.go +++ b/mcp/tools_rag.go @@ -6,7 +6,7 @@ import ( "fmt" "forge.lthn.ai/core/go-rag" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/tools_webview.go b/mcp/tools_webview.go index b1672b1..2dd3101 100644 --- a/mcp/tools_webview.go +++ b/mcp/tools_webview.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "forge.lthn.ai/core/go/pkg/webview" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/tools_ws.go b/mcp/tools_ws.go index d1377fe..80711cf 100644 --- a/mcp/tools_ws.go +++ b/mcp/tools_ws.go @@ -6,7 +6,7 @@ import ( "net" "net/http" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "forge.lthn.ai/core/go/pkg/ws" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/transport_stdio.go b/mcp/transport_stdio.go index b91fc3a..10ea27c 100644 --- a/mcp/transport_stdio.go +++ b/mcp/transport_stdio.go @@ -3,7 +3,7 @@ package mcp import ( "context" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/transport_unix.go b/mcp/transport_unix.go index aea4c2d..c70d5d9 100644 --- a/mcp/transport_unix.go +++ b/mcp/transport_unix.go @@ -5,7 +5,7 @@ import ( "net" "os" - "forge.lthn.ai/core/go/pkg/log" + "forge.lthn.ai/core/go-log" ) // ServeUnix starts a Unix domain socket server for the MCP service.