@masonbuchanan/polish-cli
v0.2.4
Published
AI-powered file organization for Obsidian with automatic markdown conversion
Maintainers
Readme
Polish 🪞
AI-powered file organization for Obsidian with automatic markdown conversion
Polish creates a dual-organization system that converts all your files into tagged markdown documents for your Obsidian vault while intelligently organizing the original files in a separate directory structure. Using AI-powered categorization and tagging, Polish transforms any file type into a searchable, linked knowledge base.
Table of Contents
- Features
- Installation
- Quick Start
- Core Concepts
- Usage Modes
- CLI Commands
- Configuration
- File Processing
- Programmatic Usage
- Architecture
- Dependencies
- Troubleshooting
- Contributing
- License
Features
👤 Multi-Profile Support
- Manage multiple vault configurations for different projects or workflows
- Quick switching between profiles without reconfiguration
- Profile cloning for creating similar setups
- Import/export profiles for backup or sharing
🤖 AI-Powered Organization
- Uses Claude AI to intelligently categorize files based on content
- Context-aware tagging that understands relationships between files
- Adaptive categorization that learns from your vault structure
- Automatic detection of file topics, projects, and themes
📝 Markdown Conversion
- Converts all file types into markdown documents for Obsidian
- Extracts text content from PDFs, DOC files, and images (OCR)
- Preserves metadata and creates structured frontmatter
- Links back to original files for reference
🏷️ Smart Tagging
- Generates hierarchical tags based on content and context
- Automatic type, date, and format tags
- Custom tag patterns for specific naming conventions
- Configurable tag limits and confidence thresholds
📂 Dual Organization
- Keeps original files organized separately from vault
- Multiple organization styles: type-based, date-based, or project-based
- Optional year-based folder structure for long-term archiving
- Maintains file integrity and original formats
🔄 Multiple Processing Modes
- Claude Code Mode: Interactive processing within Claude Code environment
- API Mode: Batch processing using Anthropic API
- Hybrid Mode: Combines local rules with AI assistance
- Local Mode: Rule-based organization without AI (offline capable)
⚡ Batch Processing
- Efficient handling of large file collections
- Progress tracking with real-time feedback
- Configurable batch sizes for optimal performance
- Dry-run mode for previewing changes
📦 Archive Support
- Recursive extraction and processing of ZIP archives
- Individual markdown documents for each extracted file
- Summary documents linking all archive contents
- Maintains archive structure context
Installation
Global Installation (Recommended)
npm install -g @masonbuchanan/polish-cliLocal Installation
npm install @masonbuchanan/polish-cliPrerequisites
- Node.js: Version 18.0.0 or higher
- npm: Version 8.0.0 or higher
- Obsidian: Installed with a configured vault (optional, but recommended)
- Anthropic API Key: Required for API mode (optional for other modes)
Quick Start
1. Initialize Configuration
polish config initThis interactive wizard will guide you through:
- Setting your Obsidian vault path
- Configuring original files organization directory
- Setting up source directories to monitor
- Choosing your preferred processing mode
- Configuring tagging preferences
2. Create a Profile
polish profile create my-vaultProfiles allow you to manage multiple configurations. Each profile stores:
- Vault and originals paths
- Source directories
- Processing preferences
- Tagging rules
- API configuration
3. Organize Files
# Organize files from configured sources
polish organize
# Organize a specific directory
polish organize ~/Desktop/ToOrganize
# Preview changes without executing
polish organize --dry-run
# Copy files instead of moving them
polish organize --copyCore Concepts
The Dual-Organization System
Polish maintains two separate but linked organizational structures:
1. Obsidian Vault (Markdown Documents)
Your vault contains markdown representations of all files, making everything searchable and linkable within Obsidian. Each markdown document includes:
- Extracted text content
- Rich metadata in frontmatter
- Tags for categorization
- Links to original files
- Processing timestamps
2. Original Files Archive
Original files are preserved in their native formats, organized by:
- File type (documents, media, code, etc.)
- Date (optional year-based folders)
- Project (custom categorization)
This dual approach gives you:
- Searchability: Find anything through Obsidian's search
- Preservation: Keep original files in native formats
- Context: View files within your knowledge graph
- Accessibility: Quick access to originals when needed
Processing Workflow
Source Directory → File Scanner → Content Extractor
↓
AI Analysis
↓
┌─────────────────┴──────────────────┐
↓ ↓
Markdown Generator File Organizer
↓ ↓
Obsidian Vault Original Files Archive- Scanning: Polish scans configured source directories for files
- Extraction: Content is extracted from files (text from PDFs, etc.)
- Analysis: AI analyzes content to suggest tags and categories
- Generation: Markdown documents are created with metadata
- Organization: Files are moved/copied to appropriate locations
File Type Mapping
Polish automatically maps file types to vault folders:
| File Type | Extensions | Default Vault Folder | |-----------|-----------|---------------------| | Documents | pdf, docx, doc, txt, rtf, odt | Documents/ | | Images | png, jpg, jpeg, gif, bmp, svg, webp | Media/ | | Code | js, ts, py, java, cpp, c, go, rs, rb, php | Code/ | | Data | json, csv, xml, yaml, yml | References/ | | Media | mp3, wav, mp4, avi, mov, mkv | Media/ | | Archives | zip, tar, gz, rar, 7z | Archives/ |
These mappings are fully customizable in your configuration.
Usage Modes
Claude Code Mode (Default, Recommended)
polish organize --mode claude-codeBest for: Interactive organization, testing, development
Features:
- No API key required
- Works within Claude Code environment
- Real-time feedback and suggestions
- Interactive error handling
- Best for learning and experimentation
Requirements: Running within Claude Code environment
Claude API Mode
export ANTHROPIC_API_KEY="your-api-key"
polish organize --mode api --batch 20Best for: Batch processing, automation, production use
Features:
- Direct API access to Claude
- Configurable batch sizes
- Faster processing for large file sets
- Programmatic control
- Cost-effective for bulk operations
Requirements: Anthropic API key
Configuration:
polish config set api.apiKey "sk-ant-..."
polish config set api.model "claude-3-5-sonnet-20241022"
polish config set api.maxTokens 4096Hybrid Mode
polish organize --mode hybridBest for: Balanced approach, semi-automated workflows
Features:
- Uses local rules for simple files
- AI analysis for complex/ambiguous files
- Optimized cost vs. quality
- Fallback to local rules if API unavailable
Local Mode
polish organize --mode localBest for: Offline work, simple organization, cost-free operation
Features:
- No AI processing
- Rule-based categorization
- File extension mapping
- Date-based organization
- Works completely offline
- Zero API costs
Limitations: No content-based tagging or advanced categorization
CLI Commands
Profile Management
Create Profile
polish profile create <name> [options]
Options:
-d, --description <desc> Profile description
-v, --vault <path> Vault path
-o, --originals <path> Originals path
Examples:
polish profile create work -d "Work projects vault"
polish profile create personal --vault ~/Documents/MyVaultList Profiles
polish profile list
# Output shows:
# ● work (active)
# personal
# researchSwitch Profile
polish profile switch <name>
Example:
polish profile switch personalShow Current Profile
polish profile current
# Shows detailed info about active profileClone Profile
polish profile clone <source> <destination>
Example:
polish profile clone work work-backupDelete Profile
polish profile delete <name> [--force]
Example:
polish profile delete old-project --forceRename Profile
polish profile rename <old-name> <new-name>
Example:
polish profile rename work company-vaultExport/Import Profile
polish profile export <name> <output-file>
polish profile import <input-file> <name>
Examples:
polish profile export work ~/work-profile.json
polish profile import ~/work-profile.json work-restoredManage Profile Sources
polish profile add-source <profile> <source-path>
polish profile remove-source <profile> <source-path>
polish profile list-sources <profile>
Examples:
polish profile add-source work ~/Documents/WorkFiles
polish profile remove-source work ~/Old/Directory
polish profile list-sources workFile Organization
Organize Command
polish organize [source] [options]
Arguments:
source Optional source directory (overrides config)
Options:
-p, --profile <name> Use specific profile
-v, --vault <path> Obsidian vault path (overrides profile)
-o, --originals <path> Original files path (overrides profile)
-d, --dry-run Preview changes without executing
-t, --types <types> Comma-separated file types to process
-m, --mode <mode> Processing mode (claude-code|api|hybrid|local)
-b, --batch <size> Batch size for API mode (default: 10)
-c, --copy Copy files instead of moving
Examples:
polish organize
polish organize ~/Desktop/NewFiles
polish organize --profile work --dry-run
polish organize --types pdf,docx --mode api
polish organize --copy --batch 20Configuration Management
Initialize Config
polish config init
# Interactive wizard for first-time setupShow Configuration
polish config show
# Displays current configuration in JSON formatGet Config Value
polish config get <key>
Examples:
polish config get vault.path
polish config get processing.maxFileSize
polish config get api.modeSet Config Value
polish config set <key> <value>
Examples:
polish config set vault.path ~/Documents/Vault
polish config set processing.maxFileSize "50MB"
polish config set tagging.maxTags 15
polish config set api.mode "api"Edit Config
polish config edit
# Opens configuration in default editorAnalysis & Status
Analyze Files
polish analyze [source] [options]
Options:
-t, --types <types> File types to analyze
-r, --report <path> Save analysis report to file
Examples:
polish analyze ~/Desktop
polish analyze --types pdf,docx --report ~/analysis.jsonShow Status
polish status [options]
Options:
-p, --profile <name> Show specific profile status
# Displays:
# - Active profile
# - Vault and originals paths
# - Configured sources
# - File counts and statistics
# - Last processing timestampsList Supported File Types
polish list-supported
# Shows all supported file extensions by categoryConfiguration
Configuration File Location
Polish stores configuration in ~/.polish/:
config.json: Global settings and active profileprofiles/: Individual profile configurations
Configuration Structure
{
"vault": {
"path": "/path/to/obsidian/vault",
"structure": {
"documents": "Documents",
"media": "Media",
"code": "Code",
"references": "References"
}
},
"originals": {
"path": "/path/to/organized/files",
"organizationStyle": "type-based", // type-based, date-based, project-based
"createYearFolders": true
},
"sources": [
{
"path": "/path/to/source/directory",
"includeSubfolders": true
}
],
"processing": {
"extractText": true,
"maxFileSize": "50MB",
"supportedFormats": ["pdf", "docx", "txt", "png", "jpg"]
},
"tagging": {
"maxTags": 10,
"autoGenerateTypeTags": true,
"autoGenerateDateTags": true,
"customTagPatterns": {
"project-*": "project/{name}",
"WIP-*": "status/in-progress"
}
},
"api": {
"mode": "claude-code",
"apiKey": "sk-ant-...",
"model": "claude-3-5-sonnet-20241022",
"maxTokens": 4096,
"temperature": 0.7
}
}Customizing Vault Structure
# Change default folder names
polish config set vault.structure.documents "Docs"
polish config set vault.structure.media "Assets"
polish config set vault.structure.code "Scripts"Customizing Originals Organization
# Choose organization style
polish config set originals.organizationStyle "date-based"
# Enable/disable year folders
polish config set originals.createYearFolders trueCustomizing Tagging
# Set maximum tags per file
polish config set tagging.maxTags 15
# Enable automatic tags
polish config set tagging.autoGenerateTypeTags true
polish config set tagging.autoGenerateDateTags true
# Add custom tag patterns
polish config set tagging.customTagPatterns.project-* "project/{name}"File Processing
Supported File Types
Documents
- PDF (
.pdf): Full text extraction, metadata preservation - Microsoft Word (
.docx,.doc): Text extraction with formatting - Plain Text (
.txt): Direct content inclusion - Rich Text (
.rtf): Formatted text extraction - OpenDocument (
.odt): Text extraction
Images
- Common Formats (
.png,.jpg,.jpeg,.gif): Metadata extraction, OCR capable - Vector Graphics (
.svg): Source code extraction - Modern Formats (
.webp,.avif): Metadata extraction
Code Files
- JavaScript/TypeScript (
.js,.ts,.tsx,.jsx): Syntax highlighting, documentation extraction - Python (
.py): Syntax highlighting, docstring extraction - Java (
.java): Syntax highlighting, JavaDoc extraction - C/C++ (
.c,.cpp,.h,.hpp): Syntax highlighting - Go (
.go): Syntax highlighting - Rust (
.rs): Syntax highlighting - Ruby (
.rb): Syntax highlighting - PHP (
.php): Syntax highlighting
Data Files
- JSON (
.json): Formatted display, structure analysis - CSV (
.csv): Table generation, data preview - XML (
.xml): Formatted display, structure analysis - YAML (
.yaml,.yml): Formatted display, structure analysis
Archives
- ZIP (
.zip): Recursive extraction and processing - TAR (
.tar,.tar.gz,.tgz): Future support planned - RAR (
.rar): Future support planned - 7-Zip (
.7z): Future support planned
Media Files
- Audio (
.mp3,.wav,.flac): Metadata extraction - Video (
.mp4,.avi,.mov,.mkv): Metadata extraction - Note: Full transcription requires additional services
Content Extraction Methods
Text Extraction
- PDF: Uses
pdf-parselibrary for text extraction - DOCX: Uses
mammothlibrary for document parsing - Images: Optional OCR integration (requires external service)
Metadata Extraction
- File size, creation date, modification date
- EXIF data for images
- Document properties for office files
- ID3 tags for audio files
Generated Markdown Format
Each processed file creates a markdown document with this structure:
---
title: "Document Name"
originalFile: "[[file:///path/to/original.pdf]]"
sourceLocation: "/original/source/path/file.pdf"
fileType: "pdf"
created: "2024-01-15T10:30:00.000Z"
processed: "2024-01-20T14:22:00.000Z"
tags:
- type/document
- format/pdf
- project/q1-planning
- topic/meeting
- date/2024/01
---
# Document Name
## Original File Information
- **Type**: PDF Document
- **Size**: 2.5 MB
- **Created**: January 15, 2024
- **Location**: [Open Original](file:///path/to/original.pdf)
## Content
[Extracted text content appears here...]
## Tags
#type/document #format/pdf #project/q1-planning #topic/meeting #date/2024/01
---
*Processed by Polish on January 20, 2024*
*Original: [file.pdf](file:///path/to/original.pdf)*Archive Processing
When processing ZIP archives, Polish:
- Extracts all files to a temporary directory
- Processes each file individually using the standard workflow
- Creates individual markdown documents for each file
- Generates a summary markdown linking all extracted files
- Organizes the original archive in the appropriate location
- Cleans up temporary files
Example archive summary:
---
title: "Archive: project_files"
archiveType: "expanded"
extractedFiles: 15
---
# Archive: project_files.zip
This archive contained 15 files that were extracted and processed:
## Document Files (8)
- [[Documents/report|report]]
- [[Documents/notes|notes]]
- [[Documents/summary|summary]]
## Image Files (5)
- [[Media/diagram|diagram]]
- [[Media/screenshot1|screenshot1]]
## Code Files (2)
- [[Code/script|script]]
- [[Code/config|config]]Programmatic Usage
Basic Usage
import { Polish } from '@masonbuchanan/polish-cli';
// Create instance with default config
const polish = new Polish();
// Load configuration
await polish.loadConfig();
// Organize files
const results = await polish.organize({
sources: ['/path/to/files'],
dryRun: false,
copy: false,
onProgress: (current, total, file) => {
console.log(`Processing ${file.name} (${current}/${total})`);
}
});
console.log(`Processed: ${results.summary.successful}`);
console.log(`Failed: ${results.summary.failed}`);
console.log(`Duration: ${results.summary.duration}ms`);Using Profiles
import { Polish, ProfileManager } from '@masonbuchanan/polish-cli';
// Initialize profile manager
const profileManager = new ProfileManager();
await profileManager.initialize();
// List available profiles
const profiles = await profileManager.listProfiles();
console.log('Available profiles:', profiles.map(p => p.name));
// Use specific profile
const polish = new Polish(undefined, 'work');
await polish.loadConfig();
// Organize with profile
const results = await polish.organize({
sources: ['/path/to/work/files']
});Custom Configuration
import { Polish, Config } from '@masonbuchanan/polish-cli';
// Create custom configuration
const customConfig: Partial<Config> = {
vault: {
path: '/custom/vault/path',
structure: {
documents: 'Docs',
media: 'Assets',
code: 'Scripts',
references: 'Refs'
}
},
api: {
mode: 'api',
apiKey: process.env.ANTHROPIC_API_KEY,
model: 'claude-3-5-sonnet-20241022'
},
tagging: {
maxTags: 15,
autoGenerateTypeTags: true,
autoGenerateDateTags: true,
customTagPatterns: {}
}
};
const polish = new Polish(customConfig);
// Organize with custom config
const results = await polish.organize({
sources: ['/path/to/files'],
dryRun: false
});Advanced Usage with Services
import {
ConfigService,
ProfileManager,
ClaudeService,
FileScanner,
FileProcessor
} from '@masonbuchanan/polish-cli';
// Use individual services
const configService = new ConfigService();
const config = await configService.loadConfig();
const claudeService = new ClaudeService(config.api);
const fileScanner = new FileScanner(config);
const fileProcessor = new FileProcessor(config, claudeService);
// Scan for files
const files = await fileScanner.scan(config.sources);
console.log(`Found ${files.length} files`);
// Process files with custom options
const results = await fileProcessor.processFiles(files, {
dryRun: false,
copy: false,
batchSize: 20,
onProgress: (current, total, file) => {
console.log(`[${current}/${total}] Processing ${file.name}`);
}
});Type Definitions
interface OrganizationResult {
processed: ProcessedFile[];
failed: Array<{ file: FileInfo; error: string }>;
summary: {
total: number;
successful: number;
failed: number;
duration: number;
};
}
interface ProcessedFile {
original: FileInfo;
markdownPath: string;
originalNewPath: string;
content: string;
frontmatter: Frontmatter;
tags: string[];
category: string;
}
interface FileInfo {
path: string;
name: string;
extension: string;
size: number;
createdAt: Date;
modifiedAt: Date;
type: FileType;
}Architecture
Module Structure
polish-cli/
├── cli/
│ ├── index.ts # CLI entry point
│ └── commands/
│ ├── organize.ts # File organization command
│ ├── config.ts # Configuration management
│ ├── analyze.ts # File analysis command
│ ├── status.ts # Status display
│ └── profile.ts # Profile management
├── services/
│ ├── ConfigService.ts # Configuration management
│ ├── ProfileManager.ts # Profile CRUD operations
│ └── ClaudeService.ts # AI integration
├── modules/
│ ├── FileScanner.ts # File discovery and filtering
│ ├── FileProcessor.ts # Core processing logic
│ ├── ContentExtractor.ts # Content extraction from files
│ └── MarkdownGenerator.ts # Markdown document generation
├── types/
│ └── index.ts # TypeScript type definitions
└── utils/
└── formatting.ts # Utility functionsComponent Responsibilities
CLI Layer (cli/)
- Purpose: User interaction and command routing
- Responsibilities:
- Parse command-line arguments
- Display progress and results
- Handle user input
- Error presentation
Services Layer (services/)
- ConfigService: Configuration file management
- ProfileManager: Profile lifecycle and switching
- ClaudeService: AI API integration and fallback
Modules Layer (modules/)
- FileScanner: File system traversal and discovery
- FileProcessor: Orchestrates the processing pipeline
- ContentExtractor: Extracts text from various formats
- MarkdownGenerator: Creates markdown documents
Types Layer (types/)
- TypeScript interfaces and enums
- Type safety across the application
- API contracts
Utils Layer (utils/)
- File name sanitization
- Date formatting
- Path manipulation
Data Flow
User Command
↓
CLI Parser
↓
Profile Manager → Load Configuration
↓
File Scanner → Discover Files
↓
File Processor → For Each File:
├── Content Extractor → Extract Text
├── Claude Service → AI Analysis
│ ├── Tag Suggestions
│ └── Category Suggestions
├── Markdown Generator → Create Document
└── File Operations → Move/Copy Files
↓
Organization Result
↓
CLI Output → Display SummaryDependencies
Production Dependencies
Core Dependencies
@anthropic-ai/sdk (^0.20.0): Claude AI API client
- Purpose: AI-powered categorization and tagging
- Used by:
ClaudeService
commander (^11.1.0): CLI framework
- Purpose: Command-line argument parsing
- Used by:
cli/index.ts
chalk (^5.3.0): Terminal styling
- Purpose: Colored output and formatting
- Used by: All CLI commands
ora (^8.0.1): Loading spinners
- Purpose: Progress indication
- Used by: Commands with long operations
inquirer (^9.2.15): Interactive prompts
- Purpose: User input collection
- Used by: Configuration and profile wizards
File Processing
pdf-parse (^1.1.1): PDF text extraction
- Purpose: Extract text from PDF documents
- Used by:
ContentExtractor
mammoth (^1.6.0): DOCX parsing
- Purpose: Extract text from Word documents
- Used by:
ContentExtractor
sharp (^0.33.2): Image processing
- Purpose: Image metadata and manipulation
- Used by:
ContentExtractor
unzipper (^0.10.14): ZIP archive extraction
- Purpose: Recursive archive processing
- Used by:
FileProcessor
Utilities
gray-matter (^4.0.3): YAML frontmatter parsing
- Purpose: Markdown frontmatter handling
- Used by:
MarkdownGenerator
globby (^14.0.0): File pattern matching
- Purpose: File discovery with patterns
- Used by:
FileScanner
dotenv (^16.4.1): Environment variables
- Purpose: API key management
- Used by: Configuration loading
Development Dependencies
TypeScript & Build
- typescript (^5.3.3): Type-safe development
- tsx (^4.7.0): TypeScript execution
- @types/node (^20.11.5): Node.js type definitions
Testing
- jest (^29.7.0): Testing framework
- ts-jest (^29.1.1): TypeScript Jest transformer
- @types/jest (^29.5.11): Jest type definitions
Linting & Formatting
- eslint (^8.56.0): Code linting
- @typescript-eslint/parser (^6.19.0): TypeScript ESLint parser
- @typescript-eslint/eslint-plugin (^6.19.0): TypeScript linting rules
Code Quality
- husky (^8.0.3): Git hooks
- pre-commit (^1.2.2): Pre-commit hooks
Peer Dependencies
None required, but works best with:
- Obsidian: For vault management and markdown viewing
- Claude Code: For claude-code mode operation
Optional Dependencies
- tesseract.js: For OCR functionality (not yet implemented)
- ffmpeg: For video processing (future feature)
Troubleshooting
Common Issues
"Profile not found"
# Check available profiles
polish profile list
# Create a new profile
polish profile create default
# Initialize configuration
polish config init"No files found to organize"
# Check configured sources
polish config get sources
# Add a source directory
polish profile add-source default ~/Desktop
# Or specify directory directly
polish organize ~/path/to/files"API key not configured"
# For API mode, set your key
export ANTHROPIC_API_KEY="sk-ant-..."
# Or set in config
polish config set api.apiKey "sk-ant-..."
# Or use a different mode
polish organize --mode claude-code"Permission denied" errors
# Ensure write permissions
ls -la ~/.polish
# Fix permissions
chmod 755 ~/.polish
chmod 644 ~/.polish/config.jsonFile processing failures
# Check supported formats
polish list-supported
# Analyze files first
polish analyze ~/path/to/files
# Use dry-run to test
polish organize --dry-run
# Check specific file types
polish organize --types pdf,docxDebug Mode
Enable verbose logging:
# Set environment variable
export DEBUG=polish:*
# Run command with debug output
polish organizeGetting Help
- Issues: https://github.com/Masbuc53/polish-cli/issues
- Discussions: https://github.com/Masbuc53/polish-cli/discussions
- Documentation: See
/docsfolder for detailed guides
Contributing
We welcome contributions! Here's how to get started:
Development Setup
# Clone repository
git clone https://github.com/Masbuc53/polish-cli.git
cd polish-cli
# Install dependencies
npm install
# Build project
npm run build
# Run in development
npm run dev
# Run tests
npm test
# Run tests with coverage
npm run test:coverageRunning Tests
# Unit tests
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverage
# CI tests
npm run test:ciCode Quality
# Lint code
npm run lint
# Fix linting issues
npm run lint:fix
# Type check
npm run typecheck
# Run all quality checks
npm run quality-checkMaking Changes
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Write tests for your changes
- Implement your feature
- Test thoroughly:
npm run quality-check - Commit with descriptive messages
- Push to your fork
- Create a Pull Request
Commit Message Format
We follow conventional commits:
type(scope): subject
body
footerTypes: feat, fix, docs, style, refactor, test, chore
Examples:
feat(cli): add support for RAR archives
fix(processor): handle files with special characters
docs(readme): improve installation instructionsLicense
MIT License
Copyright (c) 2024 Mason Buchanan
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.
Support
Made with by Mason Buchanan | GitHub
