fix pre delete dirmd set dir bug and implement last git sha and commit#
This commit is contained in:
@@ -143,6 +143,7 @@ Example output with `dirmd -o docs.md ~/repos/myproject --vertical-slices`:
|
|||||||
Output is designed for RAG ingestion and LLM context windows, not human presentation:
|
Output is designed for RAG ingestion and LLM context windows, not human presentation:
|
||||||
|
|
||||||
- **ISO 8601 Timestamp**: Added as a metadata line (`> Generated: ...`) immediately after the H1 header for every new file created.
|
- **ISO 8601 Timestamp**: Added as a metadata line (`> Generated: ...`) immediately after the H1 header for every new file created.
|
||||||
|
- **Git Commit Info**: If the source directory is a Git repository, a second metadata line (`> Commit: <sha> (<message>)`) is added below the timestamp.
|
||||||
- **ASCII tree format** (`+-` instead of Unicode box-drawing) to reduce token overhead
|
- **ASCII tree format** (`+-` instead of Unicode box-drawing) to reduce token overhead
|
||||||
- **Explicit file delimiters**: `--- FILE: ./path (bytes, lines) ---` and `--- END FILE ---`
|
- **Explicit file delimiters**: `--- FILE: ./path (bytes, lines) ---` and `--- END FILE ---`
|
||||||
- **Document type markers**: `[ROOT]` and `[SLICE]` in H1 headers
|
- **Document type markers**: `[ROOT]` and `[SLICE]` in H1 headers
|
||||||
@@ -230,6 +231,7 @@ Example output structure (non-sliced mode):
|
|||||||
|
|
||||||
# /home/user/repos/myproject [ROOT]
|
# /home/user/repos/myproject [ROOT]
|
||||||
> Generated: 2026-09-02T14:30:00Z
|
> Generated: 2026-09-02T14:30:00Z
|
||||||
|
> Commit: a1b2c3d (Fix login bug)
|
||||||
|
|
||||||
## Directory Tree
|
## Directory Tree
|
||||||
|
|
||||||
|
|||||||
+11
-4
@@ -30,7 +30,7 @@ Examples:
|
|||||||
# Create new documentation (writes index.md inside ./docs/)
|
# Create new documentation (writes index.md inside ./docs/)
|
||||||
dirmd -o ./docs ~/repos/myproject
|
dirmd -o ./docs ~/repos/myproject
|
||||||
|
|
||||||
# Overwrite existing output directory
|
# Overwrite existing output directory (DELETES everything in it first)
|
||||||
dirmd -o ./docs ~/repos/myproject -f
|
dirmd -o ./docs ~/repos/myproject -f
|
||||||
|
|
||||||
# Append a single file
|
# Append a single file
|
||||||
@@ -86,7 +86,7 @@ func Execute() {
|
|||||||
func init() {
|
func init() {
|
||||||
rootCmd.PersistentFlags().StringVarP(&outputPath, "output", "o", "", "output directory (required)")
|
rootCmd.PersistentFlags().StringVarP(&outputPath, "output", "o", "", "output directory (required)")
|
||||||
rootCmd.Flags().BoolVarP(&appendMode, "append", "a", false, "append to existing index.md")
|
rootCmd.Flags().BoolVarP(&appendMode, "append", "a", false, "append to existing index.md")
|
||||||
rootCmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite existing output directory")
|
rootCmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite existing output directory (deletes all contents)")
|
||||||
rootCmd.Flags().StringVar(&singleFile, "input-file", "", "single file input (append mode only)")
|
rootCmd.Flags().StringVar(&singleFile, "input-file", "", "single file input (append mode only)")
|
||||||
rootCmd.Flags().BoolVarP(&tuiMode, "interactive", "i", false, "launch interactive TUI configuration wizard")
|
rootCmd.Flags().BoolVarP(&tuiMode, "interactive", "i", false, "launch interactive TUI configuration wizard")
|
||||||
rootCmd.Flags().Int64Var(&maxSize, "max-size", config.DefaultMaxSize, "max file size in bytes")
|
rootCmd.Flags().Int64Var(&maxSize, "max-size", config.DefaultMaxSize, "max file size in bytes")
|
||||||
@@ -173,9 +173,16 @@ func run(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("failed to resolve output path: %w", err)
|
return fmt.Errorf("failed to resolve output path: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle output directory existence
|
||||||
if !appendMode {
|
if !appendMode {
|
||||||
if _, err := os.Stat(absOutput); err == nil && !force {
|
if _, err := os.Stat(absOutput); err == nil {
|
||||||
return fmt.Errorf("output directory %s already exists (use -f to overwrite)", absOutput)
|
if !force {
|
||||||
|
return fmt.Errorf("output directory %s already exists (use -f to overwrite)", absOutput)
|
||||||
|
}
|
||||||
|
// FORCE MODE: Always delete the entire directory and recreate it fresh
|
||||||
|
if err := os.RemoveAll(absOutput); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove existing output directory: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommitInfo struct {
|
||||||
|
ShortSHA string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommitInfo(rootDir string) *CommitInfo {
|
||||||
|
cmd := exec.Command("git", "-C", rootDir, "log", "-1", "--format=%h %s")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(strings.TrimSpace(string(output)), " ", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CommitInfo{
|
||||||
|
ShortSHA: parts[0],
|
||||||
|
Message: parts[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatCommitLine(info *CommitInfo) string {
|
||||||
|
if info == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("> Commit: %s (%s)\n", info.ShortSHA, info.Message)
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"git.flo.fo/FLO/dirmd/internal/git"
|
||||||
"git.flo.fo/FLO/dirmd/internal/tree"
|
"git.flo.fo/FLO/dirmd/internal/tree"
|
||||||
"git.flo.fo/FLO/dirmd/internal/walker"
|
"git.flo.fo/FLO/dirmd/internal/walker"
|
||||||
)
|
)
|
||||||
@@ -38,9 +39,10 @@ func Write(result *walker.Result, outputPath string, appendMode bool, singleFile
|
|||||||
|
|
||||||
fmt.Fprintf(&sb, "# %s [ROOT]\n", h1Path)
|
fmt.Fprintf(&sb, "# %s [ROOT]\n", h1Path)
|
||||||
|
|
||||||
// Add timestamp only if creating a new file (not append mode)
|
|
||||||
if !appendMode {
|
if !appendMode {
|
||||||
fmt.Fprintf(&sb, "> Generated: %s\n\n", time.Now().Format(time.RFC3339))
|
fmt.Fprintf(&sb, "> Generated: %s\n", time.Now().Format(time.RFC3339))
|
||||||
|
sb.WriteString(git.FormatCommitLine(result.Commit))
|
||||||
|
sb.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !singleFile {
|
if !singleFile {
|
||||||
@@ -116,7 +118,7 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e
|
|||||||
rootPath := filepath.Join(outputDir, indexBasename)
|
rootPath := filepath.Join(outputDir, indexBasename)
|
||||||
generated = append(generated, rootPath)
|
generated = append(generated, rootPath)
|
||||||
|
|
||||||
if err := writeRootSlice(rootPath, absRoot, rootName, repoName, rootFiles, topLevelDirs, result.OmitTest); err != nil {
|
if err := writeRootSlice(rootPath, absRoot, rootName, repoName, rootFiles, topLevelDirs, result.OmitTest, result.Commit); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write root slice: %w", err)
|
return nil, fmt.Errorf("failed to write root slice: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +153,7 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writeDirSlice(slicePath, absDir, absRoot, dirName, treeEntries, dirEntries, indexBasename, result.OmitTest); err != nil {
|
if err := writeDirSlice(slicePath, absDir, absRoot, dirName, treeEntries, dirEntries, indexBasename, result.OmitTest, result.Commit); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write slice %s: %w", sliceName, err)
|
return nil, fmt.Errorf("failed to write slice %s: %w", sliceName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +161,7 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e
|
|||||||
return generated, nil
|
return generated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker.Entry, topLevelDirs map[string][]walker.Entry, omitTest bool) error {
|
func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker.Entry, topLevelDirs map[string][]walker.Entry, omitTest bool, commit *git.CommitInfo) error {
|
||||||
f, err := os.Create(path)
|
f, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create root file: %w", err)
|
return fmt.Errorf("cannot create root file: %w", err)
|
||||||
@@ -169,8 +171,9 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker
|
|||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
|
||||||
fmt.Fprintf(&sb, "# %s [ROOT]\n", absRoot)
|
fmt.Fprintf(&sb, "# %s [ROOT]\n", absRoot)
|
||||||
// Timestamp for new file creation
|
fmt.Fprintf(&sb, "> Generated: %s\n", time.Now().Format(time.RFC3339))
|
||||||
fmt.Fprintf(&sb, "> Generated: %s\n\n", time.Now().Format(time.RFC3339))
|
sb.WriteString(git.FormatCommitLine(commit))
|
||||||
|
sb.WriteString("\n")
|
||||||
|
|
||||||
treeLines := buildTreeWithRefs(rootName, repoName, topLevelDirs, rootFiles)
|
treeLines := buildTreeWithRefs(rootName, repoName, topLevelDirs, rootFiles)
|
||||||
sb.WriteString("## Directory Tree\n\n")
|
sb.WriteString("## Directory Tree\n\n")
|
||||||
@@ -238,7 +241,7 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntries []walker.Entry, rootFileName string, omitTest bool) error {
|
func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntries []walker.Entry, rootFileName string, omitTest bool, commit *git.CommitInfo) error {
|
||||||
f, err := os.Create(path)
|
f, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create slice file: %w", err)
|
return fmt.Errorf("cannot create slice file: %w", err)
|
||||||
@@ -248,8 +251,8 @@ func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntri
|
|||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
|
||||||
fmt.Fprintf(&sb, "# %s [SLICE]\n", absDir)
|
fmt.Fprintf(&sb, "# %s [SLICE]\n", absDir)
|
||||||
// Timestamp for new file creation
|
fmt.Fprintf(&sb, "> Generated: %s\n", time.Now().Format(time.RFC3339))
|
||||||
fmt.Fprintf(&sb, "> Generated: %s\n\n", time.Now().Format(time.RFC3339))
|
sb.WriteString(git.FormatCommitLine(commit))
|
||||||
fmt.Fprintf(&sb, "Root document: %s\n\n", rootFileName)
|
fmt.Fprintf(&sb, "Root document: %s\n\n", rootFileName)
|
||||||
|
|
||||||
sb.WriteString("## Directory Tree\n\n")
|
sb.WriteString("## Directory Tree\n\n")
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"git.flo.fo/FLO/dirmd/internal/config"
|
"git.flo.fo/FLO/dirmd/internal/config"
|
||||||
"git.flo.fo/FLO/dirmd/internal/filter"
|
"git.flo.fo/FLO/dirmd/internal/filter"
|
||||||
|
"git.flo.fo/FLO/dirmd/internal/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
@@ -21,6 +22,7 @@ type Result struct {
|
|||||||
AbsRoot string
|
AbsRoot string
|
||||||
SingleFile bool
|
SingleFile bool
|
||||||
OmitTest bool
|
OmitTest bool
|
||||||
|
Commit *git.CommitInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(cfg *config.Config) (*Result, error) {
|
func Run(cfg *config.Config) (*Result, error) {
|
||||||
@@ -53,11 +55,15 @@ func processSingleFile(cfg *config.Config) (*Result, error) {
|
|||||||
return nil, fmt.Errorf("file %s filtered out", cfg.AbsRoot)
|
return nil, fmt.Errorf("file %s filtered out", cfg.AbsRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For single files, try to get git info from the parent directory
|
||||||
|
commitInfo := git.GetCommitInfo(filepath.Dir(cfg.AbsRoot))
|
||||||
|
|
||||||
return &Result{
|
return &Result{
|
||||||
Entries: []Entry{{RelPath: filepath.Base(cfg.AbsRoot), Size: info.Size()}},
|
Entries: []Entry{{RelPath: filepath.Base(cfg.AbsRoot), Size: info.Size()}},
|
||||||
AbsRoot: filepath.Dir(cfg.AbsRoot),
|
AbsRoot: filepath.Dir(cfg.AbsRoot),
|
||||||
SingleFile: true,
|
SingleFile: true,
|
||||||
OmitTest: cfg.OmitTest,
|
OmitTest: cfg.OmitTest,
|
||||||
|
Commit: commitInfo,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +79,9 @@ func walkDirectory(cfg *config.Config) (*Result, error) {
|
|||||||
|
|
||||||
result := &Result{AbsRoot: cfg.AbsRoot, OmitTest: cfg.OmitTest}
|
result := &Result{AbsRoot: cfg.AbsRoot, OmitTest: cfg.OmitTest}
|
||||||
|
|
||||||
|
// Fetch git info for the root directory
|
||||||
|
result.Commit = git.GetCommitInfo(cfg.AbsRoot)
|
||||||
|
|
||||||
err := filepath.WalkDir(cfg.AbsRoot, func(path string, d fs.DirEntry, err error) error {
|
err := filepath.WalkDir(cfg.AbsRoot, func(path string, d fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "warning: %v\n", err)
|
fmt.Fprintf(os.Stderr, "warning: %v\n", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user