From a1638bd9441e131e39820e8051a43b3f575dedab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartal=20L=C3=A6arsson?= Date: Wed, 2 Sep 2026 12:01:42 +0100 Subject: [PATCH] add .dirmd and other metadata --- .gitignore | 1 + README.md | 20 ++++ cmd/root.go | 61 ++++------- internal/config/config.go | 140 +++++++++++--------------- internal/instructions/instructions.go | 41 ++++++++ internal/renderer/renderer.go | 59 +++++------ internal/tui/run.go | 31 +++--- internal/walker/walker.go | 40 +++++--- 8 files changed, 213 insertions(+), 180 deletions(-) create mode 100644 internal/instructions/instructions.go diff --git a/.gitignore b/.gitignore index 53770bd..a76f2fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ bin/ .env vendor/ +.dirmd diff --git a/README.md b/README.md index 0e2f095..7d2a889 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,26 @@ Example output with `dirmd -o docs.md ~/repos/myproject --vertical-slices`: ├── cmd.md ← slice: tree + contents of cmd/ └── internal.md ← slice: tree + contents of internal/ +## Instructions File (`.dirmd`) + +If a file named `.dirmd` exists in the root of the input directory, you can include its contents as metadata in the generated output by passing the `--instructions` flag. + +This is useful for injecting specific context, constraints, or instructions for LLMs processing the snapshot. + +- **File Name**: `.dirmd` +- **Location**: Root of the input directory. +- **Format**: Plain text or Markdown. +- **Output**: Rendered as a quoted block (`> Instructions: ...`) immediately after the timestamp and commit info. +- **Opt-in**: The file is **ignored** unless `--instructions` is explicitly passed. + +Example `.dirmd`: + +```text +Focus on the `internal/` package for business logic. +Ignore `cmd/` unless explicitly asked. +Prioritize Go files over Python. +``` + ## LLM-Optimized Output Output is designed for RAG ingestion and LLM context windows, not human presentation: diff --git a/cmd/root.go b/cmd/root.go index 4c71c39..a9cdc1d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,33 +27,24 @@ Output is always written to a directory specified by -o. Examples: - # Create new documentation (writes index.md inside ./docs/) + # Create new documentation dirmd -o ./docs ~/repos/myproject - # Overwrite existing output directory (DELETES everything in it first) + # Overwrite existing output directory (deletes all contents) dirmd -o ./docs ~/repos/myproject -f # Append a single file dirmd -a -i README.md -o ./docs - # Vertical slices (root index.md + one markdown per top-level dir) + # Vertical slices dirmd -o ./docs ~/repos/myproject --vertical-slices - # Skip frontend files - dirmd -o ./docs ~/repos/myproject --skip-frontend - - # Frontend only - dirmd -o ./docs ~/repos/myproject --frontend-only - - # Omit test files and test blocks - dirmd -o ./docs ~/repos/myproject --omit-test + # Include instructions from .dirmd + dirmd -o ./docs ~/repos/myproject --instructions # Generate and upload to Proton Drive dirmd -o ./docs ~/repos/myproject --proton-drive - # Generate and upload to a specific remote path - dirmd -o ./docs ~/repos/myproject --proton-drive --drive-path /my-files/docs/api - # Interactive TUI mode dirmd --tui`, RunE: run, @@ -75,6 +66,7 @@ Examples: omitTest bool omitMd bool tuiMode bool + instructions bool ) func Execute() { @@ -100,6 +92,7 @@ func init() { rootCmd.Flags().BoolVar(&frontendOnly, "frontend-only", false, "include only frontend file types and README.md") rootCmd.Flags().BoolVar(&omitTest, "omit-test", false, "exclude test files (Go *_test.go, Java *Test.java) and strip #[cfg(test)] blocks from Rust") rootCmd.Flags().BoolVar(&omitMd, "omit-md", false, "omit all .md files except README.md") + rootCmd.Flags().BoolVar(&instructions, "instructions", false, "include .dirmd instructions file if present in root") } func run(cmd *cobra.Command, args []string) error { @@ -133,20 +126,21 @@ func run(cmd *cobra.Command, args []string) error { } cfg := &config.Config{ - OutputPath: outputPath, - AppendMode: appendMode, - Force: force, - MaxSize: maxSize, - MaxDepth: maxDepth, - Ignores: append(config.DefaultIgnores, ignorePatterns...), - Extensions: excludedExts, - ProtonDrive: protonDrive, - DrivePath: drivePath, - VerticalSlices: verticalSlices, - SkipFrontend: skipFrontend, - FrontendOnly: frontendOnly, - OmitTest: omitTest, - OmitMd: omitMd, + OutputPath: outputPath, + AppendMode: appendMode, + Force: force, + MaxSize: maxSize, + MaxDepth: maxDepth, + Ignores: append(config.DefaultIgnores, ignorePatterns...), + Extensions: excludedExts, + ProtonDrive: protonDrive, + DrivePath: drivePath, + VerticalSlices: verticalSlices, + SkipFrontend: skipFrontend, + FrontendOnly: frontendOnly, + OmitTest: omitTest, + OmitMd: omitMd, + IncludeInstructions: instructions, } if singleFile != "" { @@ -173,13 +167,11 @@ func run(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to resolve output path: %w", err) } - // Handle output directory existence if !appendMode { if _, err := os.Stat(absOutput); err == nil { 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) } @@ -209,7 +201,6 @@ func run(cmd *cobra.Command, args []string) error { if err != nil { return err } - fmt.Fprintf(os.Stderr, "generated %d files in %s\n", len(generated), absOutput) if protonDrive { @@ -217,27 +208,22 @@ func run(cmd *cobra.Command, args []string) error { if remoteDir == "" { remoteDir = "/my-files/md/" + filepath.Base(absOutput) } - if err := drive.CheckAuth(); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) fmt.Fprintln(os.Stderr, "local files were written successfully, but upload was skipped") return err } - if err := drive.UploadDir(absOutput, remoteDir); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) fmt.Fprintln(os.Stderr, "local files were written successfully, but upload failed") return err } - fmt.Fprintf(os.Stderr, "uploaded %s to %s on Proton Drive\n", absOutput, remoteDir) } - return nil } indexPath := filepath.Join(absOutput, indexBasename) - if err := renderer.Write(result, indexPath, appendMode, cfg.SingleFile); err != nil { return err } @@ -249,19 +235,16 @@ func run(cmd *cobra.Command, args []string) error { if remoteDir == "" { remoteDir = "/my-files/md/" + filepath.Base(absOutput) } - if err := drive.CheckAuth(); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) fmt.Fprintln(os.Stderr, "local file was written successfully, but upload was skipped") return err } - if err := drive.UploadDir(absOutput, remoteDir); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) fmt.Fprintln(os.Stderr, "local file was written successfully, but upload failed") return err } - fmt.Fprintf(os.Stderr, "uploaded %s to %s on Proton Drive\n", absOutput, remoteDir) } diff --git a/internal/config/config.go b/internal/config/config.go index a3c4bb6..670e370 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,22 +15,9 @@ const ( ) var DefaultIgnores = []string{ - ".git", - "node_modules", - "vendor", - "bin", - "go.mod", - "go.sum", - "target", - "build", - "dist", - "Cargo.lock", - ".idea", - ".vscode", - "dependency-reduced-pom.xml", - "__pycache__", - "venv", - "env", + ".git", "node_modules", "vendor", "bin", "go.mod", "go.sum", + "target", "build", "dist", "Cargo.lock", ".idea", ".vscode", + "dependency-reduced-pom.xml", "__pycache__", "venv", "env", } var DefaultExts = []string{ @@ -42,10 +29,7 @@ var DefaultExts = []string{ ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".pem", ".key", ".cert", ".db", ".sqlite", ".sqlite3", - ".class", - ".jar", - ".war", - ".ear", + ".class", ".jar", ".war", ".ear", ".pyc", ".pyo", } @@ -59,33 +43,28 @@ var FrontendExts = []string{ type stringSlice []string -func (s *stringSlice) String() string { - return strings.Join(*s, ", ") -} - -func (s *stringSlice) Set(v string) error { - *s = append(*s, v) - return nil -} +func (s *stringSlice) String() string { return strings.Join(*s, ", ") } +func (s *stringSlice) Set(v string) error { *s = append(*s, v); return nil } type Config struct { - InputPath string - OutputPath string - AppendMode bool - Force bool - SingleFile bool - MaxSize int64 - MaxDepth int - Ignores []string - Extensions []string - AbsRoot string - ProtonDrive bool - DrivePath string - VerticalSlices bool - SkipFrontend bool - FrontendOnly bool - OmitTest bool - OmitMd bool + InputPath string + OutputPath string + AppendMode bool + Force bool + SingleFile bool + MaxSize int64 + MaxDepth int + Ignores []string + Extensions []string + AbsRoot string + ProtonDrive bool + DrivePath string + VerticalSlices bool + SkipFrontend bool + FrontendOnly bool + OmitTest bool + OmitMd bool + IncludeInstructions bool } func PrintUsage() { @@ -98,22 +77,23 @@ func PrintUsage() { func Parse() (*Config, error) { var ( - input string - output string - appendMd bool - force bool - maxSize int64 - maxDepth int - ignores stringSlice - exts stringSlice - showVer bool - protonDrive bool - drivePath string - verticalSlices bool - skipFrontend bool - frontendOnly bool - omitTest bool - omitMd bool + input string + output string + appendMd bool + force bool + maxSize int64 + maxDepth int + ignores stringSlice + exts stringSlice + showVer bool + protonDrive bool + drivePath string + verticalSlices bool + skipFrontend bool + frontendOnly bool + omitTest bool + omitMd bool + includeInstructions bool ) flag.StringVar(&input, "i", "", "single file input (append mode only)") @@ -128,10 +108,11 @@ func Parse() (*Config, error) { flag.BoolVar(&protonDrive, "proton-drive", false, "upload output to Proton Drive after writing locally") flag.StringVar(&drivePath, "drive-path", "", "full remote path on Proton Drive (requires --proton-drive") flag.BoolVar(&verticalSlices, "vertical-slices", false, "split output into root file + one markdown per top-level directory") - flag.BoolVar(&skipFrontend, "skip-frontend", false, "omit frontend file types (.html, .css, .js, .ts, .vue, .svelte, etc.)") + flag.BoolVar(&skipFrontend, "skip-frontend", false, "omit frontend file types") flag.BoolVar(&frontendOnly, "frontend-only", false, "include only frontend file types and README.md") - flag.BoolVar(&omitTest, "omit-test", false, "exclude test files (Go *_test.go, Java *Test.java) and strip #[cfg(test)] blocks from Rust") + flag.BoolVar(&omitTest, "omit-test", false, "exclude test files and strip #[cfg(test)] blocks") flag.BoolVar(&omitMd, "omit-md", false, "omit all .md files except README.md") + flag.BoolVar(&includeInstructions, "instructions", false, "include .dirmd instructions file if present in root") flag.Usage = PrintUsage flag.Parse() @@ -144,19 +125,15 @@ func Parse() (*Config, error) { if output == "" { return nil, fmt.Errorf("-o is required") } - if drivePath != "" && !protonDrive { return nil, fmt.Errorf("--drive-path requires --proton-drive") } - if verticalSlices && appendMd { return nil, fmt.Errorf("--vertical-slices cannot be used with --append") } - if verticalSlices && input != "" { return nil, fmt.Errorf("--vertical-slices cannot be used with --input-file") } - if skipFrontend && frontendOnly { return nil, fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive") } @@ -167,20 +144,21 @@ func Parse() (*Config, error) { } cfg := &Config{ - OutputPath: output, - AppendMode: appendMd, - Force: force, - MaxSize: maxSize, - MaxDepth: maxDepth, - Ignores: append(DefaultIgnores, ignores...), - Extensions: excludedExts, - ProtonDrive: protonDrive, - DrivePath: drivePath, - VerticalSlices: verticalSlices, - SkipFrontend: skipFrontend, - FrontendOnly: frontendOnly, - OmitTest: omitTest, - OmitMd: omitMd, + OutputPath: output, + AppendMode: appendMd, + Force: force, + MaxSize: maxSize, + MaxDepth: maxDepth, + Ignores: append(DefaultIgnores, ignores...), + Extensions: excludedExts, + ProtonDrive: protonDrive, + DrivePath: drivePath, + VerticalSlices: verticalSlices, + SkipFrontend: skipFrontend, + FrontendOnly: frontendOnly, + OmitTest: omitTest, + OmitMd: omitMd, + IncludeInstructions: includeInstructions, } if input != "" { diff --git a/internal/instructions/instructions.go b/internal/instructions/instructions.go new file mode 100644 index 0000000..6e1d70a --- /dev/null +++ b/internal/instructions/instructions.go @@ -0,0 +1,41 @@ +package instructions + +import ( + "os" + "path/filepath" + "strings" +) + +const FileName = ".dirmd" + +func Read(rootDir string) (string, error) { + path := filepath.Join(rootDir, FileName) + data, err := os.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + return "", nil + } + return "", err + } + return string(data), nil +} + +func Format(content string) string { + if content == "" { + return "" + } + content = strings.TrimSpace(content) + if content == "" { + return "" + } + lines := strings.Split(content, "\n") + var sb strings.Builder + sb.WriteString("> Instructions:\n") + for _, line := range lines { + sb.WriteString("> ") + sb.WriteString(line) + sb.WriteString("\n") + } + sb.WriteString("\n") + return sb.String() +} diff --git a/internal/renderer/renderer.go b/internal/renderer/renderer.go index a9aa397..3707020 100644 --- a/internal/renderer/renderer.go +++ b/internal/renderer/renderer.go @@ -10,6 +10,7 @@ import ( "unicode/utf8" "git.flo.fo/FLO/dirmd/internal/git" + "git.flo.fo/FLO/dirmd/internal/instructions" "git.flo.fo/FLO/dirmd/internal/tree" "git.flo.fo/FLO/dirmd/internal/walker" ) @@ -42,7 +43,12 @@ func Write(result *walker.Result, outputPath string, appendMode bool, singleFile if !appendMode { fmt.Fprintf(&sb, "> Generated: %s\n", time.Now().Format(time.RFC3339)) sb.WriteString(git.FormatCommitLine(result.Commit)) - sb.WriteString("\n") + + if result.Instructions != "" { + sb.WriteString(instructions.Format(result.Instructions)) + } else { + sb.WriteString("\n") + } } if !singleFile { @@ -61,7 +67,6 @@ func Write(result *walker.Result, outputPath string, appendMode bool, singleFile for _, entry := range result.Entries { fullPath := filepath.Join(result.AbsRoot, entry.RelPath) - content, err := readFileForOutput(fullPath, result.OmitTest) if err != nil { fmt.Fprintf(os.Stderr, "warning: cannot read %s: %v\n", entry.RelPath, err) @@ -113,12 +118,11 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e } var generated []string - indexBasename := "index_" + repoName + ".md" rootPath := filepath.Join(outputDir, indexBasename) generated = append(generated, rootPath) - if err := writeRootSlice(rootPath, absRoot, rootName, repoName, rootFiles, topLevelDirs, result.OmitTest, result.Commit); err != nil { + if err := writeRootSlice(rootPath, absRoot, rootName, repoName, rootFiles, topLevelDirs, result.OmitTest, result.Commit, result.Instructions); err != nil { return nil, fmt.Errorf("failed to write root slice: %w", err) } @@ -144,16 +148,13 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e for i, e := range dirEntries { sepIdx := strings.Index(e.RelPath, string(filepath.Separator)) if sepIdx >= 0 { - treeEntries[i] = walker.Entry{ - RelPath: e.RelPath[sepIdx+1:], - Size: e.Size, - } + treeEntries[i] = walker.Entry{RelPath: e.RelPath[sepIdx+1:], Size: e.Size} } else { treeEntries[i] = e } } - if err := writeDirSlice(slicePath, absDir, absRoot, dirName, treeEntries, dirEntries, indexBasename, result.OmitTest, result.Commit); err != nil { + if err := writeDirSlice(slicePath, absDir, absRoot, dirName, treeEntries, dirEntries, indexBasename, result.OmitTest, result.Commit, result.Instructions); err != nil { return nil, fmt.Errorf("failed to write slice %s: %w", sliceName, err) } } @@ -161,7 +162,7 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e return generated, nil } -func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker.Entry, topLevelDirs map[string][]walker.Entry, omitTest bool, commit *git.CommitInfo) error { +func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker.Entry, topLevelDirs map[string][]walker.Entry, omitTest bool, commit *git.CommitInfo, instContent string) error { f, err := os.Create(path) if err != nil { return fmt.Errorf("cannot create root file: %w", err) @@ -169,11 +170,14 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker defer f.Close() var sb strings.Builder - fmt.Fprintf(&sb, "# %s [ROOT]\n", absRoot) fmt.Fprintf(&sb, "> Generated: %s\n", time.Now().Format(time.RFC3339)) sb.WriteString(git.FormatCommitLine(commit)) - sb.WriteString("\n") + if instContent != "" { + sb.WriteString(instructions.Format(instContent)) + } else { + sb.WriteString("\n") + } treeLines := buildTreeWithRefs(rootName, repoName, topLevelDirs, rootFiles) sb.WriteString("## Directory Tree\n\n") @@ -207,11 +211,7 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker if len(rootFiles) > 0 { sb.WriteString("## Root Contents\n\n") - - sort.Slice(rootFiles, func(i, j int) bool { - return rootFiles[i].RelPath < rootFiles[j].RelPath - }) - + sort.Slice(rootFiles, func(i, j int) bool { return rootFiles[i].RelPath < rootFiles[j].RelPath }) for _, entry := range rootFiles { fullPath := filepath.Join(absRoot, entry.RelPath) content, err := readFileForOutput(fullPath, omitTest) @@ -219,17 +219,14 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker fmt.Fprintf(os.Stderr, "warning: cannot read %s: %v\n", entry.RelPath, err) continue } - language := extToLang(filepath.Ext(entry.RelPath)) lines := countLines(content) - fmt.Fprintf(&sb, "--- FILE: ./%s (%d bytes, %d lines) ---\n", entry.RelPath, entry.Size, lines) fmt.Fprintf(&sb, "%s", fenceChars) if language != "" { fmt.Fprintf(&sb, "%s", language) } - fmt.Fprintf(&sb, "\n") - fmt.Fprintf(&sb, "%s", content) + fmt.Fprintf(&sb, "\n%s", content) if !strings.HasSuffix(content, "\n") { fmt.Fprintf(&sb, "\n") } @@ -241,7 +238,7 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker return err } -func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntries []walker.Entry, rootFileName string, omitTest bool, commit *git.CommitInfo) error { +func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntries []walker.Entry, rootFileName string, omitTest bool, commit *git.CommitInfo, instContent string) error { f, err := os.Create(path) if err != nil { return fmt.Errorf("cannot create slice file: %w", err) @@ -249,26 +246,25 @@ func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntri defer f.Close() var sb strings.Builder - fmt.Fprintf(&sb, "# %s [SLICE]\n", absDir) fmt.Fprintf(&sb, "> Generated: %s\n", time.Now().Format(time.RFC3339)) sb.WriteString(git.FormatCommitLine(commit)) + if instContent != "" { + sb.WriteString(instructions.Format(instContent)) + } else { + sb.WriteString("\n") + } fmt.Fprintf(&sb, "Root document: %s\n\n", rootFileName) sb.WriteString("## Directory Tree\n\n") sb.WriteString(fenceChars + "\n") - sliceTree := buildTreeFromEntries(dirName, treeEntries) sb.WriteString(sliceTree) sb.WriteString(fenceChars + "\n\n") if len(fileEntries) > 0 { sb.WriteString("## Contents\n\n") - - sort.Slice(fileEntries, func(i, j int) bool { - return fileEntries[i].RelPath < fileEntries[j].RelPath - }) - + sort.Slice(fileEntries, func(i, j int) bool { return fileEntries[i].RelPath < fileEntries[j].RelPath }) for _, entry := range fileEntries { fullPath := filepath.Join(absRoot, entry.RelPath) content, err := readFileForOutput(fullPath, omitTest) @@ -276,17 +272,14 @@ func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntri fmt.Fprintf(os.Stderr, "warning: cannot read %s: %v\n", entry.RelPath, err) continue } - language := extToLang(filepath.Ext(entry.RelPath)) lines := countLines(content) - fmt.Fprintf(&sb, "--- FILE: ./%s (%d bytes, %d lines) ---\n", entry.RelPath, entry.Size, lines) fmt.Fprintf(&sb, "%s", fenceChars) if language != "" { fmt.Fprintf(&sb, "%s", language) } - fmt.Fprintf(&sb, "\n") - fmt.Fprintf(&sb, "%s", content) + fmt.Fprintf(&sb, "\n%s", content) if !strings.HasSuffix(content, "\n") { fmt.Fprintf(&sb, "\n") } diff --git a/internal/tui/run.go b/internal/tui/run.go index 2d541b2..95607ca 100644 --- a/internal/tui/run.go +++ b/internal/tui/run.go @@ -79,6 +79,7 @@ func Run() error { huh.NewOption("Omit test files", "omit-test"), huh.NewOption("Omit non-README markdown", "omit-md"), huh.NewOption("Upload to Proton Drive", "proton-drive"), + huh.NewOption("Include .dirmd instructions", "instructions"), ). Value(&options). Height(10), @@ -115,6 +116,7 @@ func Run() error { omitTest := containsOption(options, "omit-test") omitMd := containsOption(options, "omit-md") protonDrive := containsOption(options, "proton-drive") + includeInstructions := containsOption(options, "instructions") if skipFrontend && frontendOnly { return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive") @@ -155,20 +157,21 @@ func Run() error { allIgnores = append(allIgnores, ignores...) cfg := &config.Config{ - InputPath: inputDir, - OutputPath: outputDir, - Force: force, - MaxSize: int64(maxSizeKB) * 1024, - MaxDepth: maxDepth, - Ignores: allIgnores, - Extensions: excludedExts, - ProtonDrive: protonDrive, - DrivePath: drivePath, - VerticalSlices: verticalSlices, - SkipFrontend: skipFrontend, - FrontendOnly: frontendOnly, - OmitTest: omitTest, - OmitMd: omitMd, + InputPath: inputDir, + OutputPath: outputDir, + Force: force, + MaxSize: int64(maxSizeKB) * 1024, + MaxDepth: maxDepth, + Ignores: allIgnores, + Extensions: excludedExts, + ProtonDrive: protonDrive, + DrivePath: drivePath, + VerticalSlices: verticalSlices, + SkipFrontend: skipFrontend, + FrontendOnly: frontendOnly, + OmitTest: omitTest, + OmitMd: omitMd, + IncludeInstructions: includeInstructions, } cmd := buildCommand(cfg) diff --git a/internal/walker/walker.go b/internal/walker/walker.go index 5aec8c9..c44e8f9 100644 --- a/internal/walker/walker.go +++ b/internal/walker/walker.go @@ -10,6 +10,7 @@ import ( "git.flo.fo/FLO/dirmd/internal/config" "git.flo.fo/FLO/dirmd/internal/filter" "git.flo.fo/FLO/dirmd/internal/git" + "git.flo.fo/FLO/dirmd/internal/instructions" ) type Entry struct { @@ -18,11 +19,12 @@ type Entry struct { } type Result struct { - Entries []Entry - AbsRoot string - SingleFile bool - OmitTest bool - Commit *git.CommitInfo + Entries []Entry + AbsRoot string + SingleFile bool + OmitTest bool + Commit *git.CommitInfo + Instructions string } func Run(cfg *config.Config) (*Result, error) { @@ -55,15 +57,19 @@ func processSingleFile(cfg *config.Config) (*Result, error) { 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)) + var instContent string + if cfg.IncludeInstructions { + instContent, _ = instructions.Read(filepath.Dir(cfg.AbsRoot)) + } return &Result{ - Entries: []Entry{{RelPath: filepath.Base(cfg.AbsRoot), Size: info.Size()}}, - AbsRoot: filepath.Dir(cfg.AbsRoot), - SingleFile: true, - OmitTest: cfg.OmitTest, - Commit: commitInfo, + Entries: []Entry{{RelPath: filepath.Base(cfg.AbsRoot), Size: info.Size()}}, + AbsRoot: filepath.Dir(cfg.AbsRoot), + SingleFile: true, + OmitTest: cfg.OmitTest, + Commit: commitInfo, + Instructions: instContent, }, nil } @@ -78,9 +84,17 @@ func walkDirectory(cfg *config.Config) (*Result, error) { } result := &Result{AbsRoot: cfg.AbsRoot, OmitTest: cfg.OmitTest} - - // Fetch git info for the root directory + result.Commit = git.GetCommitInfo(cfg.AbsRoot) + + if cfg.IncludeInstructions { + instContent, err := instructions.Read(cfg.AbsRoot) + if err != nil { + fmt.Fprintf(os.Stderr, "warning: could not read .dirmd: %v\n", err) + } else { + result.Instructions = instContent + } + } err := filepath.WalkDir(cfg.AbsRoot, func(path string, d fs.DirEntry, err error) error { if err != nil {