Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f908bffc41 | ||
|
|
a1638bd944 | ||
|
|
93d830d500 | ||
|
|
89b7eb78a7 | ||
|
|
b1dd808d1e |
@@ -1,3 +1,4 @@
|
|||||||
bin/
|
bin/
|
||||||
.env
|
.env
|
||||||
vendor/
|
vendor/
|
||||||
|
.dirmd
|
||||||
|
|||||||
@@ -138,10 +138,32 @@ Example output with `dirmd -o docs.md ~/repos/myproject --vertical-slices`:
|
|||||||
├── cmd.md ← slice: tree + contents of cmd/
|
├── cmd.md ← slice: tree + contents of cmd/
|
||||||
└── internal.md ← slice: tree + contents of internal/
|
└── 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
|
## LLM-Optimized Output
|
||||||
|
|
||||||
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.
|
||||||
|
- **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
|
||||||
@@ -218,15 +240,18 @@ For full documentation on the Proton Drive CLI, including installation, authenti
|
|||||||
Produces markdown with:
|
Produces markdown with:
|
||||||
|
|
||||||
1. Absolute path as H1 header with `[ROOT]` or `[SLICE]` marker
|
1. Absolute path as H1 header with `[ROOT]` or `[SLICE]` marker
|
||||||
2. ASCII directory tree (indented with `+-` and `|` characters)
|
2. **Timestamp metadata line** (ISO 8601) immediately after the H1 header (only for new files)
|
||||||
3. Vertical slices index table (sliced mode only)
|
3. ASCII directory tree (indented with `+-` and `|` characters)
|
||||||
4. All text files with content in fenced code blocks
|
4. Vertical slices index table (sliced mode only)
|
||||||
5. Explicit `--- FILE: ---` and `--- END FILE ---` delimiters with byte and line counts
|
5. All text files with content in fenced code blocks
|
||||||
6. Language detection from file extensions
|
6. Explicit `--- FILE: ---` and `--- END FILE ---` delimiters with byte and line counts
|
||||||
|
7. Language detection from file extensions
|
||||||
|
|
||||||
Example output structure (non-sliced mode):
|
Example output structure (non-sliced mode):
|
||||||
|
|
||||||
# /home/user/repos/myproject [ROOT]
|
# /home/user/repos/myproject [ROOT]
|
||||||
|
> Generated: 2026-09-02T14:30:00Z
|
||||||
|
> Commit: a1b2c3d (Fix login bug)
|
||||||
|
|
||||||
## Directory Tree
|
## Directory Tree
|
||||||
|
|
||||||
|
|||||||
+30
-40
@@ -27,33 +27,24 @@ Output is always written to a directory specified by -o.
|
|||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
# Create new documentation (writes index.md inside ./docs/)
|
# Create new documentation
|
||||||
dirmd -o ./docs ~/repos/myproject
|
dirmd -o ./docs ~/repos/myproject
|
||||||
|
|
||||||
# Overwrite existing output directory
|
# Overwrite existing output directory (deletes all contents)
|
||||||
dirmd -o ./docs ~/repos/myproject -f
|
dirmd -o ./docs ~/repos/myproject -f
|
||||||
|
|
||||||
# Append a single file
|
# Append a single file
|
||||||
dirmd -a -i README.md -o ./docs
|
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
|
dirmd -o ./docs ~/repos/myproject --vertical-slices
|
||||||
|
|
||||||
# Skip frontend files
|
# Include instructions from .dirmd
|
||||||
dirmd -o ./docs ~/repos/myproject --skip-frontend
|
dirmd -o ./docs ~/repos/myproject --instructions
|
||||||
|
|
||||||
# Frontend only
|
|
||||||
dirmd -o ./docs ~/repos/myproject --frontend-only
|
|
||||||
|
|
||||||
# Omit test files and test blocks
|
|
||||||
dirmd -o ./docs ~/repos/myproject --omit-test
|
|
||||||
|
|
||||||
# Generate and upload to Proton Drive
|
# Generate and upload to Proton Drive
|
||||||
dirmd -o ./docs ~/repos/myproject --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
|
# Interactive TUI mode
|
||||||
dirmd --tui`,
|
dirmd --tui`,
|
||||||
RunE: run,
|
RunE: run,
|
||||||
@@ -75,6 +66,7 @@ Examples:
|
|||||||
omitTest bool
|
omitTest bool
|
||||||
omitMd bool
|
omitMd bool
|
||||||
tuiMode bool
|
tuiMode bool
|
||||||
|
instructions bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
@@ -86,7 +78,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")
|
||||||
@@ -100,6 +92,7 @@ func init() {
|
|||||||
rootCmd.Flags().BoolVar(&frontendOnly, "frontend-only", false, "include only frontend file types and README.md")
|
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(&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(&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 {
|
func run(cmd *cobra.Command, args []string) error {
|
||||||
@@ -133,20 +126,21 @@ func run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg := &config.Config{
|
cfg := &config.Config{
|
||||||
OutputPath: outputPath,
|
OutputPath: outputPath,
|
||||||
AppendMode: appendMode,
|
AppendMode: appendMode,
|
||||||
Force: force,
|
Force: force,
|
||||||
MaxSize: maxSize,
|
MaxSize: maxSize,
|
||||||
MaxDepth: maxDepth,
|
MaxDepth: maxDepth,
|
||||||
Ignores: append(config.DefaultIgnores, ignorePatterns...),
|
Ignores: append(config.DefaultIgnores, ignorePatterns...),
|
||||||
Extensions: excludedExts,
|
Extensions: excludedExts,
|
||||||
ProtonDrive: protonDrive,
|
ProtonDrive: protonDrive,
|
||||||
DrivePath: drivePath,
|
DrivePath: drivePath,
|
||||||
VerticalSlices: verticalSlices,
|
VerticalSlices: verticalSlices,
|
||||||
SkipFrontend: skipFrontend,
|
SkipFrontend: skipFrontend,
|
||||||
FrontendOnly: frontendOnly,
|
FrontendOnly: frontendOnly,
|
||||||
OmitTest: omitTest,
|
OmitTest: omitTest,
|
||||||
OmitMd: omitMd,
|
OmitMd: omitMd,
|
||||||
|
IncludeInstructions: instructions,
|
||||||
}
|
}
|
||||||
|
|
||||||
if singleFile != "" {
|
if singleFile != "" {
|
||||||
@@ -174,8 +168,13 @@ func run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
}
|
||||||
|
if err := os.RemoveAll(absOutput); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove existing output directory: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +201,6 @@ func run(cmd *cobra.Command, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "generated %d files in %s\n", len(generated), absOutput)
|
fmt.Fprintf(os.Stderr, "generated %d files in %s\n", len(generated), absOutput)
|
||||||
|
|
||||||
if protonDrive {
|
if protonDrive {
|
||||||
@@ -210,27 +208,22 @@ func run(cmd *cobra.Command, args []string) error {
|
|||||||
if remoteDir == "" {
|
if remoteDir == "" {
|
||||||
remoteDir = "/my-files/md/" + filepath.Base(absOutput)
|
remoteDir = "/my-files/md/" + filepath.Base(absOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := drive.CheckAuth(); err != nil {
|
if err := drive.CheckAuth(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
fmt.Fprintln(os.Stderr, "local files were written successfully, but upload was skipped")
|
fmt.Fprintln(os.Stderr, "local files were written successfully, but upload was skipped")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := drive.UploadDir(absOutput, remoteDir); err != nil {
|
if err := drive.UploadDir(absOutput, remoteDir); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
fmt.Fprintln(os.Stderr, "local files were written successfully, but upload failed")
|
fmt.Fprintln(os.Stderr, "local files were written successfully, but upload failed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "uploaded %s to %s on Proton Drive\n", absOutput, remoteDir)
|
fmt.Fprintf(os.Stderr, "uploaded %s to %s on Proton Drive\n", absOutput, remoteDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
indexPath := filepath.Join(absOutput, indexBasename)
|
indexPath := filepath.Join(absOutput, indexBasename)
|
||||||
|
|
||||||
if err := renderer.Write(result, indexPath, appendMode, cfg.SingleFile); err != nil {
|
if err := renderer.Write(result, indexPath, appendMode, cfg.SingleFile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -242,19 +235,16 @@ func run(cmd *cobra.Command, args []string) error {
|
|||||||
if remoteDir == "" {
|
if remoteDir == "" {
|
||||||
remoteDir = "/my-files/md/" + filepath.Base(absOutput)
|
remoteDir = "/my-files/md/" + filepath.Base(absOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := drive.CheckAuth(); err != nil {
|
if err := drive.CheckAuth(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
fmt.Fprintln(os.Stderr, "local file was written successfully, but upload was skipped")
|
fmt.Fprintln(os.Stderr, "local file was written successfully, but upload was skipped")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := drive.UploadDir(absOutput, remoteDir); err != nil {
|
if err := drive.UploadDir(absOutput, remoteDir); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
fmt.Fprintln(os.Stderr, "local file was written successfully, but upload failed")
|
fmt.Fprintln(os.Stderr, "local file was written successfully, but upload failed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "uploaded %s to %s on Proton Drive\n", absOutput, remoteDir)
|
fmt.Fprintf(os.Stderr, "uploaded %s to %s on Proton Drive\n", absOutput, remoteDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+60
-78
@@ -15,19 +15,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var DefaultIgnores = []string{
|
var DefaultIgnores = []string{
|
||||||
".git",
|
".git", "node_modules", "vendor", "bin", "go.mod", "go.sum",
|
||||||
"node_modules",
|
"target", "build", "dist", "Cargo.lock", ".idea", ".vscode",
|
||||||
"vendor",
|
"dependency-reduced-pom.xml", "__pycache__", "venv", "env", "uv.lock",
|
||||||
"bin",
|
|
||||||
"go.mod",
|
|
||||||
"go.sum",
|
|
||||||
"target",
|
|
||||||
"build",
|
|
||||||
"dist",
|
|
||||||
"Cargo.lock",
|
|
||||||
".idea",
|
|
||||||
".vscode",
|
|
||||||
"dependency-reduced-pom.xml",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultExts = []string{
|
var DefaultExts = []string{
|
||||||
@@ -39,10 +29,8 @@ var DefaultExts = []string{
|
|||||||
".pdf", ".doc", ".docx", ".xls", ".xlsx",
|
".pdf", ".doc", ".docx", ".xls", ".xlsx",
|
||||||
".pem", ".key", ".cert",
|
".pem", ".key", ".cert",
|
||||||
".db", ".sqlite", ".sqlite3",
|
".db", ".sqlite", ".sqlite3",
|
||||||
".class",
|
".class", ".jar", ".war", ".ear",
|
||||||
".jar",
|
".pyc", ".pyo",
|
||||||
".war",
|
|
||||||
".ear",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var FrontendExts = []string{
|
var FrontendExts = []string{
|
||||||
@@ -55,33 +43,28 @@ var FrontendExts = []string{
|
|||||||
|
|
||||||
type stringSlice []string
|
type stringSlice []string
|
||||||
|
|
||||||
func (s *stringSlice) String() string {
|
func (s *stringSlice) String() string { return strings.Join(*s, ", ") }
|
||||||
return strings.Join(*s, ", ")
|
func (s *stringSlice) Set(v string) error { *s = append(*s, v); return nil }
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stringSlice) Set(v string) error {
|
|
||||||
*s = append(*s, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
InputPath string
|
InputPath string
|
||||||
OutputPath string
|
OutputPath string
|
||||||
AppendMode bool
|
AppendMode bool
|
||||||
Force bool
|
Force bool
|
||||||
SingleFile bool
|
SingleFile bool
|
||||||
MaxSize int64
|
MaxSize int64
|
||||||
MaxDepth int
|
MaxDepth int
|
||||||
Ignores []string
|
Ignores []string
|
||||||
Extensions []string
|
Extensions []string
|
||||||
AbsRoot string
|
AbsRoot string
|
||||||
ProtonDrive bool
|
ProtonDrive bool
|
||||||
DrivePath string
|
DrivePath string
|
||||||
VerticalSlices bool
|
VerticalSlices bool
|
||||||
SkipFrontend bool
|
SkipFrontend bool
|
||||||
FrontendOnly bool
|
FrontendOnly bool
|
||||||
OmitTest bool
|
OmitTest bool
|
||||||
OmitMd bool
|
OmitMd bool
|
||||||
|
IncludeInstructions bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintUsage() {
|
func PrintUsage() {
|
||||||
@@ -94,22 +77,23 @@ func PrintUsage() {
|
|||||||
|
|
||||||
func Parse() (*Config, error) {
|
func Parse() (*Config, error) {
|
||||||
var (
|
var (
|
||||||
input string
|
input string
|
||||||
output string
|
output string
|
||||||
appendMd bool
|
appendMd bool
|
||||||
force bool
|
force bool
|
||||||
maxSize int64
|
maxSize int64
|
||||||
maxDepth int
|
maxDepth int
|
||||||
ignores stringSlice
|
ignores stringSlice
|
||||||
exts stringSlice
|
exts stringSlice
|
||||||
showVer bool
|
showVer bool
|
||||||
protonDrive bool
|
protonDrive bool
|
||||||
drivePath string
|
drivePath string
|
||||||
verticalSlices bool
|
verticalSlices bool
|
||||||
skipFrontend bool
|
skipFrontend bool
|
||||||
frontendOnly bool
|
frontendOnly bool
|
||||||
omitTest bool
|
omitTest bool
|
||||||
omitMd bool
|
omitMd bool
|
||||||
|
includeInstructions bool
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&input, "i", "", "single file input (append mode only)")
|
flag.StringVar(&input, "i", "", "single file input (append mode only)")
|
||||||
@@ -124,10 +108,11 @@ func Parse() (*Config, error) {
|
|||||||
flag.BoolVar(&protonDrive, "proton-drive", false, "upload output to Proton Drive after writing locally")
|
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.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(&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(&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(&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.Usage = PrintUsage
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -140,19 +125,15 @@ func Parse() (*Config, error) {
|
|||||||
if output == "" {
|
if output == "" {
|
||||||
return nil, fmt.Errorf("-o is required")
|
return nil, fmt.Errorf("-o is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if drivePath != "" && !protonDrive {
|
if drivePath != "" && !protonDrive {
|
||||||
return nil, fmt.Errorf("--drive-path requires --proton-drive")
|
return nil, fmt.Errorf("--drive-path requires --proton-drive")
|
||||||
}
|
}
|
||||||
|
|
||||||
if verticalSlices && appendMd {
|
if verticalSlices && appendMd {
|
||||||
return nil, fmt.Errorf("--vertical-slices cannot be used with --append")
|
return nil, fmt.Errorf("--vertical-slices cannot be used with --append")
|
||||||
}
|
}
|
||||||
|
|
||||||
if verticalSlices && input != "" {
|
if verticalSlices && input != "" {
|
||||||
return nil, fmt.Errorf("--vertical-slices cannot be used with --input-file")
|
return nil, fmt.Errorf("--vertical-slices cannot be used with --input-file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipFrontend && frontendOnly {
|
if skipFrontend && frontendOnly {
|
||||||
return nil, fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
return nil, fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
||||||
}
|
}
|
||||||
@@ -163,20 +144,21 @@ func Parse() (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
OutputPath: output,
|
OutputPath: output,
|
||||||
AppendMode: appendMd,
|
AppendMode: appendMd,
|
||||||
Force: force,
|
Force: force,
|
||||||
MaxSize: maxSize,
|
MaxSize: maxSize,
|
||||||
MaxDepth: maxDepth,
|
MaxDepth: maxDepth,
|
||||||
Ignores: append(DefaultIgnores, ignores...),
|
Ignores: append(DefaultIgnores, ignores...),
|
||||||
Extensions: excludedExts,
|
Extensions: excludedExts,
|
||||||
ProtonDrive: protonDrive,
|
ProtonDrive: protonDrive,
|
||||||
DrivePath: drivePath,
|
DrivePath: drivePath,
|
||||||
VerticalSlices: verticalSlices,
|
VerticalSlices: verticalSlices,
|
||||||
SkipFrontend: skipFrontend,
|
SkipFrontend: skipFrontend,
|
||||||
FrontendOnly: frontendOnly,
|
FrontendOnly: frontendOnly,
|
||||||
OmitTest: omitTest,
|
OmitTest: omitTest,
|
||||||
OmitMd: omitMd,
|
OmitMd: omitMd,
|
||||||
|
IncludeInstructions: includeInstructions,
|
||||||
}
|
}
|
||||||
|
|
||||||
if input != "" {
|
if input != "" {
|
||||||
|
|||||||
@@ -99,6 +99,16 @@ func isTestFile(path, name string) bool {
|
|||||||
}
|
}
|
||||||
slashPath := filepath.ToSlash(path)
|
slashPath := filepath.ToSlash(path)
|
||||||
return strings.Contains(slashPath, "/src/test/")
|
return strings.Contains(slashPath, "/src/test/")
|
||||||
|
case ".py":
|
||||||
|
lower := strings.ToLower(name)
|
||||||
|
if strings.HasPrefix(lower, "test_") || strings.HasSuffix(lower, "_test.py") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.EqualFold(name, "conftest.py") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
slashPath := filepath.ToSlash(path)
|
||||||
|
return strings.Contains(slashPath, "/tests/")
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -6,8 +6,11 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"unicode/utf8"
|
"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/tree"
|
||||||
"git.flo.fo/FLO/dirmd/internal/walker"
|
"git.flo.fo/FLO/dirmd/internal/walker"
|
||||||
)
|
)
|
||||||
@@ -35,7 +38,18 @@ func Write(result *walker.Result, outputPath string, appendMode bool, singleFile
|
|||||||
h1Path = filepath.Join(result.AbsRoot, result.Entries[0].RelPath)
|
h1Path = filepath.Join(result.AbsRoot, result.Entries[0].RelPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(&sb, "# %s [ROOT]\n\n", h1Path)
|
fmt.Fprintf(&sb, "# %s [ROOT]\n", h1Path)
|
||||||
|
|
||||||
|
if !appendMode {
|
||||||
|
fmt.Fprintf(&sb, "> Generated: %s\n", time.Now().Format(time.RFC3339))
|
||||||
|
sb.WriteString(git.FormatCommitLine(result.Commit))
|
||||||
|
|
||||||
|
if result.Instructions != "" {
|
||||||
|
sb.WriteString(instructions.Format(result.Instructions))
|
||||||
|
} else {
|
||||||
|
sb.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !singleFile {
|
if !singleFile {
|
||||||
root := tree.Build(result.Entries)
|
root := tree.Build(result.Entries)
|
||||||
@@ -53,7 +67,6 @@ func Write(result *walker.Result, outputPath string, appendMode bool, singleFile
|
|||||||
|
|
||||||
for _, entry := range result.Entries {
|
for _, entry := range result.Entries {
|
||||||
fullPath := filepath.Join(result.AbsRoot, entry.RelPath)
|
fullPath := filepath.Join(result.AbsRoot, entry.RelPath)
|
||||||
|
|
||||||
content, err := readFileForOutput(fullPath, result.OmitTest)
|
content, err := readFileForOutput(fullPath, result.OmitTest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "warning: cannot read %s: %v\n", entry.RelPath, err)
|
fmt.Fprintf(os.Stderr, "warning: cannot read %s: %v\n", entry.RelPath, err)
|
||||||
@@ -105,12 +118,11 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
var generated []string
|
var generated []string
|
||||||
|
|
||||||
indexBasename := "index_" + repoName + ".md"
|
indexBasename := "index_" + repoName + ".md"
|
||||||
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, result.Instructions); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write root slice: %w", err)
|
return nil, fmt.Errorf("failed to write root slice: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,16 +148,13 @@ func WriteSlices(result *walker.Result, outputDir, repoName string) ([]string, e
|
|||||||
for i, e := range dirEntries {
|
for i, e := range dirEntries {
|
||||||
sepIdx := strings.Index(e.RelPath, string(filepath.Separator))
|
sepIdx := strings.Index(e.RelPath, string(filepath.Separator))
|
||||||
if sepIdx >= 0 {
|
if sepIdx >= 0 {
|
||||||
treeEntries[i] = walker.Entry{
|
treeEntries[i] = walker.Entry{RelPath: e.RelPath[sepIdx+1:], Size: e.Size}
|
||||||
RelPath: e.RelPath[sepIdx+1:],
|
|
||||||
Size: e.Size,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
treeEntries[i] = e
|
treeEntries[i] = 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, result.Instructions); 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +162,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, instContent string) 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)
|
||||||
@@ -161,8 +170,14 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
fmt.Fprintf(&sb, "# %s [ROOT]\n", absRoot)
|
||||||
fmt.Fprintf(&sb, "# %s [ROOT]\n\n", absRoot)
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
treeLines := buildTreeWithRefs(rootName, repoName, topLevelDirs, rootFiles)
|
treeLines := buildTreeWithRefs(rootName, repoName, topLevelDirs, rootFiles)
|
||||||
sb.WriteString("## Directory Tree\n\n")
|
sb.WriteString("## Directory Tree\n\n")
|
||||||
@@ -196,11 +211,7 @@ func writeRootSlice(path, absRoot, rootName, repoName string, rootFiles []walker
|
|||||||
|
|
||||||
if len(rootFiles) > 0 {
|
if len(rootFiles) > 0 {
|
||||||
sb.WriteString("## Root Contents\n\n")
|
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 {
|
for _, entry := range rootFiles {
|
||||||
fullPath := filepath.Join(absRoot, entry.RelPath)
|
fullPath := filepath.Join(absRoot, entry.RelPath)
|
||||||
content, err := readFileForOutput(fullPath, omitTest)
|
content, err := readFileForOutput(fullPath, omitTest)
|
||||||
@@ -208,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)
|
fmt.Fprintf(os.Stderr, "warning: cannot read %s: %v\n", entry.RelPath, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
language := extToLang(filepath.Ext(entry.RelPath))
|
language := extToLang(filepath.Ext(entry.RelPath))
|
||||||
lines := countLines(content)
|
lines := countLines(content)
|
||||||
|
|
||||||
fmt.Fprintf(&sb, "--- FILE: ./%s (%d bytes, %d lines) ---\n", entry.RelPath, entry.Size, lines)
|
fmt.Fprintf(&sb, "--- FILE: ./%s (%d bytes, %d lines) ---\n", entry.RelPath, entry.Size, lines)
|
||||||
fmt.Fprintf(&sb, "%s", fenceChars)
|
fmt.Fprintf(&sb, "%s", fenceChars)
|
||||||
if language != "" {
|
if language != "" {
|
||||||
fmt.Fprintf(&sb, "%s", language)
|
fmt.Fprintf(&sb, "%s", language)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&sb, "\n")
|
fmt.Fprintf(&sb, "\n%s", content)
|
||||||
fmt.Fprintf(&sb, "%s", content)
|
|
||||||
if !strings.HasSuffix(content, "\n") {
|
if !strings.HasSuffix(content, "\n") {
|
||||||
fmt.Fprintf(&sb, "\n")
|
fmt.Fprintf(&sb, "\n")
|
||||||
}
|
}
|
||||||
@@ -230,7 +238,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, instContent string) 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)
|
||||||
@@ -238,24 +246,25 @@ func writeDirSlice(path, absDir, absRoot, dirName string, treeEntries, fileEntri
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
fmt.Fprintf(&sb, "# %s [SLICE]\n", absDir)
|
||||||
fmt.Fprintf(&sb, "# %s [SLICE]\n\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)
|
fmt.Fprintf(&sb, "Root document: %s\n\n", rootFileName)
|
||||||
|
|
||||||
sb.WriteString("## Directory Tree\n\n")
|
sb.WriteString("## Directory Tree\n\n")
|
||||||
sb.WriteString(fenceChars + "\n")
|
sb.WriteString(fenceChars + "\n")
|
||||||
|
|
||||||
sliceTree := buildTreeFromEntries(dirName, treeEntries)
|
sliceTree := buildTreeFromEntries(dirName, treeEntries)
|
||||||
sb.WriteString(sliceTree)
|
sb.WriteString(sliceTree)
|
||||||
sb.WriteString(fenceChars + "\n\n")
|
sb.WriteString(fenceChars + "\n\n")
|
||||||
|
|
||||||
if len(fileEntries) > 0 {
|
if len(fileEntries) > 0 {
|
||||||
sb.WriteString("## Contents\n\n")
|
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 {
|
for _, entry := range fileEntries {
|
||||||
fullPath := filepath.Join(absRoot, entry.RelPath)
|
fullPath := filepath.Join(absRoot, entry.RelPath)
|
||||||
content, err := readFileForOutput(fullPath, omitTest)
|
content, err := readFileForOutput(fullPath, omitTest)
|
||||||
@@ -263,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)
|
fmt.Fprintf(os.Stderr, "warning: cannot read %s: %v\n", entry.RelPath, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
language := extToLang(filepath.Ext(entry.RelPath))
|
language := extToLang(filepath.Ext(entry.RelPath))
|
||||||
lines := countLines(content)
|
lines := countLines(content)
|
||||||
|
|
||||||
fmt.Fprintf(&sb, "--- FILE: ./%s (%d bytes, %d lines) ---\n", entry.RelPath, entry.Size, lines)
|
fmt.Fprintf(&sb, "--- FILE: ./%s (%d bytes, %d lines) ---\n", entry.RelPath, entry.Size, lines)
|
||||||
fmt.Fprintf(&sb, "%s", fenceChars)
|
fmt.Fprintf(&sb, "%s", fenceChars)
|
||||||
if language != "" {
|
if language != "" {
|
||||||
fmt.Fprintf(&sb, "%s", language)
|
fmt.Fprintf(&sb, "%s", language)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&sb, "\n")
|
fmt.Fprintf(&sb, "\n%s", content)
|
||||||
fmt.Fprintf(&sb, "%s", content)
|
|
||||||
if !strings.HasSuffix(content, "\n") {
|
if !strings.HasSuffix(content, "\n") {
|
||||||
fmt.Fprintf(&sb, "\n")
|
fmt.Fprintf(&sb, "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-14
@@ -79,6 +79,7 @@ func Run() error {
|
|||||||
huh.NewOption("Omit test files", "omit-test"),
|
huh.NewOption("Omit test files", "omit-test"),
|
||||||
huh.NewOption("Omit non-README markdown", "omit-md"),
|
huh.NewOption("Omit non-README markdown", "omit-md"),
|
||||||
huh.NewOption("Upload to Proton Drive", "proton-drive"),
|
huh.NewOption("Upload to Proton Drive", "proton-drive"),
|
||||||
|
huh.NewOption("Include .dirmd instructions", "instructions"),
|
||||||
).
|
).
|
||||||
Value(&options).
|
Value(&options).
|
||||||
Height(10),
|
Height(10),
|
||||||
@@ -115,6 +116,7 @@ func Run() error {
|
|||||||
omitTest := containsOption(options, "omit-test")
|
omitTest := containsOption(options, "omit-test")
|
||||||
omitMd := containsOption(options, "omit-md")
|
omitMd := containsOption(options, "omit-md")
|
||||||
protonDrive := containsOption(options, "proton-drive")
|
protonDrive := containsOption(options, "proton-drive")
|
||||||
|
includeInstructions := containsOption(options, "instructions")
|
||||||
|
|
||||||
if skipFrontend && frontendOnly {
|
if skipFrontend && frontendOnly {
|
||||||
return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
||||||
@@ -155,20 +157,21 @@ func Run() error {
|
|||||||
allIgnores = append(allIgnores, ignores...)
|
allIgnores = append(allIgnores, ignores...)
|
||||||
|
|
||||||
cfg := &config.Config{
|
cfg := &config.Config{
|
||||||
InputPath: inputDir,
|
InputPath: inputDir,
|
||||||
OutputPath: outputDir,
|
OutputPath: outputDir,
|
||||||
Force: force,
|
Force: force,
|
||||||
MaxSize: int64(maxSizeKB) * 1024,
|
MaxSize: int64(maxSizeKB) * 1024,
|
||||||
MaxDepth: maxDepth,
|
MaxDepth: maxDepth,
|
||||||
Ignores: allIgnores,
|
Ignores: allIgnores,
|
||||||
Extensions: excludedExts,
|
Extensions: excludedExts,
|
||||||
ProtonDrive: protonDrive,
|
ProtonDrive: protonDrive,
|
||||||
DrivePath: drivePath,
|
DrivePath: drivePath,
|
||||||
VerticalSlices: verticalSlices,
|
VerticalSlices: verticalSlices,
|
||||||
SkipFrontend: skipFrontend,
|
SkipFrontend: skipFrontend,
|
||||||
FrontendOnly: frontendOnly,
|
FrontendOnly: frontendOnly,
|
||||||
OmitTest: omitTest,
|
OmitTest: omitTest,
|
||||||
OmitMd: omitMd,
|
OmitMd: omitMd,
|
||||||
|
IncludeInstructions: includeInstructions,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := buildCommand(cfg)
|
cmd := buildCommand(cfg)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ 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"
|
||||||
|
"git.flo.fo/FLO/dirmd/internal/instructions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
@@ -17,10 +19,12 @@ type Entry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Entries []Entry
|
Entries []Entry
|
||||||
AbsRoot string
|
AbsRoot string
|
||||||
SingleFile bool
|
SingleFile bool
|
||||||
OmitTest bool
|
OmitTest bool
|
||||||
|
Commit *git.CommitInfo
|
||||||
|
Instructions string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(cfg *config.Config) (*Result, error) {
|
func Run(cfg *config.Config) (*Result, error) {
|
||||||
@@ -53,11 +57,19 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commitInfo := git.GetCommitInfo(filepath.Dir(cfg.AbsRoot))
|
||||||
|
var instContent string
|
||||||
|
if cfg.IncludeInstructions {
|
||||||
|
instContent, _ = instructions.Read(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,
|
||||||
|
Instructions: instContent,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +85,17 @@ func walkDirectory(cfg *config.Config) (*Result, error) {
|
|||||||
|
|
||||||
result := &Result{AbsRoot: cfg.AbsRoot, OmitTest: cfg.OmitTest}
|
result := &Result{AbsRoot: cfg.AbsRoot, OmitTest: cfg.OmitTest}
|
||||||
|
|
||||||
|
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 {
|
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