Files
beads/internal/utils/path.go
Steve Yegge 52135d0370 feat(types): add template molecules infrastructure for beads-1ra
Add support for template molecules (is_template field and TypeMolecule type):

- Add IsTemplate field to Issue type with JSON support
- Add TypeMolecule constant to IssueType constants
- Add IsTemplate filter to IssueFilter for querying
- Update all SQL queries to include is_template column
- Add migration 024 for is_template column
- Add FindMoleculesJSONLInDir helper for molecules.jsonl path detection

This enables treating certain issues as read-only templates that can be
instantiated to create work items. The template flag allows separating
template molecules from regular work items in queries and exports.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 22:03:22 -08:00

99 lines
3.1 KiB
Go

// Package utils provides utility functions for issue ID parsing and path handling.
package utils
import (
"os"
"path/filepath"
)
// FindJSONLInDir finds the JSONL file in the given .beads directory.
// It prefers issues.jsonl over other .jsonl files to prevent accidentally
// reading/writing to deletions.jsonl or merge artifacts (bd-tqo fix).
// Always returns a path (defaults to issues.jsonl if nothing suitable found).
//
// Search order:
// 1. issues.jsonl (canonical name)
// 2. beads.jsonl (legacy support)
// 3. Any other .jsonl file except deletions/merge artifacts
// 4. Default to issues.jsonl
func FindJSONLInDir(dbDir string) string {
pattern := filepath.Join(dbDir, "*.jsonl")
matches, err := filepath.Glob(pattern)
if err != nil || len(matches) == 0 {
// Default to issues.jsonl if glob fails or no matches
return filepath.Join(dbDir, "issues.jsonl")
}
// Prefer issues.jsonl over other .jsonl files (bd-tqo fix)
// This prevents accidentally using deletions.jsonl or merge artifacts
for _, match := range matches {
if filepath.Base(match) == "issues.jsonl" {
return match
}
}
// Fall back to beads.jsonl for legacy support
for _, match := range matches {
if filepath.Base(match) == "beads.jsonl" {
return match
}
}
// Last resort: use first match (but skip deletions.jsonl and merge artifacts)
for _, match := range matches {
base := filepath.Base(match)
// Skip deletions manifest and merge artifacts
if base == "deletions.jsonl" ||
base == "beads.base.jsonl" ||
base == "beads.left.jsonl" ||
base == "beads.right.jsonl" {
continue
}
return match
}
// If only deletions/merge files exist, default to issues.jsonl
return filepath.Join(dbDir, "issues.jsonl")
}
// FindMoleculesJSONLInDir finds the molecules.jsonl file in the given .beads directory.
// Returns the path to molecules.jsonl if it exists, empty string otherwise.
// Molecules are template issues used for instantiation (beads-1ra).
func FindMoleculesJSONLInDir(dbDir string) string {
moleculesPath := filepath.Join(dbDir, "molecules.jsonl")
// Check if file exists - we don't fall back to any other file
// because molecules.jsonl is optional and specific
if _, err := os.Stat(moleculesPath); err == nil {
return moleculesPath
}
return ""
}
// CanonicalizePath converts a path to its canonical form by:
// 1. Converting to absolute path
// 2. Resolving symlinks
//
// If either step fails, it falls back to the best available form:
// - If symlink resolution fails, returns absolute path
// - If absolute path conversion fails, returns original path
//
// This function is used to ensure consistent path handling across the codebase,
// particularly for BEADS_DIR environment variable processing.
func CanonicalizePath(path string) string {
// Try to get absolute path
absPath, err := filepath.Abs(path)
if err != nil {
// If we can't get absolute path, return original
return path
}
// Try to resolve symlinks
canonical, err := filepath.EvalSymlinks(absPath)
if err != nil {
// If we can't resolve symlinks, return absolute path
return absPath
}
return canonical
}