first commit

This commit is contained in:
David Ali
2026-01-29 01:08:41 +01:00
commit 230c326e9f
3 changed files with 269 additions and 0 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Dávid Ali
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

101
README.md Normal file
View File

@@ -0,0 +1,101 @@
# cflat (c♭)
**Make your C++ project fall flat.**
## What is this?
`cflat` takes your complex directory structure and exports it into a single text file. It merges your entire C/C++ project into one massive stream of code.
**Note:** It does not actually delete your folders. It just ignores them in the output. Your architecture is safe.
## Why?
Civilized people keep files separate. You, apparently, need them together.
* **AI Fodder:** Feed your entire codebase to an LLM so it can hallucinate a fix for that segfault you've been ignoring.
* **Code Reviews:** Send a single 50,000-line file to your reviewer. Assert dominance.
* **Grepping:** Search for a string across the project instantly because you still haven't memorized how to use `grep -r`.
* **Spite:** Annoy your lead architect by rendering their carefully crafted folder structure irrelevant.
## Features
* **Recursive:** It hunts down source files in every nested folder. Deep sub-directories are no longer safe places to hide bad code.
* **Fake Elegance:** It runs `clang-format` (if installed) so your spaghetti code at least *looks* professional in the export.
* **Hygiene:** It ignores `obj`, `bin`, `.git`, and other binary garbage that nobody wants to read.
* **Self-Preservation:** It automatically adds itself and the output file to `.gitignore`. This prevents you from accidentally committing a 12MB text file.
* **Flexible:** Run it on the current folder, a target folder, or exclude specific shameful directories.
## Installation
Trusting random scripts from the internet is a key part of the developer experience.
```bash
curl -O https://git.alidavid.hu/david/cflat/raw/branch/main/cflat
```
Make it executable:
```bash
chmod +x cflat
```
## Usage
**Default Behavior:**
Run this in your project root to flatten everything.
```bash
./cflat
```
**Target a specific folder:**
```bash
./cflat ../my-other-project/
```
**The "Denial" option (Exclusions):**
Exclude folders you don't want the world (or the AI) to see.
```bash
./cflat -e build -e node_modules -e "legacy_code"
```
### ⚠️ Wildcards (Important)
Your shell expands `*` before the script sees it. If you don't use quotes, the script will break.
```bash
# Good:
./cflat -e "*.tmp"
# Bad (The shell expands this and confuses the script):
./cflat -e *.tmp
```
**Need help?**
```bash
./cflat -?
```
## Output
The script generates a file named `cflat-export-YYYY-MM-DD.txt`. It contains:
1. **Project Stats:** How many lines of code you are dealing with.
2. **The Tree:** A visual map of the directory structure.
3. **The Cast:** A list of all classes and structs found.
4. **The Blob:** The full content of every source file, stitched together.
## Requirements
* **OS:** Linux or macOS. (Windows users: please use WSL).
* **Shell:** Bash.
* **Optional:**
* `tree`: For structure visualization.
* `clang-format`: For code cleaning.
## License
MIT

147
cflat Executable file
View File

@@ -0,0 +1,147 @@
#!/bin/bash
# --- cflat (c♭) ---
# Flattens C/C++ projects into a single text file.
# https://git.alidavid.hu/david/cflat
VERSION="1.0.0"
SCRIPT_NAME=$(basename "$0")
# 1. HELP FUNCTION
function show_help {
echo "cflat (c♭) v$VERSION"
echo "Flattens C/C++ projects into a single text file."
echo "https://git.alidavid.hu/david/cflat"
echo ""
echo "Usage: ./$SCRIPT_NAME [DIRECTORY] [-e PATTERN]..."
echo ""
echo "Options:"
echo " -e, --exclude Exclude exact path relative to the target directory."
echo " 'aaa.a' -> Excludes ./aaa.a (Target root only)"
echo " 'src/lib' -> Excludes ./src/lib (and its contents)"
echo " '*.txt' -> Excludes ./*.txt (Target root only)"
echo " '*/*.txt' -> Excludes ./*/*.txt (Recursive)"
echo " -h, --help, -? Show this message"
exit 0
}
# 2. ARGUMENT PARSING
TARGET_DIR="."
EXCLUDE_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help|-\?) show_help ;;
-e|--exclude)
if [[ -n "$2" && "$2" != -* ]]; then
# Clean trailing slash from pattern to match find output safely
CLEAN_PAT="${2%/}"
EXCLUDE_ARGS+=("$CLEAN_PAT")
shift 2
else
echo "Error: --exclude requires an argument." >&2; exit 1
fi
;;
-*) echo "Error: Unknown option $1" >&2; exit 1 ;;
*) TARGET_DIR="$1"; shift ;;
esac
done
# Clean target directory (remove trailing slash)
TARGET_DIR=${TARGET_DIR%/}
# 3. BUILD FIND COMMAND
# We anchor every exclusion to the TARGET_DIR.
# This ensures that exclusions are strict (e.g., 'aaa.a' only matches the root file).
# Default ignores (Hidden files, obj, bin, build, and our own output)
BASE_IGNORES=(
-path '*/.*' -o
-path '*/obj' -o
-path '*/bin' -o
-path '*/build' -o
-name "cflat-*.txt" -o
-name "$SCRIPT_NAME"
)
# Build User Excludes
USER_PRUNE_ARGS=()
if [[ ${#EXCLUDE_ARGS[@]} -gt 0 ]]; then
for pattern in "${EXCLUDE_ARGS[@]}"; do
# Construct the strict path match anchored to TARGET_DIR
# e.g., if target is "." and pattern is "file", we match "./file"
MATCH_PATH="$TARGET_DIR/$pattern"
USER_PRUNE_ARGS+=("-path" "$MATCH_PATH" "-o")
done
fi
# Combine logic: ( UserExcludes OR BaseIgnores ) -prune
FINAL_PRUNE_ARGS=("(" "${USER_PRUNE_ARGS[@]}" "${BASE_IGNORES[@]}" ")" "-prune")
# 4. CONFIGURATION
TIMESTAMP=$(date +"%Y-%m-%dT%H-%M-%S")
OUTPUT="cflat-export-$TIMESTAMP.txt"
MAX_SIZE_KB=100
HAS_CLANG=$(command -v clang-format)
# 5. HEADER
echo "Generating export for: $TARGET_DIR"
echo "C-FLAT EXPORT" > "$OUTPUT"
echo "Generated on: $(date)" >> "$OUTPUT"
echo "----------------------------------------" >> "$OUTPUT"
# 6. STATISTICS
echo "PROJECT STATISTICS:" >> "$OUTPUT"
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.c" -o -name "*.h" \) -print0 | xargs -0 -r wc -l | tail -n 1 >> "$OUTPUT"
# 7. KEY DEFINITIONS
echo -e "\nKEY DEFINITIONS FOUND:" >> "$OUTPUT"
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -type f -exec grep -nE "class [A-Za-z0-9_]+|struct [A-Za-z0-9_]+" {} + >> "$OUTPUT"
# 8. PROJECT STRUCTURE
echo -e "\nPROJECT STRUCTURE:" >> "$OUTPUT"
# Print all non-pruned paths for the tree visualization
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -print | sort | sed -e "s;[^/]*/;|____;g;s;____|; |;g" >> "$OUTPUT"
echo -e "\n--- FILE CONTENTS ---\n" >> "$OUTPUT"
# 9. CONTENT EXPORT
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -type f -print | while read -r file; do
if [[ "$file" =~ \.(cpp|hpp|h|c|cc|cxx)$ ]]; then
echo "--- START OF SOURCE FILE: $file ---" >> "$OUTPUT"
if [ -n "$HAS_CLANG" ]; then
clang-format --style=LLVM "$file" >> "$OUTPUT"
else
cat "$file" >> "$OUTPUT"
fi
echo -e "\n--- END OF SOURCE FILE: $file ---\n" >> "$OUTPUT"
elif [[ "$file" == *"Makefile"* ]] || [[ "$file" =~ \.(txt|py|sh|md|cmake)$ ]]; then
echo "--- START OF CONFIG/SCRIPT: $file ---" >> "$OUTPUT"
cat "$file" >> "$OUTPUT"
echo -e "\n--- END OF CONFIG/SCRIPT: $file ---\n" >> "$OUTPUT"
elif grep -qI . "$file"; then
FILE_SIZE=$(du -k "$file" | cut -f1)
if [ "$FILE_SIZE" -lt "$MAX_SIZE_KB" ]; then
echo "--- START OF ASSET FILE: $file ---" >> "$OUTPUT"
cat "$file" >> "$OUTPUT"
echo -e "\n--- END OF ASSET FILE: $file ---\n" >> "$OUTPUT"
fi
fi
done
# 10. AUTO-GITIGNORE
GITIGNORE="$TARGET_DIR/.gitignore"
if [ -f "$GITIGNORE" ]; then
if ! grep -qxF "$SCRIPT_NAME" "$GITIGNORE"; then
echo -e "\n# C-Flat ignores\n$SCRIPT_NAME" >> "$GITIGNORE"
fi
if ! grep -qF "cflat-*.txt" "$GITIGNORE"; then
echo "cflat-*.txt" >> "$GITIGNORE"
fi
fi
echo "Done: Output saved to $OUTPUT"