Add Docker support and documentation updates
- Add Dockerfile for containerized builds - Add .dockerignore for cleaner builds - Add CLAUDE.md for development guidance - Update README and docs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
7e9bd0d533
commit
65279bd8c8
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
*.egg-info
|
||||||
|
.eggs
|
||||||
|
*.egg
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
.venv
|
||||||
|
venv
|
||||||
|
ENV
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose*
|
||||||
|
.docker
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
.coverage
|
||||||
|
.pytest_cache
|
||||||
|
htmlcov
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
SmartTools is a lightweight personal tool builder for AI-powered CLI commands. It lets users create custom terminal commands that call AI providers, chain prompts with Python code steps, and use them like any Unix pipe command.
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install for development
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
pytest
|
||||||
|
|
||||||
|
# Run a single test
|
||||||
|
pytest tests/test.py::test_name
|
||||||
|
|
||||||
|
# Run the CLI
|
||||||
|
python -m smarttools.cli
|
||||||
|
|
||||||
|
# Launch the UI
|
||||||
|
smarttools ui
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Core Modules (`src/smarttools/`)
|
||||||
|
|
||||||
|
- **cli.py**: Entry point (`smarttools` command). Routes subcommands: list, create, edit, delete, test, run, ui, refresh
|
||||||
|
- **tool.py**: Tool definition dataclasses (`Tool`, `ToolArgument`, `PromptStep`, `CodeStep`), YAML config loading/saving, wrapper script generation
|
||||||
|
- **runner.py**: Execution engine. Runs tool steps sequentially, handles variable substitution (`{input}`, `{varname}`), executes Python code steps via `exec()`
|
||||||
|
- **providers.py**: Provider abstraction. Calls AI CLI tools via subprocess, reads provider configs from `~/.smarttools/providers.yaml`
|
||||||
|
- **ui.py**: UI dispatcher - selects between urwid and snack implementations
|
||||||
|
- **ui_urwid.py**: Full TUI implementation using urwid library
|
||||||
|
- **ui_snack.py**: Fallback TUI using python-newt/snack
|
||||||
|
|
||||||
|
### Key Paths
|
||||||
|
|
||||||
|
- **Tools storage**: `~/.smarttools/<toolname>/config.yaml`
|
||||||
|
- **Wrapper scripts**: `~/.local/bin/<toolname>` (auto-generated bash scripts)
|
||||||
|
- **Provider config**: `~/.smarttools/providers.yaml`
|
||||||
|
|
||||||
|
### Tool Structure
|
||||||
|
|
||||||
|
Tools are YAML configs with:
|
||||||
|
- `name`, `description`, `category`
|
||||||
|
- `arguments`: Custom flags with defaults (e.g., `--max` → `{max}`)
|
||||||
|
- `steps`: Ordered list of `prompt` or `code` steps
|
||||||
|
- `output`: Template for final output (e.g., `"{response}"`)
|
||||||
|
|
||||||
|
### Step Types
|
||||||
|
|
||||||
|
1. **Prompt Step**: Calls AI provider with template, stores result in `output_var`
|
||||||
|
2. **Code Step**: Executes Python code via `exec()`, captures specified variables
|
||||||
|
|
||||||
|
### Variable Flow
|
||||||
|
|
||||||
|
Variables are passed between steps:
|
||||||
|
- `{input}` - always available (stdin/file content)
|
||||||
|
- `{argname}` - from tool arguments
|
||||||
|
- `{step_output_var}` - from previous step's `output_var`
|
||||||
|
|
||||||
|
Variable substitution is simple string replacement in `runner.py:substitute_variables()`.
|
||||||
|
|
||||||
|
## Provider System
|
||||||
|
|
||||||
|
Providers are CLI tools that accept prompts via stdin and output to stdout. Defined in `~/.smarttools/providers.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
providers:
|
||||||
|
- name: claude
|
||||||
|
command: "claude -p"
|
||||||
|
- name: mock
|
||||||
|
command: "echo '[MOCK]'"
|
||||||
|
```
|
||||||
|
|
||||||
|
The `mock` provider is built-in for testing without API calls.
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
# SmartTools - AI-powered CLI command builder
|
||||||
|
# Build: docker build -t smarttools .
|
||||||
|
# Run: docker run -it --rm -v ~/.smarttools:/root/.smarttools smarttools
|
||||||
|
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
LABEL maintainer="rob"
|
||||||
|
LABEL description="SmartTools - Personal AI-powered CLI command builder"
|
||||||
|
|
||||||
|
# Install system dependencies
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy project files
|
||||||
|
COPY pyproject.toml README.md ./
|
||||||
|
COPY src/ ./src/
|
||||||
|
COPY examples/ ./examples/
|
||||||
|
COPY docs/ ./docs/
|
||||||
|
|
||||||
|
# Install SmartTools and dependencies
|
||||||
|
RUN pip install --no-cache-dir -e ".[dev]"
|
||||||
|
|
||||||
|
# Create smarttools directory
|
||||||
|
RUN mkdir -p /root/.smarttools /root/.local/bin
|
||||||
|
|
||||||
|
# Install example tools
|
||||||
|
RUN python examples/install.py
|
||||||
|
|
||||||
|
# Generate CLI wrappers
|
||||||
|
RUN smarttools refresh
|
||||||
|
|
||||||
|
# Add local bin to PATH
|
||||||
|
ENV PATH="/root/.local/bin:${PATH}"
|
||||||
|
|
||||||
|
# Default command - show help
|
||||||
|
CMD ["smarttools", "--help"]
|
||||||
|
|
||||||
|
# For interactive use:
|
||||||
|
# docker run -it --rm smarttools smarttools ui
|
||||||
|
# docker run -it --rm smarttools bash
|
||||||
68
README.md
68
README.md
|
|
@ -123,6 +123,59 @@ cat file.txt | mytool --dry-run
|
||||||
cat file.txt | mytool --provider mock
|
cat file.txt | mytool --provider mock
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Composing Tools
|
||||||
|
|
||||||
|
SmartTools follows Unix philosophy: each tool does one thing well, and tools chain together for complex workflows.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Chain SmartTools together
|
||||||
|
cat error.log | log-errors | summarize | translate --lang Spanish
|
||||||
|
|
||||||
|
# Code review pipeline: focus on changes, review, then summarize
|
||||||
|
git diff | diff-focus | review-code | tldr
|
||||||
|
|
||||||
|
# Extract data, convert format, analyze
|
||||||
|
cat report.pdf | json-extract --fields "revenue, costs, date" | json2csv | csv-insights
|
||||||
|
|
||||||
|
# Process text through multiple transformations
|
||||||
|
cat technical_doc.txt | simplify | fix-grammar | tone-shift --tone friendly
|
||||||
|
|
||||||
|
# Generate code, validate it, then explain what it does
|
||||||
|
echo "parse CSV and calculate averages" | code-validate | explain-code
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mix with standard Unix tools:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract emails, deduplicate, count
|
||||||
|
cat inbox.txt | extract-emails | sort -u | wc -l
|
||||||
|
|
||||||
|
# Find errors in multiple logs, explain them
|
||||||
|
cat *.log | grep -i error | explain-error
|
||||||
|
|
||||||
|
# Generate changelog for specific author
|
||||||
|
git log --author="rob" --oneline | changelog > CHANGELOG.md
|
||||||
|
|
||||||
|
# Review only large files in a commit
|
||||||
|
git diff --stat | awk '$3 > 100 {print $1}' | xargs cat | review-code
|
||||||
|
```
|
||||||
|
|
||||||
|
**Build shell functions for common pipelines:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ~/.bashrc
|
||||||
|
quick-review() {
|
||||||
|
git diff "$@" | diff-focus | review-code --focus "bugs and security" | tldr
|
||||||
|
}
|
||||||
|
|
||||||
|
translate-doc() {
|
||||||
|
cat "$1" | summarize | translate --lang "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: quick-review HEAD~3
|
||||||
|
# Usage: translate-doc manual.txt French
|
||||||
|
```
|
||||||
|
|
||||||
## Example Tools
|
## Example Tools
|
||||||
|
|
||||||
SmartTools comes with 28 pre-built examples you can install:
|
SmartTools comes with 28 pre-built examples you can install:
|
||||||
|
|
@ -347,12 +400,17 @@ This lets you generate or modify Python code using AI directly within the tool b
|
||||||
|
|
||||||
## Philosophy
|
## Philosophy
|
||||||
|
|
||||||
SmartTools is a **personal power tool**:
|
SmartTools is built on **Unix philosophy**:
|
||||||
|
|
||||||
- **You own your tools** - YAML files you can read, edit, share
|
1. **Do one thing well** - Each tool solves one problem. `summarize` summarizes. `translate` translates. No bloated mega-tools.
|
||||||
- **You choose the AI** - Any provider, swap anytime
|
|
||||||
- **You accept responsibility** - No sandboxing, like any script you write
|
2. **Compose freely** - Tools read stdin, write stdout. Chain them: `cat doc.txt | summarize | translate --lang French`
|
||||||
- **Unix philosophy** - Small tools that compose
|
|
||||||
|
3. **Text is the interface** - No proprietary formats. Pipe text in, get text out. Works with `grep`, `awk`, `sed`, and every other Unix tool.
|
||||||
|
|
||||||
|
4. **You own everything** - Tools are YAML files. Prompts are plain text. No vendor lock-in, no cloud dependency for your tool definitions.
|
||||||
|
|
||||||
|
5. **Swap parts freely** - Change AI providers per-tool or per-run. Today's best model might not be tomorrow's.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,50 @@ Tool names must:
|
||||||
- Contain only letters, numbers, underscores, and dashes
|
- Contain only letters, numbers, underscores, and dashes
|
||||||
- Not contain spaces or shell-problematic characters (`/\|&;$`"'<>(){}[]!?*#~`)
|
- Not contain spaces or shell-problematic characters (`/\|&;$`"'<>(){}[]!?*#~`)
|
||||||
|
|
||||||
|
## Tool Composition
|
||||||
|
|
||||||
|
SmartTools are designed to chain together like any Unix command.
|
||||||
|
|
||||||
|
### External Pipelines (Tool-to-Tool)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Chain multiple SmartTools
|
||||||
|
cat logs.txt | log-errors | summarize | translate --lang Japanese
|
||||||
|
|
||||||
|
# Mix with standard Unix tools
|
||||||
|
git log --oneline | head -20 | changelog | tee CHANGELOG.md
|
||||||
|
|
||||||
|
# Build complex workflows
|
||||||
|
cat *.py | review-code --focus security | json-extract --fields "issue, severity, file" | json2csv
|
||||||
|
```
|
||||||
|
|
||||||
|
Each tool reads stdin and writes stdout. No special integration needed.
|
||||||
|
|
||||||
|
### Internal Pipelines (Multi-Step)
|
||||||
|
|
||||||
|
Within a single tool, chain steps for preprocessing/validation:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- type: code # Preprocess
|
||||||
|
code: |
|
||||||
|
filtered = '\n'.join(l for l in input.split('\n') if 'ERROR' in l)
|
||||||
|
output_var: filtered
|
||||||
|
- type: prompt # AI processes filtered input
|
||||||
|
prompt: "Explain these errors: {filtered}"
|
||||||
|
provider: claude-haiku
|
||||||
|
output_var: explanation
|
||||||
|
- type: code # Post-process
|
||||||
|
code: |
|
||||||
|
result = f"Found {len(filtered.split(chr(10)))} errors:\n\n{explanation}"
|
||||||
|
output_var: result
|
||||||
|
output: "{result}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use which:**
|
||||||
|
- **External pipelines**: Reusable tools, different providers per stage, standard Unix interop
|
||||||
|
- **Internal pipelines**: Tightly coupled steps, shared context, validation of AI output
|
||||||
|
|
||||||
## What This Design Doesn't Include
|
## What This Design Doesn't Include
|
||||||
|
|
||||||
Intentionally omitted (not needed for personal use):
|
Intentionally omitted (not needed for personal use):
|
||||||
|
|
|
||||||
|
|
@ -958,3 +958,101 @@ output: "{result}"
|
||||||
2. **Use code steps** to validate AI output
|
2. **Use code steps** to validate AI output
|
||||||
3. **Preprocess large inputs** to reduce tokens
|
3. **Preprocess large inputs** to reduce tokens
|
||||||
4. **Test with mock** before using real AI: `--provider mock`
|
4. **Test with mock** before using real AI: `--provider mock`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pipeline Recipes
|
||||||
|
|
||||||
|
SmartTools chain together like Unix commands. Here are practical examples:
|
||||||
|
|
||||||
|
### Development Workflows
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Quick PR review: extract changes, review, summarize
|
||||||
|
git diff main | diff-focus | review-code --focus "bugs and security" | tldr
|
||||||
|
|
||||||
|
# Explain and fix an error in one pipeline
|
||||||
|
python script.py 2>&1 | explain-error | tee error_analysis.txt
|
||||||
|
|
||||||
|
# Generate tests for changed files only
|
||||||
|
git diff --name-only | grep '\.py$' | xargs cat | gen-tests > new_tests.py
|
||||||
|
|
||||||
|
# Create release notes from commits
|
||||||
|
git log v1.0..v1.1 --oneline | changelog | translate --lang French > RELEASE_FR.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Processing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract, transform, analyze
|
||||||
|
curl -s api.example.com/data | json-extract --fields "users, revenue" | json2csv | csv-insights
|
||||||
|
|
||||||
|
# Process multiple files
|
||||||
|
for f in reports/*.txt; do
|
||||||
|
cat "$f" | json-extract --fields "total, date"
|
||||||
|
done | json2csv > summary.csv
|
||||||
|
|
||||||
|
# Clean and deduplicate contacts
|
||||||
|
cat *.vcf | extract-contacts | sort -u -t',' -k2 > contacts.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
### Text Processing Pipelines
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Translate technical docs for international team
|
||||||
|
cat API.md | simplify --level "non-technical" | translate --lang Spanish > API_ES.md
|
||||||
|
|
||||||
|
# Process customer feedback
|
||||||
|
cat feedback.txt | summarize --length "10 points" | tone-shift --tone analytical | json-extract --fields "themes, sentiment"
|
||||||
|
|
||||||
|
# Prepare content for different audiences
|
||||||
|
cat whitepaper.txt | eli5 > simple_version.txt
|
||||||
|
cat whitepaper.txt | summarize > executive_summary.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shell Functions
|
||||||
|
|
||||||
|
Add these to `~/.bashrc` for common workflows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Review recent changes
|
||||||
|
review-recent() {
|
||||||
|
git diff HEAD~"${1:-1}" | diff-focus | review-code | tldr
|
||||||
|
}
|
||||||
|
|
||||||
|
# Quick translate with summary
|
||||||
|
translate-summary() {
|
||||||
|
cat "$1" | summarize | translate --lang "${2:-Spanish}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Analyze any log file
|
||||||
|
analyze-log() {
|
||||||
|
cat "$1" | log-errors | summarize --length "5 key issues"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate commit message and commit
|
||||||
|
auto-commit() {
|
||||||
|
msg=$(git diff --staged | commit-msg)
|
||||||
|
echo "Commit message: $msg"
|
||||||
|
read -p "Commit? [y/N] " confirm
|
||||||
|
[[ $confirm == [yY] ]] && git commit -m "$msg"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Combining with Standard Unix Tools
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Filter before AI processing (saves tokens)
|
||||||
|
cat huge.log | grep -i error | tail -100 | explain-error
|
||||||
|
|
||||||
|
# Post-process AI output
|
||||||
|
cat doc.txt | summarize | fmt -w 80 > formatted_summary.txt
|
||||||
|
|
||||||
|
# Parallel processing
|
||||||
|
find . -name "*.py" | parallel -j4 'cat {} | review-code > {}.review'
|
||||||
|
|
||||||
|
# Watch and process
|
||||||
|
tail -f app.log | grep --line-buffered ERROR | while read line; do
|
||||||
|
echo "$line" | explain-error
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,79 @@ def cmd_refresh(args):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_docs(args):
|
||||||
|
"""View or edit tool documentation."""
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from .tool import get_tools_dir
|
||||||
|
|
||||||
|
tool = load_tool(args.name)
|
||||||
|
if not tool:
|
||||||
|
print(f"Error: Tool '{args.name}' not found.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
readme_path = get_tools_dir() / args.name / "README.md"
|
||||||
|
|
||||||
|
if args.edit:
|
||||||
|
# Edit/create README
|
||||||
|
editor = os.environ.get("EDITOR", "nano")
|
||||||
|
|
||||||
|
# Create a template if README doesn't exist
|
||||||
|
if not readme_path.exists():
|
||||||
|
template = f"""# {args.name}
|
||||||
|
|
||||||
|
{tool.description or 'No description provided.'}
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "input" | {args.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Flag | Default | Description |
|
||||||
|
|------|---------|-------------|
|
||||||
|
"""
|
||||||
|
for arg in tool.arguments:
|
||||||
|
template += f"| `{arg.flag}` | {arg.default or ''} | {arg.description or ''} |\n"
|
||||||
|
|
||||||
|
template += """
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- List any dependencies here
|
||||||
|
"""
|
||||||
|
readme_path.write_text(template)
|
||||||
|
print(f"Created template: {readme_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run([editor, str(readme_path)], check=True)
|
||||||
|
print(f"Documentation updated: {readme_path}")
|
||||||
|
return 0
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("Error: Editor failed.")
|
||||||
|
return 1
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"Error: Editor '{editor}' not found. Set $EDITOR environment variable.")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
# View README
|
||||||
|
if not readme_path.exists():
|
||||||
|
print(f"No documentation found for '{args.name}'.")
|
||||||
|
print(f"Create it with: smarttools docs {args.name} --edit")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(readme_path.read_text())
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main CLI entry point."""
|
"""Main CLI entry point."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
|
|
@ -326,6 +399,12 @@ def main():
|
||||||
p_refresh = subparsers.add_parser("refresh", help="Refresh all wrapper scripts")
|
p_refresh = subparsers.add_parser("refresh", help="Refresh all wrapper scripts")
|
||||||
p_refresh.set_defaults(func=cmd_refresh)
|
p_refresh.set_defaults(func=cmd_refresh)
|
||||||
|
|
||||||
|
# 'docs' command
|
||||||
|
p_docs = subparsers.add_parser("docs", help="View or edit tool documentation")
|
||||||
|
p_docs.add_argument("name", help="Tool name")
|
||||||
|
p_docs.add_argument("-e", "--edit", action="store_true", help="Edit/create README in $EDITOR")
|
||||||
|
p_docs.set_defaults(func=cmd_docs)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# If no command, launch UI
|
# If no command, launch UI
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
@startuml
|
||||||
|
start
|
||||||
|
:Set a = 5;
|
||||||
|
:Set b = 6;
|
||||||
|
:Set c = 7;
|
||||||
|
:Calculate semi-perimeter s = (a + b + c) / 2;
|
||||||
|
:Calculate area = sqrt(s*(s-a)*(s-b)*(s-c));
|
||||||
|
:Print "The area of the triangle is [area]";
|
||||||
|
stop
|
||||||
|
@enduml
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Python Program to find the area of triangle
|
||||||
|
|
||||||
|
a = 5
|
||||||
|
b = 6
|
||||||
|
c = 7
|
||||||
|
|
||||||
|
# Uncomment below to take inputs from the user
|
||||||
|
# a = float(input('Enter first side: '))
|
||||||
|
# b = float(input('Enter second side: '))
|
||||||
|
# c = float(input('Enter third side: '))
|
||||||
|
|
||||||
|
# calculate the semi-perimeter
|
||||||
|
s = (a + b + c) / 2
|
||||||
|
|
||||||
|
# calculate the area
|
||||||
|
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
|
||||||
|
print('The area of the triangle is %0.2f' %area)
|
||||||
Loading…
Reference in New Issue