Clean up readme, and default config
This commit is contained in:
@@ -174,3 +174,6 @@ cython_debug/
|
|||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
|
||||||
|
# Custom
|
||||||
|
config.json
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
@@ -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}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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"]))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user