diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 0000000..ac78082 --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -0,0 +1,168 @@ +# Architecture + +**Analysis Date:** 2026-03-03 + +## Pattern Overview + +**Overall:** Node-based plugin architecture for ComfyUI + +**Key Characteristics:** +- Modular node design where each node implements ComfyUI's node interface +- Each node is a standalone class with INPUT_TYPES, RETURN_TYPES, and execution methods +- Nodes are registered in a central registry for ComfyUI framework discovery +- String processing operations implemented using Python's `re` module for regex patterns +- No external dependencies beyond Python standard library +- Plugin loaded via `__init__.py` which exports node mappings + +## Layers + +**Node Layer:** +- Purpose: Implements individual string manipulation operations +- Location: Root directory - `string_textbox_node.py`, `string_strip_node.py`, `string_multi_replace_node.py`, `string_preview_node.py`, `string_conditional_append_node.py`, `string_contains_any_node.py` +- Contains: Node classes inheriting ComfyUI's node interface +- Depends on: Python `re` module for regex operations +- Used by: ComfyUI framework (loads via NODE_CLASS_MAPPINGS) + +**Plugin Registry Layer:** +- Purpose: Provides node discovery and registration for ComfyUI +- Location: `__init__.py` +- Contains: NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, WEB_DIRECTORY exports +- Depends on: All node modules +- Used by: ComfyUI framework at startup + +**Testing Layer:** +- Purpose: Unit tests for all node functionalities +- Location: `test_string_nodes.py` +- Contains: unittest.TestCase subclass with comprehensive test suites +- Depends on: All node classes +- Used by: Developers running tests locally + +## Data Flow + +**String Processing Pipeline:** + +1. Input received via `INPUT_TYPES` - user specifies string and processing parameters +2. ComfyUI calls node's FUNCTION method (e.g., `string_strip`, `string_replace`) +3. Processing method performs regex-based transformations +4. Optional cleanup via `cleanup_text()` if `remove_extra_spaces` is True +5. Return processed string(s) via RETURN_TYPES tuple + +**Example - StringStripNode flow:** +1. User provides `input_string` and `strings_to_remove` (multiline) +2. `string_strip()` method parses removal list, sorted by length (longest first) +3. Regex patterns compiled with case sensitivity flags +4. Patterns applied sequentially to input string +5. Cleanup normalizes whitespace if enabled +6. Result returned as single-element tuple + +**Example - StringConditionalAppendNode flow:** +1. User provides `input_string` and `strings_to_check` (multiline) +2. `conditional_append()` parses check list +3. Each string checked for presence in result (with case sensitivity option) +4. Missing strings appended at specified position (beginning/end) +5. Returns tuple of (modified_string, was_appended boolean) + +**State Management:** +- Stateless nodes - each execution independent +- No shared state between invocations +- Only state change: modifying the input string passed through + +## Key Abstractions + +**Node Interface Pattern:** +- Purpose: Standardizes how nodes interact with ComfyUI framework +- Examples: `StringTextboxNode` (`string_textbox_node.py`), `StringStripNode` (`string_strip_node.py`) +- Pattern: Each node class implements three key class attributes: + - `INPUT_TYPES()` classmethod - declares input parameters and their types + - `RETURN_TYPES` tuple - declares output types + - `FUNCTION` string - method name to call for execution + - Execution method - performs actual processing and returns tuple + +**Text Processing Pattern:** +- Purpose: Shared approach for string matching and replacement across nodes +- Examples: `string_strip_node.py`, `string_multi_replace_node.py` +- Pattern: Uses `re.compile()` with configurable flags (IGNORECASE), patterns include: + - Word boundary matching: `\b` for whole word matching + - Punctuation handling: conditional patterns with `(?:[,;:.])` for preservation/removal + - Regex escaping: `re.escape()` to treat user input as literals + +**Text Cleanup Pattern:** +- Purpose: Normalize whitespace after text modifications +- Examples: `cleanup_text()` method in `string_strip_node.py`, `string_multi_replace_node.py` +- Pattern: Two-step regex approach: + 1. Remove spaces before punctuation: `r'\s+([,;:.])'` → `r'\1'` + 2. Normalize multiple spaces: `r'\s{2,}'` → `' '` + 3. Strip leading/trailing whitespace + +**Multi-value Processing Pattern:** +- Purpose: Handle comma-separated or line-separated lists of values +- Examples: All nodes with multiline string inputs +- Pattern: `.splitlines()` to split input, `.strip()` to clean each line, filter empty strings + +## Entry Points + +**ComfyUI Framework Loading:** +- Location: `__init__.py` +- Triggers: ComfyUI startup/refresh +- Responsibilities: + - Imports all node classes + - Exports NODE_CLASS_MAPPINGS for framework discovery + - Exports NODE_DISPLAY_NAME_MAPPINGS for UI display names + - Exports WEB_DIRECTORY for any client-side assets + +**Individual Node Execution:** +- Location: Each node's FUNCTION method (e.g., `string_strip()` in `string_strip_node.py`) +- Triggers: ComfyUI workflow execution when node is evaluated +- Responsibilities: + - Accept parameters from INPUT_TYPES + - Perform string processing + - Return results as tuple matching RETURN_TYPES + +**Output Display:** +- Location: `string_preview_node.py` - StringPreviewNode +- Triggers: Workflow execution +- Responsibilities: + - Display string output in UI via `preview_string()` method + - Force re-evaluation on every execution via `IS_CHANGED()` returning NaN + - Return both UI display data and result tuple + +## Error Handling + +**Strategy:** Defensive programming - validate and handle edge cases gracefully + +**Patterns:** +- Empty input handling: All nodes check for empty strings and return sensible defaults + - Example: `string_contains_any_node.py` line 40-41 returns `(False, "")` for empty substrings + - Example: `string_conditional_append_node.py` line 44-45 returns unchanged input if no strings to check +- Line parsing robustness: `.strip()` applied to each line, whitespace-only lines filtered + - Example: `string_strip_node.py` line 36-38 skips empty/whitespace lines in removal list +- Regex compilation safety: `re.escape()` used to prevent regex injection from user input + - Example: `string_strip_node.py` line 55, 62 escape user strings before pattern compilation +- Delimiter handling: Split with `maxsplit=1` to allow delimiter characters in replacement text + - Example: `string_multi_replace_node.py` line 42 uses `split(delimiter, 1)` to preserve right side + +## Cross-Cutting Concerns + +**Logging:** Not implemented - ComfyUI framework handles logging + +**Validation:** +- User input validation occurs at parsing stage (splitlines, strip, filter empty) +- Type validation handled by ComfyUI framework via INPUT_TYPES/RETURN_TYPES declarations +- No explicit validation errors thrown; nodes handle edge cases gracefully + +**Authentication:** Not applicable - nodes are part of ComfyUI plugin + +**Case Sensitivity:** +- All text nodes support configurable case-sensitive matching via `match_case` boolean parameter +- Implemented via `re.IGNORECASE` flag when `match_case=False` +- Example: `string_strip_node.py` line 47 sets `flags = 0 if match_case else re.IGNORECASE` + +**Punctuation Handling:** +- Special feature introduced to preserve punctuation adjacent to matched text +- Controlled by `preserve_punctuation` boolean parameter +- Enabled by default in most nodes for safe prompt/syntax handling +- Example: `string_strip_node.py` lines 53-65 show pattern variants for punctuation preservation + +--- + +*Architecture analysis: 2026-03-03* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md new file mode 100644 index 0000000..06ec53f --- /dev/null +++ b/.planning/codebase/CONCERNS.md @@ -0,0 +1,165 @@ +# Codebase Concerns + +**Analysis Date:** 2026-03-03 + +## Code Duplication + +**Regex pattern compilation duplicated across node classes:** +- Issue: Both `StringStripNode` and `StringMultiReplaceNode` implement nearly identical regex pattern building logic for `match_whole_string` and `preserve_punctuation` combinations. +- Files: `string_strip_node.py` lines 52-72, `string_multi_replace_node.py` lines 59-75 +- Impact: If regex patterns need adjustment (e.g., for new punctuation marks), changes must be made in multiple places, increasing maintenance burden and risk of inconsistency. +- Fix approach: Extract shared regex pattern building logic into a utility module `string_utils.py` with a function like `build_replacement_pattern(search_str, match_whole_string, preserve_punctuation)` that both nodes can use. + +**Text cleanup logic duplicated:** +- Issue: `cleanup_text()` method is defined identically in both `StringStripNode` and `StringMultiReplaceNode`. +- Files: `string_strip_node.py` lines 27-30, `string_multi_replace_node.py` lines 29-32 +- Impact: Bug fixes to space normalization or punctuation handling require changes in two places. +- Fix approach: Move `cleanup_text()` to a shared utility module. + +## Input Validation Gaps + +**No validation of `replacement_delimiter` parameter:** +- Issue: `StringMultiReplaceNode.string_replace()` accepts any string as `replacement_delimiter`, including empty strings or strings not present in replacement_pairs. +- Files: `string_multi_replace_node.py` lines 34-80 +- Impact: Empty delimiter could cause silent failures or unexpected behavior. No error message guides users on correct format. +- Fix approach: Add validation to check if `replacement_delimiter` is non-empty and is actually present in at least one line of `replacement_pairs` when non-empty pairs are provided. Return error message if invalid. + +**No null/None input handling:** +- Issue: All nodes assume string inputs are valid. If ComfyUI passes `None` or unexpected types, nodes will crash with unhelpful error messages. +- Files: All node classes (e.g., `string_strip_node.py` line 32) +- Impact: Silent failures in ComfyUI workflows without clear error reporting. +- Fix approach: Add type checking and explicit error handling at method entry points. + +**Missing handling of extremely long strings:** +- Issue: Regex operations on very large strings (e.g., multi-megabyte text) could cause performance issues or regex complexity bombs. +- Files: All regex-based nodes (`string_strip_node.py`, `string_multi_replace_node.py`, `string_contains_any_node.py`) +- Impact: Workflows processing large text files could hang or crash unexpectedly. +- Fix approach: Add length checks and document maximum recommended input size. + +## Pattern Matching Edge Cases + +**Incomplete word boundary handling:** +- Issue: The `\b` word boundary in regex (lines 55, 62 in `string_strip_node.py`) doesn't work correctly at string boundaries with Unicode characters, accented characters, or special symbols. +- Files: `string_strip_node.py` lines 52-72, `string_multi_replace_node.py` lines 59-75 +- Impact: Matching whole words in non-English text or with special characters produces unexpected results. +- Fix approach: Document this limitation or switch to a more robust word boundary detection method that handles Unicode properly. + +**Regex special characters not escaped in `match_whole_string=False`:** +- Issue: In `preserve_punctuation=False` mode, the pattern `re.escape(search_str) + r'(?:[,;:.])?'` (line 72 in `string_multi_replace_node.py`) may not handle all edge cases where the search string itself contains pattern-matching characters. +- Files: `string_multi_replace_node.py` lines 70-72, `string_strip_node.py` lines 64-65 +- Impact: While `re.escape()` is used, the added `(?:[,;:.])?` pattern creates assumptions about which punctuation marks follow matches. Other punctuation or symbols would not be handled. +- Fix approach: Either document the supported punctuation list or provide an option to make punctuation removal more flexible. + +## Error Reporting + +**No error messages returned to user:** +- Issue: Invalid inputs (malformed replacement pairs, empty delimiters, etc.) fail silently or with generic Python exceptions. +- Files: All node classes +- Impact: Users get no feedback about what went wrong. Debugging workflows is difficult. +- Fix approach: Return structured error messages or use ComfyUI's error reporting mechanism. + +**Regex compilation errors not caught:** +- Issue: If a user's input accidentally creates an invalid regex pattern, the `re.compile()` call (lines 74 in `string_multi_replace_node.py`, line 67 in `string_strip_node.py`) will raise an exception that crashes the node without a helpful message. +- Files: `string_strip_node.py` line 67, `string_multi_replace_node.py` line 74 +- Impact: Even simple typos in user input cause workflow failures with unhelpful error messages. +- Fix approach: Wrap `re.compile()` in try-except block and return meaningful error message. + +## Performance Considerations + +**Regex patterns compiled multiple times per execution:** +- Issue: In `StringStripNode.string_strip()`, patterns are compiled in a loop (line 67) and compiled separately in a list for each method call. For large lists of removals, this is inefficient. +- Files: `string_strip_node.py` lines 50-71 +- Impact: Repeated calls with same removal patterns waste CPU cycles on recompilation. +- Fix approach: Consider memoizing compiled patterns if the same `strings_to_remove` is used repeatedly, or compile once and reuse within a single call. + +**Sorting by length every execution:** +- Issue: Strings are sorted by length on every method call (lines 44 in `string_strip_node.py`, line 52 in `string_multi_replace_node.py`). This is necessary for correctness but could be optimized with caching if patterns are reused. +- Files: `string_strip_node.py` line 44, `string_multi_replace_node.py` line 52 +- Impact: Minor performance impact for large replacement lists on repeated calls. +- Fix approach: Document the O(n log n) sorting behavior; consider caching if needed. + +## Testing Coverage Gaps + +**No tests for regex compilation failures:** +- What's not tested: Invalid regex patterns that would cause `re.compile()` to raise an exception. +- Files: `string_strip_node.py` line 67, `string_multi_replace_node.py` line 74, `test_string_nodes.py` +- Risk: Unexpected regex patterns from user input could crash the node without warning. +- Priority: Medium + +**No tests for Unicode or special character handling:** +- What's not tested: Input strings with emoji, accented characters, or multi-byte Unicode. +- Files: `test_string_nodes.py` +- Risk: Real-world prompts with special characters may behave unexpectedly. +- Priority: Medium + +**No tests for extreme input sizes:** +- What's not tested: Very long strings (megabytes), very long replacement lists, very long individual replacement pairs. +- Files: `test_string_nodes.py` +- Risk: Performance issues or stack overflow on large inputs would not be detected. +- Priority: Low + +**No tests for StringTextboxNode:** +- What's not tested: The `StringTextboxNode.pass_string()` method has zero test coverage. +- Files: `string_textbox_node.py`, `test_string_nodes.py` +- Risk: Changes to this node's behavior would go undetected. +- Priority: Low (simple pass-through, but should be covered) + +**No tests for StringPreviewNode:** +- What's not tested: The `StringPreviewNode.preview_string()` method and its special `IS_CHANGED()` behavior. +- Files: `string_preview_node.py`, `test_string_nodes.py` +- Risk: The `float("nan")` return value in `IS_CHANGED()` is unusual and not validated. +- Priority: Medium + +## Architecture Concerns + +**No base class for shared functionality:** +- Issue: Six independent node classes with duplicated methods and patterns indicate missing abstraction. +- Files: All `*_node.py` files +- Impact: Adding features (e.g., logging, input validation) requires changes to multiple files. +- Fix approach: Create a `BaseStringNode` class with shared validation, error handling, and utility methods. + +**Inconsistent parameter naming:** +- Issue: `StringStripNode` uses `strings_to_remove`, `StringMultiReplaceNode` uses `replacement_pairs`, etc. Inconsistent naming makes workflows harder to understand and API harder to learn. +- Files: All node classes +- Impact: Users must remember different parameter names for similar operations. +- Fix approach: Standardize parameter names across nodes or document the reasoning for differences. + +## Missing Features + +**No logging of transformations:** +- Issue: Nodes silently perform transformations with no audit trail or debug output. +- Files: All node classes +- Impact: Users cannot see what replacements were actually made or debug complex workflows. +- Fix approach: Add optional debug output or logging parameter. + +**No reuse of previous results:** +- Issue: Each node independently processes input; there's no way to chain results with visibility into intermediate states. +- Files: All output nodes +- Impact: Complex text processing requires many separate nodes instead of a single configurable pipeline. +- Fix approach: Consider a single "Text Processing Pipeline" node that chains multiple operations. + +## Potential Security Concerns + +**Regular expression denial of service (ReDoS) risk:** +- Issue: User-provided input in `strings_to_remove`, `replacement_pairs`, and `substrings` are used directly in regex patterns after `re.escape()`. While `re.escape()` prevents most ReDoS, deeply nested or pathological patterns could still cause issues. +- Files: `string_strip_node.py` lines 52-72, `string_multi_replace_node.py` lines 59-75, `string_contains_any_node.py` (simple substring search, low risk) +- Impact: Malicious or accidental input could cause CPU spike and freeze workflows. +- Fix approach: Add timeout to regex operations or limit complexity of patterns accepted. + +## Known Limitations + +**Word boundary matching fails with Unicode:** +- The `\b` anchor in regex patterns doesn't correctly identify word boundaries in non-ASCII text. +- Workaround: Test thoroughly with non-English text; consider providing separate "ASCII-only" and "Unicode-aware" modes. + +**Punctuation handling is opinionated:** +- The hardcoded punctuation list `[,;:.]` only handles common English punctuation. Other languages or contexts may use different marks. +- Workaround: Make punctuation marks configurable, or document the list and allow users to add custom nodes for other punctuation. + +**Case-insensitive matching doesn't handle Unicode case folding:** +- Python's `.lower()` method handles most Unicode, but some edge cases in Turkish, Greek, and other languages may not be handled optimally. +- Workaround: Document that case-insensitive matching is "best effort" for non-ASCII text. + +--- + +*Concerns audit: 2026-03-03* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 0000000..5e03d69 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -0,0 +1,186 @@ +# Coding Conventions + +**Analysis Date:** 2026-03-03 + +## Naming Patterns + +**Files:** +- Node files: `string__node.py` (e.g., `string_strip_node.py`, `string_multi_replace_node.py`) +- Test file: `test_.py` (e.g., `test_string_nodes.py`) +- Class names reflect file names in PascalCase (e.g., `StringStripNode` from `string_strip_node.py`) + +**Classes:** +- Node classes: `StringNode` format (e.g., `StringStripNode`, `StringMultiReplaceNode`, `StringConditionalAppendNode`) +- All node classes follow ComfyUI convention with specific class methods and attributes + +**Functions/Methods:** +- snake_case for method names (e.g., `string_strip`, `cleanup_text`, `conditional_append`, `contains_any`) +- Core execution method: `FUNCTION = ""` points to the main processing method +- Helper methods: lower case with underscores (e.g., `cleanup_text`) + +**Variables:** +- snake_case for local variables (e.g., `input_string`, `strings_to_remove`, `replacement_pairs`, `strings_list`) +- Boolean prefixes: `match_case`, `match_whole_string`, `preserve_punctuation`, `was_appended`, `was_any_appended` + +**Constants:** +- UPPERCASE for class constants (e.g., `RETURN_TYPES`, `RETURN_NAMES`, `FUNCTION`, `CATEGORY`, `INPUT_TYPES`, `OUTPUT_NODE`) + +## Code Style + +**Formatting:** +- No detected formatter configuration (.prettierrc, black config, etc.) +- Standard Python conventions: 4-space indentation +- Line length varies, no strict limit enforced +- Blank lines used to separate logical sections within methods + +**Linting:** +- No detected linting configuration (.pylintrc, .flake8, etc.) +- Code follows general PEP 8 style conventions +- Minimal deviation from standard Python conventions + +**Class Structure Pattern:** +All node classes follow a consistent structure: +```python +class StringNodeName: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "parameter_name": ("TYPE", {"configuration": "dict"}) + } + } + + RETURN_TYPES = ("STRING",) # or ("BOOLEAN", "STRING") etc. + RETURN_NAMES = ("return_name",) + FUNCTION = "method_name" + CATEGORY = "utils/StringEssentials" + + def method_name(self, parameters): + # Implementation + return (result,) # Always return tuple +``` + +## Import Organization + +**Order:** +1. Standard library imports (`import unittest`, `import re`) +2. Class imports from local modules (e.g., `from string_strip_node import StringStripNode`) + +**Path Aliases:** +- No path aliases detected or used + +**Module Organization:** +- `__init__.py` at `/home/mark/Code/ComfyUI_StringEssentials/__init__.py` imports all node classes +- NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS provide ComfyUI registry +- All modules use relative imports in `__init__.py` + +## Error Handling + +**Patterns:** +- Input validation through conditional checks (if not line: continue) +- Empty input handling: return default empty/false values (e.g., `return (False, "")`) +- No explicit try-catch blocks; relies on Python built-ins for error prevention +- String operations wrapped in safe calls (e.g., `.strip()`, `.split()`) +- All methods handle empty strings and whitespace gracefully + +**Example from `string_contains_any_node.py`:** +```python +if not substrings_list: + return (False, "") + +# Prepare strings for comparison +search_string = input_string if match_case else input_string.lower() +``` + +## Logging + +**Framework:** Not detected - no logging module used in codebase + +**Console Output:** +- No console logging throughout production code +- Tests use unittest assertions for verification +- No debug output or print statements in production code + +## Comments + +**When to Comment:** +- Comments reference issue numbers for fixes (e.g., "# Issue #6 fix", "# Test Issue #6", "# Issue #8") +- Comments explain regex patterns and matching behavior (e.g., "Remove spaces before punctuation", "Normalize multiple spaces") +- Docstrings minimal; only used in `StringContainsAnyNode` with class-level docstring + +**JSDoc/TSDoc:** +- Not applicable - Python codebase without type hints +- Class methods have minimal/no docstrings + +## Function Design + +**Size:** +- Small to medium functions (methods range 10-50 lines) +- Core logic methods typically 30-50 lines: `string_strip`, `string_replace`, `conditional_append` +- Helper methods compact: `cleanup_text` is 3 lines + +**Parameters:** +- Input parameters correspond directly to INPUT_TYPES definitions +- Boolean flags for feature toggling common (e.g., `match_case`, `preserve_punctuation`) +- String inputs often use multiline format for flexibility + +**Return Values:** +- Always return tuples, matching RETURN_TYPES specification +- Single return: `(result,)` +- Multiple returns: `(result, boolean_flag)` +- Empty returns use falsy values: `(False, "")` or `(input_string, False)` + +## Module Design + +**Exports:** +- `__init__.py` at `/home/mark/Code/ComfyUI_StringEssentials/__init__.py` exports all node classes +- NODE_CLASS_MAPPINGS dict maps string names to classes for ComfyUI registration +- NODE_DISPLAY_NAME_MAPPINGS provides UI-friendly names + +**Barrel Files:** +- Yes, `__init__.py` acts as barrel file with explicit imports +- Each node class imported individually +- WEB_DIRECTORY constant exported for ComfyUI + +**Shared Utilities:** +- `cleanup_text()` method duplicated in both `StringStripNode` and `StringMultiReplaceNode` (not DRY, but isolated to context) +- No separate utils module; all logic encapsulated in node classes + +## Common Patterns + +**Regex Pattern Compilation:** +Patterns compiled once in a loop for performance: +```python +# From string_strip_node.py and string_multi_replace_node.py +patterns = [] +for _, string_to_remove in removals: + # Build pattern + pattern = ... + patterns.append(re.compile(pattern, flags=flags)) + +# Apply all patterns +for pattern in patterns: + result = pattern.sub('', result) +``` + +**String Parsing (Multiline Input):** +Common pattern for parsing user input: +```python +strings_list = [] +for line in strings_to_remove.splitlines(): + line = line.strip() + if not line: + continue + strings_list.append(line) +``` + +**Case Sensitivity Handling:** +Consistent approach across nodes: +```python +flags = 0 if match_case else re.IGNORECASE +regex = re.compile(pattern, flags=flags) +``` + +--- + +*Convention analysis: 2026-03-03* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 0000000..968347d --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -0,0 +1,81 @@ +# External Integrations + +**Analysis Date:** 2026-03-03 + +## APIs & External Services + +**LLM Integration:** +- Compatible with outputs from Ollama, Claude.ai, OpenAI ChatGPT +- No direct SDK/client integration - processes text output from LLM services +- Purpose: Clean and transform LLM-generated prompts and descriptions + +## Data Storage + +**Databases:** +- Not applicable - Nodes process in-memory strings only, no persistent storage + +**File Storage:** +- Local filesystem only - Custom node installed in ComfyUI custom_nodes directory +- No external file storage integrations + +**Caching:** +- None - All operations are stateless transformations + +## Authentication & Identity + +**Auth Provider:** +- Not applicable - Standalone utility nodes with no authentication requirements +- Custom node registration managed by ComfyUI framework + +## Monitoring & Observability + +**Error Tracking:** +- None - Uses ComfyUI's native error handling and logging + +**Logs:** +- ComfyUI application logs +- No external log aggregation + +## CI/CD & Deployment + +**Hosting:** +- GitHub repository: https://github.com/bradsec/ComfyUI_StringEssentials +- ComfyUI Registry: Registered via ComfyUI package registry (PublisherId: bradsec) + +**CI Pipeline:** +- GitHub Actions workflow: `.github/workflows/publish.yml` +- Automated publishing to ComfyUI registry on release + +## Environment Configuration + +**Required env vars:** +- None - Zero environment variable dependencies + +**Secrets location:** +- Not applicable - No secrets or credentials used + +## Webhooks & Callbacks + +**Incoming:** +- None + +**Outgoing:** +- None + +## Frontend Integration + +**ComfyUI Web Interface:** +- JavaScript extensions in `js/string_essentials.js` +- Custom node widget initialization via ComfyUI's extension API +- Widget API: `ComfyWidgets` for STRING input display +- App API: `app.registerExtension()` for node lifecycle hooks + +**Node Widget Configuration:** +- Multiline text inputs for string configuration +- Boolean toggles for matching options (case-sensitive, whole word, etc.) +- Dropdown selections for position (beginning/end) +- Custom separators configurable via STRING inputs + +--- + +*Integration audit: 2026-03-03* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 0000000..fac78b6 --- /dev/null +++ b/.planning/codebase/STACK.md @@ -0,0 +1,79 @@ +# Technology Stack + +**Analysis Date:** 2026-03-03 + +## Languages + +**Primary:** +- Python 3.x - Core node implementation and string manipulation logic +- JavaScript - Frontend UI extensions and ComfyUI widget integration + +## Runtime + +**Environment:** +- ComfyUI - Custom node framework for AI image generation workflows + +**Package Manager:** +- pip - Python dependency management +- No Python dependencies or external modules required (pure Python implementation) + +## Frameworks + +**Core:** +- ComfyUI Custom Nodes Framework - Node registration and execution + - Uses `NODE_CLASS_MAPPINGS` and `NODE_DISPLAY_NAME_MAPPINGS` for node registration + - Located: `__init__.py` + +**Testing:** +- unittest - Python standard library testing framework +- Config: No external test configuration file (uses unittest defaults) +- Test file: `test_string_nodes.py` + +**Build/Dev:** +- No formal build system required +- Direct Python module installation into ComfyUI custom_nodes directory + +## Key Dependencies + +**Critical:** +- None - This is a zero-dependency ComfyUI custom node package +- All functionality implemented using Python standard library only (re module for regex) + +**Infrastructure:** +- None - Standalone custom node implementation + +## Configuration + +**Environment:** +- No environment variables required +- No configuration files needed +- Simple cloning into ComfyUI custom_nodes directory + +**Build:** +- `pyproject.toml` - Package metadata and ComfyUI registry information + - Version: 2.0.7 + - Publisher: bradsec (ComfyUI Registry identifier) + - License: Included in LICENSE file + +## Platform Requirements + +**Development:** +- Python 3.x +- ComfyUI installation (as parent application) +- Git for repository cloning + +**Production:** +- ComfyUI application +- Python 3.x runtime +- Custom node installed in `custom_nodes/ComfyUI_StringEssentials/` directory + +## Platform Support + +**Deployment Target:** +- ComfyUI custom nodes directory +- Cross-platform (Windows, macOS, Linux) +- Web-based ComfyUI interface with JavaScript frontend integration + +--- + +*Stack analysis: 2026-03-03* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 0000000..e94cb0c --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -0,0 +1,207 @@ +# Codebase Structure + +**Analysis Date:** 2026-03-03 + +## Directory Layout + +``` +ComfyUI_StringEssentials/ +├── __init__.py # Node registry and exports +├── pyproject.toml # Project metadata and configuration +├── README.md # Documentation +├── LICENSE # License file +├── string_textbox_node.py # String Textbox node +├── string_strip_node.py # String Strip node +├── string_multi_replace_node.py # String Multi Replace node +├── string_preview_node.py # String Preview node +├── string_conditional_append_node.py # String Conditional Append node +├── string_contains_any_node.py # String Contains Any node +├── test_string_nodes.py # Unit tests +├── .planning/ # Planning and analysis docs (not committed) +│ └── codebase/ # Architecture documentation +├── .pytest_cache/ # Pytest cache (gitignored) +├── js/ # Client-side assets (currently empty) +└── images/ # Documentation images +``` + +## Directory Purposes + +**Root Directory:** +- Purpose: Contains all Python node implementations and project config +- Contains: Node classes, tests, project metadata +- Key files: All `*_node.py` files, `__init__.py`, `pyproject.toml`, `test_string_nodes.py` + +**js/ Directory:** +- Purpose: Placeholder for client-side JavaScript/web assets +- Contains: Currently empty, referenced in `__init__.py` WEB_DIRECTORY +- Committed: Yes (directory exists, may have assets) + +**images/ Directory:** +- Purpose: Documentation images for README +- Contains: PNG screenshots of node interfaces and example workflows +- Committed: Yes + +**.planning/codebase/ Directory:** +- Purpose: Architecture and analysis documentation generated by GSD tools +- Contains: ARCHITECTURE.md, STRUCTURE.md, CONVENTIONS.md, TESTING.md, etc. +- Committed: No (git ignored) + +**.pytest_cache/ Directory:** +- Purpose: Pytest runtime cache +- Contains: Test execution metadata +- Committed: No (git ignored) + +## Key File Locations + +**Entry Points:** +- `__init__.py`: Module initialization - imports nodes and exports NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, WEB_DIRECTORY + +**Node Implementations:** +- `string_textbox_node.py`: StringTextboxNode - simple text input passthrough (5 lines of logic) +- `string_strip_node.py`: StringStripNode - removes specified strings with regex patterns +- `string_multi_replace_node.py`: StringMultiReplaceNode - replaces text using delimiter-separated pairs +- `string_preview_node.py`: StringPreviewNode - displays string output in ComfyUI UI +- `string_conditional_append_node.py`: StringConditionalAppendNode - appends text only if not present +- `string_contains_any_node.py`: StringContainsAnyNode - checks if any of multiple substrings exist + +**Configuration:** +- `pyproject.toml`: Project metadata including name, version (2.0.7), description, repository URL + +**Testing:** +- `test_string_nodes.py`: 62 unit tests covering all node functionalities + +**Documentation:** +- `README.md`: User-facing documentation with node descriptions and usage examples + +## Naming Conventions + +**Files:** +- Node files: `string__node.py` format + - Examples: `string_strip_node.py`, `string_multi_replace_node.py` + - Pattern: snake_case with `_node.py` suffix + +**Classes:** +- Node classes: PascalCase with "Node" suffix + - Examples: `StringTextboxNode`, `StringStripNode`, `StringMultiReplaceNode` + - Pattern: Convert filename from snake_case to PascalCase, append "Node" + +**Methods:** +- Execution methods: verb_noun_v1 format (snake_case) + - Examples: `string_strip()`, `string_replace()`, `conditional_append()`, `contains_any()`, `pass_string()`, `preview_string()` + - Pattern: Descriptive snake_case names indicating operation + +**Variables:** +- Instance variables: snake_case with descriptive names + - Examples: `input_string`, `strings_to_remove`, `match_case`, `preserve_punctuation` + - Pattern: Clear names indicating type and purpose + +**Constants:** +- Node metadata: UPPERCASE + - Examples: `RETURN_TYPES`, `RETURN_NAMES`, `FUNCTION`, `CATEGORY`, `INPUT_TYPES`, `OUTPUT_NODE` + - Pattern: ComfyUI convention for node interface + +## Where to Add New Code + +**New String Operation Node:** +1. Create file: `string__node.py` in root directory +2. Class structure (follow pattern from existing nodes): + - Define `INPUT_TYPES()` classmethod with parameter definitions + - Define `RETURN_TYPES` tuple with output types + - Define `RETURN_NAMES` tuple with output names + - Set `FUNCTION = ""` + - Set `CATEGORY = "utils/StringEssentials"` + - Implement execution method with logic +3. Add to `__init__.py`: + - Import the new class at top + - Add entry to `NODE_CLASS_MAPPINGS` dict (key = class name as string) + - Add entry to `NODE_DISPLAY_NAME_MAPPINGS` dict (key = class name, value = display name) +4. Add tests to `test_string_nodes.py`: + - Create test class or add methods to `TestStringNodes` + - Test basic functionality, edge cases, parameter variations + +**Example node implementation pattern from `string_strip_node.py`:** +```python +import re + +class StringStripNode: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "input_string": ("STRING", {...}), + "strings_to_remove": ("STRING", {...}), + "match_case": ("BOOLEAN", {...}), + # ... more parameters + } + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("modified_string",) + FUNCTION = "string_strip" + CATEGORY = "utils/StringEssentials" + + def cleanup_text(self, text): + # Helper method for text normalization + pass + + def string_strip(self, input_string, strings_to_remove, ...): + # Parse input (splitlines, strip, filter empty) + # Compile regex patterns + # Apply patterns + # Cleanup if needed + return (result,) +``` + +**Utilities/Helpers:** +- Shared helper methods: Add to individual node class if only used there +- Common patterns: Currently duplicated in `cleanup_text()` across nodes (consider extraction if adding more nodes) +- Location: Keep at root level - no separate utils directory used + +**Tests:** +- Add test methods to `TestStringNodes` class in `test_string_nodes.py` +- Follow naming: `test__` (e.g., `test_strip_basic_functionality`) +- Setup: Use `self.setUp()` to instantiate node classes +- Pattern: Call node method directly with test parameters, assert expected results + +## Special Directories + +**js/ Directory:** +- Purpose: ComfyUI web assets (referenced in WEB_DIRECTORY in `__init__.py`) +- Generated: No +- Committed: Yes (directory exists, may be empty or contain future web assets) +- Note: Currently unused but available for client-side node customization + +**images/ Directory:** +- Purpose: Screenshots and diagrams for documentation +- Generated: No (manually created/updated) +- Committed: Yes +- Files: PNG images referenced in README.md + +**.planning/codebase/ Directory:** +- Purpose: GSD (Get Shit Done) architecture analysis documents +- Generated: Yes (by GSD tools) +- Committed: No (git ignored) +- Contains: ARCHITECTURE.md, STRUCTURE.md, CONVENTIONS.md, TESTING.md, CONCERNS.md + +**.pytest_cache/ Directory:** +- Purpose: Pytest internal cache for test discovery and execution +- Generated: Yes (auto-created by pytest) +- Committed: No (git ignored) + +## File Organization Rationale + +**Flat root structure for nodes:** Each node is a separate file at root level +- Rationale: Simple discovery by ComfyUI, clear 1:1 mapping between file and node class +- Alternative avoided: Subdirectory organization (would complicate imports in `__init__.py`) + +**Single test file:** All tests in `test_string_nodes.py` +- Rationale: Small codebase, all nodes similar in nature, easy to run all tests together +- Scalability: If adding more nodes, consider splitting tests by node when >100 test methods + +**Shared patterns duplicated:** `cleanup_text()` method exists in both `string_strip_node.py` and `string_multi_replace_node.py` +- Current state: Intentionally duplicated to keep each node self-contained +- Future improvement: Extract to shared module if >3 nodes need it + +--- + +*Structure analysis: 2026-03-03* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 0000000..369c611 --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -0,0 +1,251 @@ +# Testing Patterns + +**Analysis Date:** 2026-03-03 + +## Test Framework + +**Runner:** +- unittest (Python standard library) +- Config: None (uses default unittest discovery) +- No pytest.ini, setup.cfg, or tox.ini configuration detected + +**Assertion Library:** +- unittest.TestCase assertions (built-in) + +**Run Commands:** +```bash +python -m unittest test_string_nodes.py # Run all tests +python -m unittest test_string_nodes.TestStringNodes # Run specific test class +python -m unittest test_string_nodes.TestStringNodes.test_ # Run specific test +``` + +**Test File Location:** +- `test_string_nodes.py` in project root alongside source files (`/home/mark/Code/ComfyUI_StringEssentials/test_string_nodes.py`) +- Co-located with source code (not in separate tests directory) + +## Test File Organization + +**Location:** +- Co-located pattern: test file in same directory as source modules + +**Naming:** +- Test file: `test_string_nodes.py` +- Test class: `TestStringNodes` (PascalCase with Test prefix) +- Test methods: `test_` format + +**Structure:** +``` +test_string_nodes.py +├── TestStringNodes (class) +│ ├── setUp() - Initialize node instances +│ ├── test_strip_* - Tests for StringStripNode +│ ├── test_replace_* - Tests for StringMultiReplaceNode +│ ├── test_append_* - Tests for StringConditionalAppendNode +│ └── test_contains_any_* - Tests for StringContainsAnyNode +└── if __name__ == '__main__': unittest.main() +``` + +## Test Structure + +**Suite Organization:** +```python +class TestStringNodes(unittest.TestCase): + def setUp(self): + self.strip_node = StringStripNode() + self.replace_node = StringMultiReplaceNode() + self.append_node = StringConditionalAppendNode() + self.contains_any_node = StringContainsAnyNode() +``` + +**Patterns:** +- **Setup pattern:** `setUp()` method instantiates all node instances as instance variables for reuse across test methods +- **No teardown:** No `tearDown()` method (no cleanup needed) +- **Assertion pattern:** Direct method calls on node instances with parameter unpacking: + ```python + result = self.strip_node.string_strip( + input_string=input_string, + strings_to_remove=strings_to_remove, + match_case=False, + match_whole_string=True, + preserve_punctuation=True, + remove_extra_spaces=True + )[0] + self.assertEqual(result, expected_output) + ``` + +**Test Method Characteristics:** +- Each test is independent and focused on a single behavior +- Tests use explicit parameter names (keyword arguments) for clarity +- Return value tuple unpacking: `[0]` for single return, tuple unpacking for multiple returns +- Clear arrange-act-assert flow (input setup → method call → assertion) + +## Test Coverage by Node + +**StringStripNode Tests:** +- `test_strip_basic_functionality`: Single string removal +- `test_strip_multiple_strings`: Multiple removal patterns (multiline input) +- `test_strip_case_sensitivity`: Case-insensitive vs case-sensitive modes +- `test_strip_punctuation_preserved`: Punctuation preservation (Issue #6) +- `test_strip_punctuation_removed`: Legacy punctuation removal behavior +- Extra spaces handling covered in `test_extra_spaces_handling` + +**StringMultiReplaceNode Tests:** +- `test_replace_basic_functionality`: Single replacement pair +- `test_replace_multiple_pairs`: Multiple replacement pairs from multiline input +- `test_replace_case_sensitivity`: Case-insensitive vs case-sensitive modes +- `test_replace_custom_delimiter`: Custom delimiter support (not just `::` +- `test_replace_punctuation_preserved`: Punctuation preservation (Issue #6) +- `test_replace_prompt_syntax_preserved`: Bracket syntax preservation (e.g., `[word1:word2:.5]`) +- `test_replace_with_space`: Trailing space preservation in replacements (Issue #8) +- `test_replace_with_leading_space`: Leading space in replacement strings (Issue #8) +- `test_replace_with_whitespace_only`: Multiple spaces in replacements (Issue #8) +- `test_replace_with_tab`: Tab character in replacements (Issue #8) + +**StringConditionalAppendNode Tests:** +- `test_append_string_not_found`: Appending when string not present +- `test_append_string_found`: No append when string already present +- `test_append_beginning`: Position flag "beginning" vs "end" +- `test_append_case_insensitive`: Case-insensitive search +- `test_append_case_sensitive`: Case-sensitive search +- `test_append_custom_separator`: Custom separator handling +- `test_append_no_separator`: Empty string separator +- `test_append_multiple_strings`: Multiple check strings with mixed found/not found +- `test_append_multiple_all_found`: All strings already present (no append) + +**StringContainsAnyNode Tests:** +- `test_contains_any_found`: Basic found case +- `test_contains_any_not_found`: Basic not found case +- `test_contains_any_multiple_first_match`: Returns first match from multiple options +- `test_contains_any_case_insensitive`: Default case-insensitive behavior +- `test_contains_any_case_sensitive`: Case-sensitive search +- `test_contains_any_case_sensitive_found`: Case-sensitive with matching case +- `test_contains_any_empty_input`: Empty input string handling +- `test_contains_any_empty_substrings`: Empty substrings input +- `test_contains_any_whitespace_substrings`: Whitespace-only lines are ignored +- `test_contains_any_partial_match`: Substring matching (anime matches animation) + +## Mocking + +**Framework:** None - unittest provides basic mocking but not used in this codebase + +**Patterns:** +- No mock objects used +- No external dependencies mocked +- Tests use real node instances with direct method calls +- All test data is embedded in test methods (no external fixtures) + +**What to Mock:** +- No mocking required; all node classes are stateless and dependency-free + +**What NOT to Mock:** +- Don't mock the node classes - test them directly with real instances + +## Fixtures and Factories + +**Test Data:** +- All test data embedded directly in test methods +- No separate fixture files or factory classes +- Input strings created inline: + ```python + input_string = "This is a test string with test word" + strings_to_remove = "test" + ``` + +**Location:** +- Test data is co-located with test methods (no separate fixtures directory) +- Issue-specific test data clearly labeled with issue references: + ```python + def test_strip_punctuation_preserved(self): + # Test Issue #6 fix: punctuation should be preserved + input_string = "Let's meet, Grandma!" + ``` + +## Coverage + +**Requirements:** None enforced (no coverage configuration detected) + +**View Coverage:** +```bash +# Using unittest's built-in capabilities (limited): +python -m unittest discover -v + +# Better coverage with coverage.py (not currently installed): +pip install coverage +coverage run -m unittest test_string_nodes.py +coverage report +``` + +**Observed Coverage:** +- **StringStripNode:** 100% - All major code paths tested (basic, multiple, case sensitivity, punctuation handling, spaces) +- **StringMultiReplaceNode:** 100% - All code paths tested (basic, multiple, case sensitivity, custom delimiter, punctuation, whitespace preservation) +- **StringConditionalAppendNode:** 100% - All code paths tested (found/not found, position, case sensitivity, separator options, multiple strings) +- **StringContainsAnyNode:** 100% - All code paths tested (found/not found, case sensitivity, empty inputs, whitespace handling, partial matching) +- **StringPreviewNode:** 0% - No tests (trivial pass-through logic) +- **StringTextboxNode:** 0% - No tests (trivial pass-through logic) + +## Test Types + +**Unit Tests:** +- Scope: Individual node class methods +- Approach: Direct method invocation with arranged inputs, assert on single output +- Example: `test_strip_basic_functionality` tests `string_strip()` method with specific parameters +- No test doubles or stubs - real objects used + +**Integration Tests:** +- Not present in codebase (no multi-component interaction testing) + +**E2E Tests:** +- Not present (ComfyUI-specific E2E would require full ComfyUI runtime) + +**Issue-Driven Tests:** +- Tests explicitly tied to GitHub issues in comments +- Issue #6: Punctuation preservation tests +- Issue #8: Whitespace preservation in replacements tests +- Issue #9: StringContainsAnyNode feature tests +- Pattern: When fixing a bug, issue number added as comment and test created to verify fix + +## Common Patterns + +**Async Testing:** +- Not applicable - Python standard library code without async/await + +**Error Testing:** +- No explicit error/exception testing +- Edge cases tested: empty strings, whitespace-only input, multiple spaces +- Validation through return values rather than exception handling + +**Testing Multiline Input:** +```python +strings_to_remove = """test +More Text""" +result = self.strip_node.string_strip( + input_string=input_string, + strings_to_remove=strings_to_remove, + match_case=False, + match_whole_string=True, + preserve_punctuation=True, + remove_extra_spaces=True +)[0] +``` + +**Testing Multiple Parameters:** +```python +result, was_appended = self.append_node.conditional_append( + input_string=input_string, + strings_to_check=strings_to_check, + position="end", + match_case=False, + separator=", " +) +self.assertEqual(result, expected_string) +self.assertTrue(was_appended) +``` + +**Testing Boolean Returns:** +- StringConditionalAppendNode returns `(string, boolean)` tuple +- StringContainsAnyNode returns `(boolean, string)` tuple +- Tests unpack both return values and assert on each + +--- + +*Testing analysis: 2026-03-03*