Clean up readme, and default config

This commit is contained in:
Alexander R.
2026-07-21 22:51:28 +00:00
parent edfcc872d9
commit 0b7259a011
8 changed files with 43 additions and 35 deletions
+3
View File
@@ -174,3 +174,6 @@ cython_debug/
# PyPI configuration file # PyPI configuration file
.pypirc .pypirc
# Custom
config.json
+19 -15
View File
@@ -6,21 +6,21 @@
## Overview ## Overview
**TactiTerm** is an interactive software engineering tutor designed with a "guidance over answers" philosophy. Rather than providing copy-paste solutions, TactiTerm uses a **Socratic AI Mentor** to ask probing questions, provide hints, and guide users toward mastering concepts across 10 programming languages. **TactiTerm** is an interactive software engineering tutor designed with a "guidance over answers" philosophy. Rather than providing copy-paste solutions, TactiTerm utilizes an **LLM Harness** to ask probing questions, provide hints, and guide users toward mastering concepts across various languages.
TactiTerm features both a rich **Terminal User Interface (TUI)** for command-line productivity and a modern **Web UI** featuring the Monaco Editor. TactiTerm features both a rich **Terminal User Interface (TUI)** for command-line use, and a modern **Web UI** featuring the Monaco Editor.
--- ---
## Features ## Features
- **Socratic AI Mentor:** Provides step-by-step guidance, code review, and conceptual hints without spoiling direct answers. - **AI Mentor:** Provides step-by-step guidance, code review, and conceptual hints without spoiling direct answers.
- **Dual Interfaces:** - **Dual Interfaces:**
- **TUI:** Built with Python and Textual for high-productivity, keyboard-driven terminal workflows. - **TUI:** Built with Python and Textual for lightweight, keyboard-driven terminal workflows.
- **Web UI:** Built with React, Vite, TypeScript, Tailwind CSS, and Monaco Editor. - **Web UI:** Built with React, Vite, TypeScript, Tailwind CSS, and Monaco Editor.
- **10 Supported Languages:** Python, C#, C++, Java, JavaScript, TypeScript, Rust, Lua, HTML, and Go. - **10 Supported Languages:** Python, C#, C++, Java, JavaScript, TypeScript, Rust, Lua, HTML, and Go.
- **Real-time Linting & Execution:** Tree-sitter powered syntax validation and safe local code execution. - **Real-time Linting & Execution:** Tree-sitter powered syntax validation and safe local code execution.
- **Interactive Syntax Handbook:** W3Schools-style reference catalog with on-demand AI code examples for functions and core language topics. - **Interactive Syntax Handbook:** Comprehensive reference catalog with on-demand AI code examples for functions and core language topics.
- **AI Challenge Generator (GenTUI):** Create and save custom Markdown programming challenges tailored to specific subjects and difficulties. - **AI Challenge Generator (GenTUI):** Create and save custom Markdown programming challenges tailored to specific subjects and difficulties.
- **Configurable & Network Ready:** Easily configure LLM endpoints, ports, and enable `"public": true` to host the Web UI across your local network (`0.0.0.0`). - **Configurable & Network Ready:** Easily configure LLM endpoints, ports, and enable `"public": true` to host the Web UI across your local network (`0.0.0.0`).
@@ -30,10 +30,9 @@ TactiTerm features both a rich **Terminal User Interface (TUI)** for command-lin
### Prerequisites ### Prerequisites
- Python 3.10+ - [Devbox](https://www.jetify.com/devbox)
- [`uv`](https://github.com/astral-sh/uv) (Python package runner)
- Node.js & npm (for Web UI) > Currently we only officially support running this through devbox to simplify deployment, though if you have the prequisites installed, you should be able to run TactiTerm without it
- [Devbox](https://www.jetify.com/devbox) (optional, for reproducible envs)
### Installation ### Installation
@@ -45,30 +44,35 @@ TactiTerm features both a rich **Terminal User Interface (TUI)** for command-lin
2. Install dependencies: 2. Install dependencies:
```bash ```bash
make install devbox run install
``` ```
*(or `make install` inside `devbox shell`)*
--- ---
## 🎮 Usage ## Usage
TactiTerm is executed via the use of **Devbox** run commands.
### Terminal Interface (TUI) ### Terminal Interface (TUI)
Launch the interactive terminal application: Launch the interactive terminal application:
```bash ```bash
make tui devbox run tui
``` ```
*(or `make tui` inside `devbox shell`)*
Additional TUI modes: Additional TUI modes:
- `make gentui` - Launch the standalone GenTUI AI Challenge Generator. - `devbox run gentui` - Launch the standalone GenTUI AI Challenge Generator.
- `make tui-debug` - Launch TUI in debug logging mode. - `devbox run -- make tui-debug` - Launch TUI in debug logging mode.
### Web Interface (Web UI) ### Web Interface (Web UI)
Launch the FastAPI backend server and Vite frontend dev server: Launch the FastAPI backend server and Vite frontend dev server:
```bash ```bash
make web devbox run web
``` ```
*(or `make web` inside `devbox shell`)*
Then open your browser to `http://localhost:5173`. Then open your browser to `http://localhost:5173`.
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"llm": { "llm": {
"base_url": "http://100.82.205.18:1010/", "base_url": "http://localhost:8080/",
"model": "local-model", "model": "local-model",
"api_key": "not-needed", "api_key": "not-needed",
"temperature": 0.9, "temperature": 0.9,
+1
View File
@@ -11,6 +11,7 @@
], ],
"shell": { "shell": {
"scripts": { "scripts": {
"install": "make install",
"tui": "make tui", "tui": "make tui",
"gentui": "make gentui", "gentui": "make gentui",
"web": "make web" "web": "make web"
+7 -7
View File
@@ -68,13 +68,13 @@ const Workspace: React.FC<WorkspaceProps> = ({ challengeId, onBack }) => {
const [userQuestion, setUserQuestion] = useState(''); const [userQuestion, setUserQuestion] = useState('');
const [isLoadingGuidance, setIsLoadingGuidance] = useState(false); const [isLoadingGuidance, setIsLoadingGuidance] = useState(false);
// Handbook W3Schools state // Handbook state
const [handbookCatalog, setHandbookCatalog] = useState<HandbookCatalog>({ functions: [], subjects: [] }); const [handbookCatalog, setHandbookCatalog] = useState<HandbookCatalog>({ functions: [], subjects: [] });
const [handbookTab, setHandbookTab] = useState<'functions' | 'subjects'>('functions'); const [handbookTab, setHandbookTab] = useState<'functions' | 'subjects'>('functions');
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [selectedTopic, setSelectedTopic] = useState<HandbookTopic | null>(null); const [selectedTopic, setSelectedTopic] = useState<HandbookTopic | null>(null);
const [handbookExample, setHandbookExample] = useState<string>( const [handbookExample, setHandbookExample] = useState<string>(
'Select a built-in function or topic above to view W3Schools reference card...' 'Select a built-in function or topic above to view reference card...'
); );
const [isLoadingExample, setIsLoadingExample] = useState(false); const [isLoadingExample, setIsLoadingExample] = useState(false);
@@ -248,7 +248,7 @@ const Workspace: React.FC<WorkspaceProps> = ({ challengeId, onBack }) => {
if (!currentChallenge) return; if (!currentChallenge) return;
setSelectedTopic(topic); setSelectedTopic(topic);
setIsLoadingExample(true); setIsLoadingExample(true);
setHandbookExample(`⏳ **Generating W3Schools reference card for '${topic.title}' in ${currentChallenge.language}...**\n\n*Consulting LLM backend...*`); setHandbookExample(`⏳ **Generating reference card for '${topic.title}' in ${currentChallenge.language}...**\n\n*Consulting LLM backend...*`);
try { try {
const res = await getHandbookExample(currentChallenge.language, topic.id, topic.title); const res = await getHandbookExample(currentChallenge.language, topic.id, topic.title);
@@ -392,7 +392,7 @@ const Workspace: React.FC<WorkspaceProps> = ({ challengeId, onBack }) => {
}`} }`}
> >
<BookMarked size={14} /> <BookMarked size={14} />
<span>W3Schools Handbook</span> <span>Syntax Handbook</span>
</button> </button>
</div> </div>
</header> </header>
@@ -634,7 +634,7 @@ const Workspace: React.FC<WorkspaceProps> = ({ challengeId, onBack }) => {
</div> </div>
)} )}
{/* Right Column Mode 2: W3Schools-Style Handbook Sidebar (Scalable Width) */} {/* Right Column Mode 2: Handbook Sidebar (Scalable Width) */}
{rightSidebarMode === 'handbook' && ( {rightSidebarMode === 'handbook' && (
<div <div
style={{ width: `${rightWidth}px` }} style={{ width: `${rightWidth}px` }}
@@ -642,7 +642,7 @@ const Workspace: React.FC<WorkspaceProps> = ({ challengeId, onBack }) => {
> >
<div className="p-3 border-b border-darkBorder bg-darkBg flex justify-between items-center"> <div className="p-3 border-b border-darkBorder bg-darkBg flex justify-between items-center">
<h2 className="flex items-center gap-2 font-bold text-indigo-400 text-xs uppercase tracking-wider"> <h2 className="flex items-center gap-2 font-bold text-indigo-400 text-xs uppercase tracking-wider">
<BookMarked size={16} /> W3Schools Reference ({currentChallenge?.language || 'Python'}) <BookMarked size={16} /> Syntax Reference ({currentChallenge?.language || 'Python'})
</h2> </h2>
<button <button
onClick={() => setRightSidebarMode('closed')} onClick={() => setRightSidebarMode('closed')}
@@ -716,7 +716,7 @@ const Workspace: React.FC<WorkspaceProps> = ({ challengeId, onBack }) => {
)} )}
</div> </div>
{/* Generated LLM W3Schools Reference Card View */} {/* Generated LLM Reference Card View */}
<div className="flex-1 p-4 overflow-y-auto"> <div className="flex-1 p-4 overflow-y-auto">
<div className="p-4 bg-darkBg border border-darkBorder rounded-xl shadow-inner"> <div className="p-4 bg-darkBg border border-darkBorder rounded-xl shadow-inner">
<MarkdownView content={handbookExample} /> <MarkdownView content={handbookExample} />
+1 -1
View File
@@ -144,7 +144,7 @@ class HandbookExampleRequest(BaseModel):
@app.get("/handbook/catalog/{language}") @app.get("/handbook/catalog/{language}")
def get_handbook_catalog_endpoint(language: str): def get_handbook_catalog_endpoint(language: str):
"""Returns W3Schools-style syntax handbook catalog (functions & subjects) for specified language.""" """Returns syntax handbook catalog (functions & subjects) for specified language."""
catalog = handbook_service.get_catalog(language) catalog = handbook_service.get_catalog(language)
return {"language": language, "catalog": catalog} return {"language": language, "catalog": catalog}
+5 -5
View File
@@ -1,4 +1,4 @@
"""W3Schools-Style Language Reference Handbook service for built-in functions, methods, primitive types, and topics.""" """Language Reference Handbook service for built-in functions, methods, primitive types, and topics."""
import httpx import httpx
from typing import Dict, List, Any from typing import Dict, List, Any
from src.core.config import config from src.core.config import config
@@ -197,7 +197,7 @@ HANDBOOK_CATALOG: Dict[str, Dict[str, List[Dict[str, str]]]] = {
class HandbookService: class HandbookService:
"""W3Schools-Style Language Reference Handbook service.""" """Language Reference Handbook service."""
def get_catalog(self, language: str) -> Dict[str, List[Dict[str, str]]]: def get_catalog(self, language: str) -> Dict[str, List[Dict[str, str]]]:
canonical = registry.canonical_name(language) canonical = registry.canonical_name(language)
@@ -210,7 +210,7 @@ class HandbookService:
lang_name = config_info.get("name", language) if config_info else language lang_name = config_info.get("name", language) if config_info else language
prompt = ( prompt = (
f"Generate a W3Schools-Style Reference Card for '{topic_title}' in {lang_name}.\n" f"Generate a Reference Card for '{topic_title}' in {lang_name}.\n"
f"Structure your response strictly into these four Markdown sections:\n" f"Structure your response strictly into these four Markdown sections:\n"
f"1. **Syntax / Signature:** Clear representation of how to invoke or write it.\n" f"1. **Syntax / Signature:** Clear representation of how to invoke or write it.\n"
f"2. **Description & Parameters:** Brief summary of parameter types and return value.\n" f"2. **Description & Parameters:** Brief summary of parameter types and return value.\n"
@@ -219,7 +219,7 @@ class HandbookService:
) )
sys_prompt = ( sys_prompt = (
"You are an expert W3Schools-Style Documentation Assistant for TactiTerm.\n" "You are an expert Documentation Assistant for TactiTerm.\n"
"Provide clean, educational reference cards. Ensure the code example is realistic, complete, and copy-pasteable." "Provide clean, educational reference cards. Ensure the code example is realistic, complete, and copy-pasteable."
) )
@@ -259,7 +259,7 @@ class HandbookService:
except Exception as e: except Exception as e:
return { return {
"status": "error", "status": "error",
"message": f"Failed to generate W3Schools reference card via LLM: {e}", "message": f"Failed to generate reference card via LLM: {e}",
} }
+6 -6
View File
@@ -1,4 +1,4 @@
"""TactiTerm TUI — Coding Tutor Terminal User Interface with W3Schools-style Handbook & Expandable Output Terminal.""" """TactiTerm TUI — Coding Tutor Terminal User Interface with Handbook & Expandable Output Terminal."""
import argparse import argparse
import logging import logging
from typing import List, Dict, Any from typing import List, Dict, Any
@@ -336,14 +336,14 @@ class TactiTermTUI(App):
yield MentorInputTextArea(id="mentor-question-input") yield MentorInputTextArea(id="mentor-question-input")
yield Button("💡 Ask Mentor (Enter)", id="btn-send-mentor", variant="warning") yield Button("💡 Ask Mentor (Enter)", id="btn-send-mentor", variant="warning")
# Right column 2: W3Schools-Style Handbook Sidebar # Right column 2: Handbook Sidebar
with Container(id="handbook-sidebar"): with Container(id="handbook-sidebar"):
yield Static("📖 W3Schools Handbook", id="handbook-header") yield Static("📖 Handbook", id="handbook-header")
yield Input(placeholder="🔍 Search built-ins or topics...", id="handbook-search-input") yield Input(placeholder="🔍 Search built-ins or topics...", id="handbook-search-input")
yield Button("Filter: All Items (Click to Switch)", id="btn-handbook-mode", variant="default") yield Button("Filter: All Items (Click to Switch)", id="btn-handbook-mode", variant="default")
yield OptionList(id="handbook_topic_list") yield OptionList(id="handbook_topic_list")
with VerticalScroll(id="handbook-example-scroll"): with VerticalScroll(id="handbook-example-scroll"):
yield Markdown("Select a function or topic above to view W3Schools reference card...", id="handbook-example-display") yield Markdown("Select a function or topic above to view reference card...", id="handbook-example-display")
yield Static("Ready", id="status-bar") yield Static("Ready", id="status-bar")
yield Footer() yield Footer()
@@ -524,7 +524,7 @@ class TactiTermTUI(App):
self.load_handbook_catalog(lang) self.load_handbook_catalog(lang)
self.query_one("#handbook_topic_list", OptionList).focus() self.query_one("#handbook_topic_list", OptionList).focus()
self.query_one("#status-bar", Static).update(f"W3Schools Handbook open for {lang}.") self.query_one("#status-bar", Static).update(f"Handbook open for {lang}.")
def on_mentor_input_text_area_submit_question( def on_mentor_input_text_area_submit_question(
self, event: MentorInputTextArea.SubmitQuestion self, event: MentorInputTextArea.SubmitQuestion
@@ -699,7 +699,7 @@ class TactiTermTUI(App):
lang = challenge.language if challenge else "Python" lang = challenge.language if challenge else "Python"
self.query_one("#handbook-example-display", Markdown).update( self.query_one("#handbook-example-display", Markdown).update(
f"⏳ **Generating W3Schools reference card for '{item['title']}' in {lang}...**" f"⏳ **Generating reference card for '{item['title']}' in {lang}...**"
) )
self.run_worker(self._handbook_worker(lang, item["id"], item["title"])) self.run_worker(self._handbook_worker(lang, item["id"], item["title"]))