Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1dd808d1e | ||
|
|
d247095e87 | ||
|
|
66579d068d | ||
|
|
4c14ef2cee | ||
|
|
0f94e50746 | ||
|
|
c9aa028b3c | ||
|
|
bca125cb92 | ||
|
|
7d73901631 | ||
|
|
6663cb25f8 | ||
|
|
8a28364839 | ||
|
|
894f97820e | ||
|
|
fb646a94be | ||
|
|
4bcae1358d | ||
|
|
2428a33046 | ||
|
|
a48fe55d4b | ||
|
|
0df346dbf9 | ||
|
|
d7f4c6826b | ||
|
|
79be211df3 | ||
|
|
ba87d6184b | ||
|
|
180c3f0eba | ||
|
|
be0c761cb3 | ||
|
|
4ebf3bd60f | ||
|
|
63547e74c2 | ||
|
|
79b630b622 |
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
Generate markdown documentation from directory structure. Optimized for LLM ingestion.
|
Generate markdown documentation from directory structure. Optimized for LLM ingestion.
|
||||||
|
|
||||||
|
<video src="./docs/videos/dirmd.webm" autoplay loop muted playsinline></video>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Recursive directory traversal
|
- Recursive directory traversal
|
||||||
@@ -17,6 +19,13 @@ Generate markdown documentation from directory structure. Optimized for LLM inge
|
|||||||
- Frontend only mode (include only frontend files and `README.md`)
|
- Frontend only mode (include only frontend files and `README.md`)
|
||||||
- Omit test files and inline test blocks (Go, Java, Rust)
|
- Omit test files and inline test blocks (Go, Java, Rust)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Only for linux.
|
||||||
|
Download binary from release page or install with go
|
||||||
|
```bash
|
||||||
|
go install git.flo.fo/FLO/dirmd@latest
|
||||||
|
```
|
||||||
|
|
||||||
## Use with Git Post Hook
|
## Use with Git Post Hook
|
||||||
combine with git post hook to sync with proton drive on each commit
|
combine with git post hook to sync with proton drive on each commit
|
||||||
|
|
||||||
|
|||||||
@@ -173,8 +173,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
absOutput = filepath.Join(filepath.Dir(absOutput), filepath.Base(absOutput)+"_dirmd")
|
|
||||||
|
|
||||||
if !appendMode {
|
if !appendMode {
|
||||||
if _, err := os.Stat(absOutput); err == nil && !force {
|
if _, err := os.Stat(absOutput); err == nil && !force {
|
||||||
return fmt.Errorf("output directory %s already exists (use -f to overwrite)", absOutput)
|
return fmt.Errorf("output directory %s already exists (use -f to overwrite)", absOutput)
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# Automating sending files to proton drive on commits
|
|
||||||
|
|
||||||
If you add this to any repos, and have dirmd in path and logged in on proton drive cli, then after each commit the newest code is pushed to proton drive, as vertical slices. If you do not want vertical slices of the code omit --vertical-slices. Also check out the other flags of dirmd if required.
|
|
||||||
|
|
||||||
## repos-root/.git/hooks/post-commit
|
|
||||||
|
|
||||||
create this file
|
|
||||||
|
|
||||||
```sh
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
if ! command -v dirmd &>/dev/null; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
||||||
REPO_NAME=$(basename "$REPO_ROOT")
|
|
||||||
|
|
||||||
dirmd -o "/tmp/${REPO_NAME}_new" "$REPO_ROOT" -f --vertical-slices --proton-drive
|
|
||||||
```
|
|
||||||
|
|
||||||
and then make it executable with something like `chmod +x .git/hooks/post-commit`
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 522 KiB |
Binary file not shown.
Binary file not shown.
@@ -28,6 +28,9 @@ var DefaultIgnores = []string{
|
|||||||
".idea",
|
".idea",
|
||||||
".vscode",
|
".vscode",
|
||||||
"dependency-reduced-pom.xml",
|
"dependency-reduced-pom.xml",
|
||||||
|
"__pycache__",
|
||||||
|
"venv",
|
||||||
|
"env",
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultExts = []string{
|
var DefaultExts = []string{
|
||||||
@@ -43,6 +46,7 @@ var DefaultExts = []string{
|
|||||||
".jar",
|
".jar",
|
||||||
".war",
|
".war",
|
||||||
".ear",
|
".ear",
|
||||||
|
".pyc", ".pyo",
|
||||||
}
|
}
|
||||||
|
|
||||||
var FrontendExts = []string{
|
var FrontendExts = []string{
|
||||||
|
|||||||
+17
-11
@@ -30,7 +30,6 @@ func CheckAuth() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureRemoteParent creates the parent remote directory if it doesn't exist.
|
|
||||||
func EnsureRemoteParent(remoteDir string) error {
|
func EnsureRemoteParent(remoteDir string) error {
|
||||||
binary, err := Lookup()
|
binary, err := Lookup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -38,37 +37,45 @@ func EnsureRemoteParent(remoteDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parentDir := filepath.Dir(remoteDir)
|
parentDir := filepath.Dir(remoteDir)
|
||||||
|
|
||||||
// Try create-folder on the grandparent for the parent
|
|
||||||
grandparent := filepath.Dir(parentDir)
|
grandparent := filepath.Dir(parentDir)
|
||||||
parentName := filepath.Base(parentDir)
|
parentName := filepath.Base(parentDir)
|
||||||
|
|
||||||
cmd := exec.Command(binary, "filesystem", "create-folder", grandparent, parentName)
|
cmd := exec.Command(binary, "filesystem", "create-folder", grandparent, parentName)
|
||||||
cmd.Stdout = nil
|
cmd.Stdout = nil
|
||||||
cmd.Stderr = nil
|
cmd.Stderr = nil
|
||||||
cmd.Run() // Ignore errors — folder may already exist
|
cmd.Run()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadDir uploads an entire local directory to Proton Drive.
|
func CleanRemote(remoteDir string) {
|
||||||
// The local directory's basename becomes the remote directory name.
|
binary, err := Lookup()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(binary, "filesystem", "delete", remoteDir)
|
||||||
|
cmd.Stdout = nil
|
||||||
|
cmd.Stderr = nil
|
||||||
|
cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
func UploadDir(localDir, remoteDir string) error {
|
func UploadDir(localDir, remoteDir string) error {
|
||||||
binary, err := Lookup()
|
binary, err := Lookup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("proton-drive not found in PATH: %w", err)
|
return fmt.Errorf("proton-drive not found in PATH: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure parent of remote dir exists
|
|
||||||
if err := EnsureRemoteParent(remoteDir); err != nil {
|
if err := EnsureRemoteParent(remoteDir); err != nil {
|
||||||
return fmt.Errorf("cannot prepare remote directory: %w", err)
|
return fmt.Errorf("cannot prepare remote directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload to parent — the local dir's basename creates the remote dir
|
CleanRemote(remoteDir)
|
||||||
|
|
||||||
remoteParent := filepath.Dir(remoteDir)
|
remoteParent := filepath.Dir(remoteDir)
|
||||||
|
|
||||||
cmd := exec.Command(binary, "filesystem", "upload", localDir, remoteParent,
|
cmd := exec.Command(binary, "filesystem", "upload", localDir, remoteParent,
|
||||||
"--conflict-strategy", "replace")
|
"--folder-conflict-strategy", "replace", "--file-conflict-strategy", "replace")
|
||||||
|
|
||||||
var stderr strings.Builder
|
var stderr strings.Builder
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
@@ -80,7 +87,6 @@ func UploadDir(localDir, remoteDir string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload uploads a single local file to a remote path on Proton Drive.
|
|
||||||
func Upload(localPath, remotePath string) error {
|
func Upload(localPath, remotePath string) error {
|
||||||
binary, err := Lookup()
|
binary, err := Lookup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -111,7 +117,7 @@ func Upload(localPath, remotePath string) error {
|
|||||||
|
|
||||||
func runUpload(binary, localPath, remoteDir string) error {
|
func runUpload(binary, localPath, remoteDir string) error {
|
||||||
cmd := exec.Command(binary, "filesystem", "upload", localPath, remoteDir,
|
cmd := exec.Command(binary, "filesystem", "upload", localPath, remoteDir,
|
||||||
"--conflict-strategy", "replace")
|
"--folder-conflict-strategy", "replace", "--file-conflict-strategy", "replace")
|
||||||
|
|
||||||
var stderr strings.Builder
|
var stderr strings.Builder
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -89,18 +89,10 @@ func Generate(cfg *config.Config) GenerateResult {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.VerticalSlices {
|
|
||||||
if err := drive.UploadDir(cfg.OutputPath, remoteDir); err != nil {
|
if err := drive.UploadDir(cfg.OutputPath, remoteDir); err != nil {
|
||||||
result.Error = fmt.Errorf("drive upload: %w", err)
|
result.Error = fmt.Errorf("drive upload: %w", err)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
indexPath := filepath.Join(cfg.OutputPath, "index_"+repoName+".md")
|
|
||||||
if err := drive.Upload(indexPath, remoteDir); err != nil {
|
|
||||||
result.Error = fmt.Errorf("drive upload: %w", err)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.Uploaded = true
|
result.Uploaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user