### Development Environment Setup Commands Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-01-project-foundation.plan.md Commands to set up a Python development environment using `uv`. This includes creating a virtual environment, activating it, and installing the project in editable mode with development dependencies. ```bash uv venv source .venv/bin/activate uv pip install -e ".[dev]" ``` -------------------------------- ### Tallyman CLI Usage Examples Source: https://context7.com/mikeckennedy/tallyman/llms.txt Demonstrates common command-line interface commands for Tallyman, including basic analysis, specifying paths, interactive setup, disabling color, generating image summaries, and checking the version. ```bash # Basic usage - analyze current directory tallyman # Analyze a specific project path tallyman /path/to/project # Re-run interactive setup TUI to configure exclusions tallyman --setup # Disable colored output (also respects NO_COLOR env variable) tallyman --no-color # Generate PNG summary card (saved to Desktop or current directory) tallyman --image # dark theme tallyman --image-light # light theme # Check version tallyman --version ``` -------------------------------- ### Run TUI Setup Function in Python Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-03-configuration-and-tui.plan.md The `run_setup` function is the entry point for launching the TUI setup application. It initializes the `SetupApp` with the project root, gitignore specifications, and existing exclusions. It returns the set of excluded directory paths or None if the user quits without saving. ```python def run_setup(root: Path, gitignore_spec, existing_exclusions: set[str]) -> set[str] | None: """ Launch the TUI setup app. Returns the set of excluded directory paths, or None if user quit without saving. """ app = SetupApp(root, gitignore_spec, existing_exclusions) result = app.run() return result ``` -------------------------------- ### Install Tallyman using uv Source: https://github.com/mikeckennedy/tallyman/blob/main/README.md This command installs the Tallyman tool using the 'uv' package manager. It's a straightforward installation process for the command-line utility. ```bash uv tool install tallyman-metrics ``` -------------------------------- ### Re-run Tallyman Setup Configuration Source: https://github.com/mikeckennedy/tallyman/blob/main/README.md Execute the `--setup` command to re-initiate the interactive TUI for Tallyman configuration. This allows you to re-configure directory exclusions or mark directories as spec directories, saving your choices to `.tally-config.toml` in the project root. ```bash tallyman --setup ``` -------------------------------- ### Python: Initialize and Compose Textual App for Directory Setup Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-03-configuration-and-tui.plan.md Initializes a Textual App for directory setup, defining key bindings for user interaction. The `compose` method yields the Header, a dynamically built Tree widget, and the Footer. Dependencies include Textual widgets and app components. ```python from textual.widgets import Tree, Header, Footer from textual.app import App, ComposeResult from pathlib import Path import pathspec class SetupApp(App): """First-run setup: choose which directories to include.""" BINDINGS = [ ('space', 'toggle_node', 'Toggle include/exclude'), ('enter', 'save_and_exit', 'Save config & run'), ('escape', 'quit_no_save', 'Quit without saving'), ] def __init__(self, root: Path, gitignore_spec: pathspec.PathSpec, existing_exclusions: set[str]): super().__init__() self.root = root self.gitignore_spec = gitignore_spec self.existing_exclusions = existing_exclusions self.user_excluded: set[str] = set(existing_exclusions) def compose(self) -> ComposeResult: yield Header() yield self._build_tree() yield Footer() def _build_tree(self) -> Tree: tree = Tree(self.root.name) tree.root.data = {'path': '', 'gitignored': False, 'excluded': False} self._populate(tree.root, self.root, '') tree.root.expand_all() return tree ``` -------------------------------- ### Tallyman CLI Entry Point and Basic Functionality Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-01-project-foundation.plan.md Implements the main command-line interface for Tallyman. It uses `argparse` to define arguments for the target path, setup TUI, and color disabling. The `main` function parses these arguments and prints a placeholder message, indicating the tool's current status. ```python import argparse import sys def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog='tallyman', description='Summarize codebase size by language.', ) parser.add_argument( 'path', nargs='?', default='.', help='Directory to analyze (default: current directory)', ) parser.add_argument( '--setup', action='store_true', help='Re-run the setup TUI even if .tally-config.toml exists', ) parser.add_argument( '--no-color', action='store_true', help='Disable colored output', ) return parser def main() -> None: parser = build_parser() args = parser.parse_args() print(f'Tallyman v0.1.0 - analyzing: {args.path}') print('(not yet implemented)') sys.exit(0) ``` -------------------------------- ### Verification Commands for Tallyman CLI Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-01-project-foundation.plan.md Commands to verify the installation and basic functionality of the `tallyman` command-line tool. These commands check the help message, the default execution, and execution via `python -m tallyman`, ensuring the placeholder output is displayed correctly. ```bash tallyman --help tallyman python -m tallyman ``` -------------------------------- ### Install Tallyman (Dev Mode) Source: https://github.com/mikeckennedy/tallyman/blob/main/CLAUDE.md Installs Tallyman in development mode using uv pip, including development dependencies. ```bash # Install (dev mode) uv pip install -e ".[dev]" ``` -------------------------------- ### TUI Setup API Source: https://context7.com/mikeckennedy/tallyman/llms.txt Launch the interactive Textual TUI for configuring directory inclusions and exclusions. Allows users to visually manage project settings. ```APIDOC ## TUI Setup API ### Description Launch the interactive Textual TUI for configuring directory inclusions and exclusions. ### Method N/A (Code Example) ### Endpoint N/A (Code Example) ### Parameters N/A ### Request Example ```python from pathlib import Path from tallyman.tui.setup_app import run_setup from tallyman.walker import load_gitignore root = Path(".") gitignore_spec = load_gitignore(root) # Existing config to pre-populate (empty sets for fresh setup) existing_exclusions = {"node_modules", "build"} existing_specs = {"planning"} # Launch TUI - returns (excluded_dirs, spec_dirs) or None if cancelled result = run_setup(root, gitignore_spec, existing_exclusions, existing_specs) if result is not None: excluded_dirs, spec_dirs = result print(f"User excluded: {excluded_dirs}") print(f"User spec dirs: {spec_dirs}") else: print("Setup cancelled by user") ``` ### Response #### Success Response (200) Returns a tuple of `(excluded_dirs, spec_dirs)` if the setup is completed, or `None` if cancelled. TUI features: - Directory tree with expand/collapse (arrow keys) - Toggle include/exclude (x key) - Toggle spec directory (s key) - Gitignored directories shown but not toggleable - Auto-detected spec directories marked with "S" #### Response Example ``` User excluded: {'node_modules', 'build', 'dist'} User spec dirs: {'planning', 'docs/specs'} ``` ``` -------------------------------- ### Integrate TUI and Config Loading in Python CLI Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-03-configuration-and-tui.plan.md The `main` function orchestrates the Tallyman CLI. It parses arguments, resolves the project path, loads gitignore and configuration files. If no config exists or `--setup` is used, it launches the TUI setup; otherwise, it loads the existing configuration. It then proceeds to walk the project, count lines, aggregate results, and display them. ```python def main() -> None: parser = build_parser() args = parser.parse_args() root = Path(args.path).resolve() if not root.is_dir(): print(f'Error: {root} is not a directory', file=sys.stderr) sys.exit(1) # Load gitignore gitignore_spec = load_gitignore(root) # Load or create config config_path = root / CONFIG_FILENAME existing_config = find_config(root) if existing_config and not args.setup: # Config exists, load it excluded_dirs = load_config(existing_config) else: # First run or --setup: launch TUI existing_exclusions = load_config(existing_config) if existing_config else set() result = run_setup(root, gitignore_spec, existing_exclusions) if result is None: # User quit without saving print('Setup cancelled.') sys.exit(0) excluded_dirs = result save_config(config_path, excluded_dirs) # Walk, count, aggregate, display file_results = [] for file_path, language in walk_project(root, excluded_dirs): counts = count_lines(file_path, language) file_results.append((language, counts)) result = aggregate(iter(file_results)) # Display (Phase 4 replaces this with rich output) for stats in result.by_language: print(f'{stats.language.name}: {stats.total_lines:,} lines') ``` -------------------------------- ### Project Configuration with pyproject.toml Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-01-project-foundation.plan.md Defines the project's metadata, dependencies, script entry points, and build system configuration using PEP 621 format. It specifies project name, version, description, Python version requirements, and lists key dependencies like textual, rich, and pathspec. It also configures build targets and linter settings for Ruff. ```toml [project] name = "tallyman" version = "0.1.0" description = "A command-line tool that summarizes codebase size by language." readme = "README.md" requires-python = ">=3.14" license = "MIT" dependencies = [ "textual>=1.0.0", "rich>=13.0.0", "pathspec>=0.12.0", ] [project.scripts] tallyman = "tallyman.cli:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/tallyman"] [tool.ruff] line-length = 120 target-version = "py314" [tool.ruff.lint] select = ["E", "F", "W", "I"] [tool.ruff.format] quote-style = "single" [tool.pytest.ini_options] testpaths = ["tests"] ``` -------------------------------- ### TUI Pre-population with Nested Configs Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/004-nested-config-discovery/00-plan.md In `cli.py`, before initializing the TUI setup, the `discover_nested_configs` function is called with the project root. The results are merged into the existing configuration. This ensures that the TUI's setup widget accurately reflects any nested exclusions or specifications as pre-applied when the user interacts with it. ```python root_config = load_config(root / CONFIG_FILENAME) nested_configs = discover_nested_configs(root) merged_config = merge_configs(root_config, nested_configs) # Assuming merge_configs exists # ... then launch TUI setup with merged_config ``` -------------------------------- ### Python Package Initialization and Main Entry Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-01-project-foundation.plan.md Sets the package version and provides an entry point for running the package as a module. `__init__.py` defines the package version, while `__main__.py` allows execution via `python -m tallyman` by importing and calling the main function from `cli.py`. ```python # src/tallyman/__init__.py __version__ = '0.1.0' ``` ```python # src/tallyman/__main__.py from tallyman.cli import main main() ``` -------------------------------- ### TUI Setup API: Launch Interactive Textual TUI Source: https://context7.com/mikeckennedy/tallyman/llms.txt Launches an interactive Textual TUI for configuring directory inclusions and exclusions. Users can navigate the directory tree, toggle include/exclude status, and mark directories as spec directories. Gitignored directories are displayed but cannot be toggled. ```python from pathlib import Path from tallyman.tui.setup_app import run_setup from tallyman.walker import load_gitignore root = Path(".") gitignore_spec = load_gitignore(root) # Existing config to pre-populate (empty sets for fresh setup) existing_exclusions = {"node_modules", "build"} existing_specs = {"planning"} # Launch TUI - returns (excluded_dirs, spec_dirs) or None if cancelled result = run_setup(root, gitignore_spec, existing_exclusions, existing_specs) if result is not None: excluded_dirs, spec_dirs = result print(f"User excluded: {excluded_dirs}") print(f"User spec dirs: {spec_dirs}") else: print("Setup cancelled by user") ``` -------------------------------- ### Run Tallyman for Project Analysis Source: https://github.com/mikeckennedy/tallyman/blob/main/README.md These commands demonstrate how to run Tallyman to analyze a project's codebase. You can analyze the current directory, a specific path, or use flags for setup, disabling color, and generating image summaries. ```bash tallyman # analyze current directory tallyman /path/to/project # analyze a specific path tallyman --setup # re-run the interactive setup tallyman --no-color # disable colored output tallyman --image # save a PNG summary card to Desktop tallyman --image-light # light-themed PNG variant ``` -------------------------------- ### Update `run_setup` Signature in Python Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/002-specs-category/phase-02-tui-s-key.plan.md Updates the signature of the `run_setup` function to accept `existing_exclusions` and `existing_specs` as arguments. This function initializes and runs the TUI setup app, returning the final tuple of excluded and spec directories, or None if the user quits without saving. ```python def run_setup( root: Path, gitignore_spec: GitIgnoreSpec, existing_exclusions: set[str], existing_specs: set[str], ) -> tuple[set[str], set[str]] | None: """Launch the TUI setup app. Returns (excluded_dirs, spec_dirs), or None if the user quit without saving. """ app = SetupApp(root, gitignore_spec, existing_exclusions, existing_specs) return app.run() ``` -------------------------------- ### Run Tallyman CLI Commands Source: https://github.com/mikeckennedy/tallyman/blob/main/CLAUDE.md Provides common commands for running the Tallyman CLI tool to analyze codebases, including options for specifying a path, re-running setup, and disabling color output. ```bash # Run tallyman # analyze current directory tallyman /path/to/project # analyze specific path tallyman --setup # re-run interactive TUI setup tallyman --no-color # disable colors ``` -------------------------------- ### Configuration File Example for Tallyman Specs Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/002-specs-category/00-plan-introduction.plan.md Illustrates the TOML configuration file structure for Tallyman, specifically showing how to define custom directories for the 'Specs' category alongside the default 'exclude' directories. This configuration is used to persist user-designated spec directories. ```toml # Generated by tallyman. Edit manually or re-run: tallyman --setup [exclude] directories = [ "vendor", ] [specs] directories = [ "docs/architecture", "project/requirements", ] ``` -------------------------------- ### Update CLI Integration with TUI Return Type (Python) Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/002-specs-category/phase-02-tui-s-key.plan.md This Python code snippet updates the CLI flow to incorporate the new TUI return type. It handles loading existing configurations or running a setup process if no configuration is found. It then walks the project directory, excluding specified directories and counting lines of code for each file based on its language. ```python if existing_config and not args.setup: config = load_config(existing_config) excluded_dirs = config.excluded_dirs spec_dirs = config.spec_dirs else: existing = load_config(existing_config) if existing_config else TallyConfig(set(), set()) result = run_setup(root, gitignore_spec, existing.excluded_dirs, existing.spec_dirs) if result is None: print('Setup cancelled.') sys.exit(0) excluded_dirs, spec_dirs = result save_config(config_path, excluded_dirs, spec_dirs) # Walk and count for file_path, language in walk_project(root, excluded_dirs, gitignore_spec, spec_dirs): counts = count_lines(file_path, language) file_results.append((language, counts)) ``` -------------------------------- ### Initialize Spec Directory Tracking in Constructor (Python) Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/002-specs-category/phase-02-tui-s-key.plan.md Updates the `SetupApp` constructor to accept and store a set of `existing_specs`. This allows the application to load and maintain user-defined spec directories across sessions, alongside existing exclusions. ```python def __init__( self, root: Path, gitignore_spec: GitIgnoreSpec, existing_exclusions: set[str], existing_specs: set[str], ) -> None: super().__init__() self.root = root self.title = f'Setup Tallyman for {root.name}' self.gitignore_spec = gitignore_spec self.user_excluded: set[str] = set(existing_exclusions) self.user_spec_dirs: set[str] = set(existing_specs) ``` -------------------------------- ### Initialize Display Module with Rich Console Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-04-output-and-formatting.plan.md Sets up the main display function using Rich's Console for colored and fallback-compatible terminal output. It initializes the console with options for color and highlighting, and calls sub-functions to display different sections of the results. ```python from rich.console import Console from rich.text import Text def display_results(result: TallyResult, no_color: bool = False) -> None: """Render the full tallyman output to the terminal.""" console = Console(no_color=no_color, highlight=False) _display_languages(console, result) _display_separator(console) _display_category_totals(console, result) _display_percentage_bar(console, result) ``` -------------------------------- ### Update TUI Return Type for Exclusions and Specs in Python Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/002-specs-category/phase-02-tui-s-key.plan.md Changes the return type of the TUI setup app to provide both excluded directories and spec directories as a tuple of sets. This allows for more comprehensive state management upon saving, returning `(cleaned_excluded, cleaned_specs)` or `None` if cancelled. ```python class SetupApp(App[tuple[set[str], set[str]] | None]): """First-run setup: choose which directories to include/exclude and designate specs.""" # ... def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == 'save': cleaned_excluded = self._clean_exclusions(self.user_excluded) cleaned_specs = self._clean_exclusions(self.user_spec_dirs) self.exit((cleaned_excluded, cleaned_specs)) elif event.button.id == 'cancel': self.exit(None) ``` -------------------------------- ### Run Tallyman Tests and Linting Source: https://github.com/mikeckennedy/tallyman/blob/main/CLAUDE.md Demonstrates commands for running tests using pytest and for linting and formatting code with ruff. ```bash # Tests pytest # all 84 tests pytest -v # verbose pytest tests/test_walker.py # single module # Lint & Format ruff check src/ tests/ ruff format src/ tests/ ``` -------------------------------- ### Complete `cli.py` Main Flow in Python Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-04-output-and-formatting.plan.md This is the complete main function for the `cli.py` script in the Tallyman project. It handles argument parsing, directory validation, gitignore loading, configuration loading or setup, project file traversal, line counting, aggregation of results, and finally, displaying the results to the console. ```python def main() -> None: parser = build_parser() args = parser.parse_args() root = Path(args.path).resolve() if not root.is_dir(): console = Console(stderr=True) console.print(f'[red]Error:[/red] {root} is not a directory') sys.exit(1) # Gitignore gitignore_spec = load_gitignore(root) # Config config_path = root / CONFIG_FILENAME existing_config = find_config(root) if existing_config and not args.setup: excluded_dirs = load_config(existing_config) else: existing_exclusions = load_config(existing_config) if existing_config else set() result = run_setup(root, gitignore_spec, existing_exclusions) if result is None: sys.exit(0) excluded_dirs = result save_config(config_path, excluded_dirs) # Count file_results = [] for file_path, language in walk_project(root, excluded_dirs): counts = count_lines(file_path, language) file_results.append((language, counts)) # Aggregate tally = aggregate(iter(file_results)) # Display no_color = args.no_color or os.environ.get('NO_COLOR') is not None display_results(tally, no_color=no_color) ``` -------------------------------- ### Swap Docs Language to Spec Variant (Python) Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/002-specs-category/phase-01-language-config-walker.plan.md Demonstrates how documentation files (`language.category == 'docs'`) located within a detected spec directory (`dir_is_spec`) are transformed into their spec variants using the `as_spec` function. ```python # Swap docs → specs if inside a spec directory if dir_is_spec and language.category == 'docs': language = as_spec(language) ``` -------------------------------- ### Font Bundling and Packaging Configuration (TOML) Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/003-image-export/00-plan-introduction.plan.md This details the process of bundling custom fonts and configuring the project packaging. It involves downloading font files, placing them in the correct directory structure, and updating the `pyproject.toml` file to include these fonts as package data and add Pillow as a dependency. ```toml # Update pyproject.toml to include font files as package data # Add Pillow to dependencies: Pillow>=10.0.0 ``` -------------------------------- ### Walk Project Directory Tree - Python Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-02-core-counting-engine.plan.md Recursively walks a project directory, yielding countable source files. It skips directories matching gitignore patterns, excluded directories from configuration, non-language-recognized files, binary files, and symlinks. It uses `pathspec` for gitignore matching and `os.walk` for directory traversal. ```Python from pathlib import Path from collections.abc import Iterator def walk_project( root: Path, excluded_dirs: set[str], ) -> Iterator[tuple[Path, Language]]: """ Yield (file_path, language) for every countable source file under root. excluded_dirs: set of directory paths relative to root that should be skipped (e.g. {'static/external', 'vendor'}). """ ``` -------------------------------- ### Handle Empty Projects in Python Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-04-output-and-formatting.plan.md This code snippet provides a way to gracefully handle projects where no recognized source files are found. It checks if the `result.by_language` is empty and, if so, prints a message to the console indicating that no files were found and exits the program. ```python if not result.by_language: console.print('[dim]No recognized source files found.[/dim]') sys.exit(0) ``` -------------------------------- ### Test Display Output Capture in Python Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-04-output-and-formatting.plan.md This Python code snippet outlines a test case for verifying the output of display functions in the Tallyman project. It demonstrates how to use `io.StringIO` to capture console output and `rich.console.Console` to direct output to this buffer, allowing for assertions on the generated text. ```python from io import StringIO from rich.console import Console def test_display_produces_output(): """display_results writes to the console without errors.""" # Build a TallyResult with known data # Create a Console with file=StringIO() and no_color=True # Call display functions # Assert the output string contains expected text ``` -------------------------------- ### Python CLI Main Function for Tallyman Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-02-core-counting-engine.plan.md The main entry point for the Tallyman CLI tool. It parses command-line arguments, resolves the project path, walks the directory to gather file counts, aggregates the results, and prints a plain-text summary. ```python import sys from pathlib import Path from typing import Iterator, Tuple, Set # Assuming build_parser, walk_project, count_lines, aggregate, Language, FileCount, TallyResult are defined elsewhere def main() -> None: parser = build_parser() args = parser.parse_args() root = Path(args.path).resolve() if not root.is_dir(): print(f'Error: {root} is not a directory', file=sys.stderr) sys.exit(1) # Load config (Phase 3 - for now, empty set) excluded_dirs: Set[str] = set() # Walk and count file_results: List[Tuple[Language, FileCount]] = [] for file_path, language in walk_project(root, excluded_dirs): counts = count_lines(file_path, language) file_results.append((language, counts)) # Aggregate result = aggregate(iter(file_results)) # Display (Phase 4 - for now, plain text) for stats in result.by_language: print(f'{stats.language.name}: {stats.total_lines:,} lines, ' f'{stats.non_blank_non_comment:,} excluding comments and blank lines') ``` -------------------------------- ### Generate Image Summary Card with Tallyman Source: https://github.com/mikeckennedy/tallyman/blob/main/README.md Use the `--image` or `--image-light` flags to generate a PNG summary card of your project's code statistics. The image is saved to your Desktop or current directory with a filename based on the project name. This feature is useful for sharing project summaries in READMEs, presentations, or social media. ```bash tallyman --image # dark theme (default) tallyman --image-light # light theme ``` -------------------------------- ### Run Test Suite and Linting with Bash Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/002-specs-category/phase-03-aggregator-display-tests.plan.md These bash commands are used to execute the full test suite for the Tallyman project and perform linting checks using Ruff. The first command runs pytest on the 'tests/' directory, while the subsequent commands check for code style violations and formatting consistency in both 'src/' and 'tests/' directories. ```bash python -m pytest tests/ -v ruff check src/ tests/ ruff format --check src/ tests/ ``` -------------------------------- ### Tallyman Configuration File Format Source: https://context7.com/mikeckennedy/tallyman/llms.txt Illustrates the TOML format for Tallyman's configuration file (.tally-config.toml), showing how to specify directories to exclude and directories that contain specifications. ```toml # Generated by tallyman. Edit manually or re-run: tallyman --setup [exclude] directories = [ "node_modules", "static/external", "vendor", ] [specs] directories = [ "docs/specifications", "planning", ] ``` -------------------------------- ### Testing Image Generation (Python) Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/003-image-export/00-plan-introduction.plan.md This outlines the test cases for the image generation functionality. It includes testing filename slugification, numeric suffix logic for file conflicts, desktop path resolution, validation of generated PNG files, and verification of theme rendering for both dark and light modes. Edge cases like empty results are also considered. ```python # Test slugification (spaces, special chars, unicode) # Test numeric suffix logic (no conflict, conflict with 1, conflict with multiple) # Test desktop path resolution (exists vs fallback) # Test image generation produces valid PNG (check file header bytes) # Test dark and light themes produce different images # Test with empty results (no recognized files) # Test color conversion from Rich names to RGB ``` -------------------------------- ### Traverse Project Directory and Identify Files (Python API) Source: https://context7.com/mikeckennedy/tallyman/llms.txt Illustrates using `walk_project` from `tallyman.walker` to iterate through a project directory. It yields tuples of file paths and their identified `Language` objects, respecting gitignore patterns and custom spec directories. ```python from pathlib import Path from tallyman.walker import walk_project, load_gitignore, SPEC_DIR_NAMES root = Path("/path/to/project") # Load gitignore patterns (searches up to git root) gitignore_spec = load_gitignore(root) # Define directories to exclude and spec directories excluded_dirs = {"node_modules", "vendor", ".venv"} spec_dirs = {"planning", "specs"} # Markdown/RST here becomes "specs" category # Walk yields (file_path, language) tuples for file_path, language in walk_project(root, excluded_dirs, gitignore_spec, spec_dirs): print(f"{file_path}: {language.name} ({language.category})") # Auto-detected spec directory names (case-insensitive) print(SPEC_DIR_NAMES) # frozenset({'specs', 'specifications', 'plans', 'agents'}) ``` -------------------------------- ### Image Generation Logic (Python) Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/003-image-export/00-plan-introduction.plan.md This outlines the core components of the image generation module in Python. It includes defining themes, a main entry point for generating images, and helper functions for drawing various elements like headers, category rows, and percentage bars. It also details font loading and color conversion. ```python # Theme dataclass with colors for background, text, dimmed text, bar background, card background # Dark and light theme presets # generate_image(result, directory, output_path, theme) main entry point # Helper functions: draw header, draw category rows, draw percentage bar, draw legend, draw attribution # Font loading with fallback chain # Rich color name to RGB conversion using rich.color.Color # Percentage bar with rounded rectangle segments using ImageDraw.rounded_rectangle() ``` -------------------------------- ### Tallyman Architecture Pipeline Source: https://github.com/mikeckennedy/tallyman/blob/main/CLAUDE.md Illustrates the linear pipeline architecture of the Tallyman CLI tool, showing the flow of data from the main entry point through various stages to the final display of results. ```python cli.main() → Load/create .tally-config.toml (or launch TUI) → walker.walk_project() yields (path, Language) tuples → counter.count_lines() returns FileCount per file → aggregator.aggregate() produces TallyResult → display.display_results() renders to terminal ``` -------------------------------- ### Python: Recursively Populate Directory Tree Widget Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-03-configuration-and-tui.plan.md Recursively populates a Textual Tree widget with directories and files from a given path. It checks for gitignore rules and user exclusions to determine the display state and label of each node. It avoids recursing into gitignored directories. ```python def _populate(self, parent_node, dir_path: Path, rel_path: str) -> None: """Recursively add subdirectories to the tree.""" try: entries = sorted(dir_path.iterdir()) except PermissionError: return for entry in entries: if not entry.is_dir() or entry.name.startswith('.'): continue child_rel = f'{rel_path}/{entry.name}'.lstrip('/') is_gitignored = self.gitignore_spec.match_file(child_rel + '/') is_excluded = child_rel in self.user_excluded or is_gitignored # Build the label with visual indicators if is_gitignored: label = f'[dim]{entry.name} (gitignored)[/dim]' elif is_excluded: label = f'[red]✗[/red] [dim]{entry.name}[/dim]' else: label = f'[green]✓[/green] {entry.name}' node = parent_node.add( label, data={ 'path': child_rel, 'gitignored': is_gitignored, 'excluded': is_excluded, }, ) # Don't recurse into gitignored dirs (they're fully excluded) if not is_gitignored: self._populate(node, entry, child_rel) ``` -------------------------------- ### Python: Save Configuration and Exit App Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/001-tallyman-cli-tool/phase-03-configuration-and-tui.plan.md Implements the 'save_and_exit' action, cleaning up the user-excluded paths by removing redundant child paths when a parent is already excluded. The cleaned set of exclusions is then returned as the app's result. ```python def action_save_and_exit(self) -> None: """Save the config file and exit the app.""" # Clean up: remove children whose parents are already excluded # (only store the highest-level exclusion) cleaned = self._clean_exclusions(self.user_excluded) self.result = cleaned # Return to caller via app.run() self.exit(cleaned) ``` -------------------------------- ### Detect Desktop Path for Image Output Source: https://github.com/mikeckennedy/tallyman/blob/main/plans/003-image-export/00-plan-introduction.plan.md This snippet demonstrates how to programmatically determine the user's desktop directory across different operating systems for saving generated images. It falls back to the current working directory if the desktop path is not found, issuing a warning. ```python from pathlib import Path desktop = Path.home() / 'Desktop' if not desktop.exists(): # Fallback to current working directory with a warning print("Warning: Desktop directory not found. Saving to current directory.") output_path = Path.cwd() else: output_path = desktop print(f"Output path set to: {output_path}") ``` -------------------------------- ### Configuration Management API: Load, Save, and Discover Config Files Source: https://context7.com/mikeckennedy/tallyman/llms.txt Manages Tallyman configuration files, allowing for loading, saving, and discovering nested configurations. It handles exclusions and spec directories, useful for monorepos. Configurations are found by walking up the directory tree. ```python from pathlib import Path from tallyman.config import ( TallyConfig, CONFIG_FILENAME, find_config, load_config, save_config, discover_nested_configs ) root = Path("/path/to/project") # Find config by walking up directory tree config_path = find_config(root) # Returns Path or None if config_path: config = load_config(config_path) print(f"Excluded: {config.excluded_dirs}") print(f"Spec dirs: {config.spec_dirs}") # Save configuration save_config( root / CONFIG_FILENAME, excluded_dirs={"node_modules", "vendor"}, spec_dirs={"docs/specs", "planning"} ) # Discover nested configs in subdirectories (for monorepos) nested = discover_nested_configs(root) # Paths are translated relative to root print(f"Nested exclusions: {nested.excluded_dirs}") print(f"Nested spec dirs: {nested.spec_dirs}") ```