FocusQuest Extension (Prototype Evolving to MVP)

Demo GIF generated locally (no external services).
Changelog · This is a preview build (0.1.x). See CHANGELOG for recent changes.
This extension/
folder now contains the evolving TypeScript implementation of FocusQuest: gamified task breakdown, XP + levels, badges, streaks, Pomodoro sessions, hierarchical epics, and optional local AI task generation via Ollama.
Privacy & Local-First Guarantee: FocusQuest makes no outbound network or telemetry calls. The only HTTP requests performed are to a user‑controlled local Ollama endpoint (default http://localhost:11434
). If unreachable, a safe fallback task list is generated locally.
Run Locally (Dev)
- Open the repository in VS Code.
- Open this
extension
folder (or use a multi-root workspace).
- Install dependencies:
npm install
.
- Press F5 (Run Extension) to launch the Extension Development Host.
- Use the Command Palette (Ctrl+Shift+P) and try commands under the “FocusQuest:” prefix.
Build / test quick reference:
npm run build # compile to dist/
npm test # run mocha tests (TS + legacy JS test)
Key Features (Current Prototype)
Area |
Capabilities |
Tasks / Quests |
Add, complete, hierarchical epics with progress auto-complete, AI-labeled subtasks |
AI Breakdown |
Local LLM (Ollama) generates 3–7 focused subtasks (fallback if unreachable) |
Gamification |
XP + level curve, multiple badges (first quest, focus sessions, streak tiers, AI usage, Pomodoro master, distraction slayer) |
Streaks |
Daily focus session streak tracking with longest streak memory & new 5‑day badge |
Pomodoro |
Start focus / break sessions, pause/resume, auto-switch focus→break, persistent restart after window reload |
Dashboard |
Webview with live incremental updates (XP, streak, sessions, tasks, badges, last AI suggestion) |
Status Bar |
XP summary, truncated last AI suggestion, live Pomodoro remaining time (updates persist across reload) |
Persistence |
Unified immutable state store via VS Code globalState (Pomodoro session restored if still active) |
Level formula: level = floor( (xp / 100) ** 0.8 )
(subject to tuning).
Commands
Note: The authoritative list of contributed commands lives in package.json
. This table highlights the most common user-facing commands.
| Command | Purpose |
|---------|---------|
| FocusQuest: Add Task | Create a new quest/task |
| FocusQuest: Complete Quest | Mark a task done (awards XP) |
| FocusQuest: Start Focus Session | Begin a focus Pomodoro (25m default) |
| FocusQuest: Start Break | Begin break interval (5m default) |
| FocusQuest: Open Dashboard | Show live progress dashboard |
| FocusQuest: Break Down Task | Create a new epic + AI subtasks |
| FocusQuest: Break Down This Task | Context-menu breakdown for existing task (adds children) |
| FocusQuest: Check Ollama Status | Verifies local Ollama service & model availability |
| FocusQuest: Convert Task to Epic | Turns a normal task into an epic so you can add subtasks (manual or AI) |
| FocusQuest: Rename Task | Rename an existing quest/epic |
| FocusQuest: Delete Task | Delete a task (and cascades its subtasks) |
| FocusQuest: Pause Pomodoro | Pause the active focus/break timer (retains remaining time) |
| FocusQuest: Resume Pomodoro | Resume a paused timer |
| FocusQuest: Show Stats | Display tasks completed, focus sessions, streak info |
Context Menu: In the quest tree, right-click (or use inline menu) to break down an existing task when pending.
Local LLM (Ollama) Setup
FocusQuest performs all AI breakdowns locally. Install Ollama and pull a model (default: llama3
). See ../docs/ollama-setup.md
for detailed steps (Windows focused + cross-platform notes).
Quick start:
ollama pull llama3
ollama run llama3 "ping"
Then in VS Code run: FocusQuest: Check Ollama Status
.
If Ollama is not reachable, breakdown commands still return a fallback list so you are never blocked.
Architecture Overview
Primary modules live under src/
:
stateStore.ts
– unified immutable persisted app state
questManager.ts
– task CRUD and ordering
questTreeProvider.ts
– hierarchical tree with epic progress labels
rewards.ts
/ badges.ts
– XP curve & badge evaluation
streak.ts
– date-based streak advancement
pomodoroTimer.ts
– session events foundation
llm/LLMClient.ts
– local Ollama integration + fallback
webview/DashboardPanel.ts
– incremental UI updates via postMessage
Tests
Run all tests:
npm test
Current coverage includes rewards, badges (including AI & streak tiers), streak logic, state store immutability, hierarchy/epics, and AI badge logic.
Settings
You can customize AI usage and Pomodoro durations via VS Code Settings (File > Preferences > Settings) and searching for "FocusQuest":
Setting |
Description |
Default |
focusquest.ai.enabled |
Toggle all AI breakdown features |
true |
focusquest.ai.model |
Ollama model name used for breakdowns |
llama3 |
focusquest.ai.timeoutMs |
Legacy overall AI operation timeout (non-stream path) |
10000 |
focusquest.ai.requestTimeoutMs |
Overall HTTP / idle stream timeout (resets per chunk) |
20000 |
focusquest.ai.baseUrl |
Base URL for local Ollama server |
http://localhost:11434 |
focusquest.ai.streamingEnabled |
Stream incremental breakdown chunks |
true |
focusquest.ai.streamChunkSize |
Max characters per streamed chunk |
200 |
focusquest.ai.feedMaxChars |
Max characters stored per AI feed line |
240 |
focusquest.ai.zeroTaskAutoRetry |
Silent retry when zero subtasks parsed |
true |
focusquest.ai.collapseWhitespace |
Normalize whitespace in generated subtasks |
true |
focusquest.experimentalBadges |
Enable extra (non-MVP) badge logic |
false |
focusquest.motivation.enabled |
Enable idle/celebration motivational prompts |
true |
focusquest.motivation.idleMinutes |
Minutes idle before nudge |
90 |
focusquest.pomodoro.focusMinutes |
Focus session length (minutes) |
25 |
focusquest.pomodoro.breakMinutes |
Break length (minutes) |
5 |
Changing a setting hot-reloads the client & timer (you'll see a toast: "FocusQuest settings reloaded"). If AI is disabled, breakdown commands show a warning and abort.
AI Timeouts Explained
There are two relevant timeout layers now:
focusquest.ai.requestTimeoutMs
– Low-level HTTP/socket timeout. If the Ollama endpoint does not respond or stalls establishing a stream, the request aborts at this boundary (triggers a single automatic retry unless you cancelled).
- Streaming idle timeout (internal) – Instead of a hard wall clock, streaming breakdowns now reset an idle timer every time a chunk arrives. Only if no chunk arrives for the configured idle window (derived from
ai.timeoutMs
) will it abort. This prevents large model responses from being cut off prematurely.
If both the initial attempt and retry fail (non-abort), FocusQuest falls back to a generic 4‑step task list so you're never blocked.
Zero-Task UX & Metrics
If the model returns zero parseable subtasks (not a fallback), the feed line is annotated with (no subtasks parsed)
and an informational toast offers a one-click "Regenerate with more detail" action that re-queues the breakdown with an appended guidance hint. These occurrences are counted in the ai.emptyBreakdowns
metric (viewable via FocusQuest: Show AI Health
). When focusquest.ai.zeroTaskAutoRetry
is true, a silent second attempt is made first (with a light instruction suffix) before surfacing the UX prompt.
Roadmap (Short Term Ideas)
- Drag-and-drop task reordering
- Additional badge images & tooltips
- Advanced epic analytics (velocity, completion pacing)
- In-editor inline progress annotations
Contributing
See root CONTRIBUTING.md
. Please open an issue before large changes. All contributions should keep AI calls local.
This is an iterative prototype: expect rapid refactors. Feedback & issue reports welcome.
Implementation Notes (Recent Enhancements)
- Debounced tree refresh batching (50ms) minimizes UI churn when AI generates many subtasks at once.
- Duplicate AI subtask prevention: repeated breakdown of the same task ignores case/whitespace duplicates.
- Configurable whitespace normalization: set
focusquest.ai.collapseWhitespace
to false to preserve original spacing (may allow near-duplicates intentionally).
- Duplicate AI subtask prevention with telemetry: skipped duplicates counted in
ai.duplicatesSkipped
(potential future "Refined Planner" badge hook).
- Zero-yield feedback: if no unique subtasks are produced or the model yields zero tasks, feed marks
(no subtasks parsed)
and you get a rephrase prompt; counted in emptyBreakdowns
.
- New recovery badge (experimental):
first_non_empty_after_empty
awarded when you successfully generate a non-empty breakdown after at least one empty attempt.
- QuestManager now uses immutable updates and throws a descriptive error if a frozen task sneaks into a mutation path (dev safety guard).
- Pomodoro persistence: active session (including paused state) survives reload; status bar + dashboard stay in sync.
Events (Developer Reference)
FocusQuest uses a lightweight internal event bus to decouple controllers and UI surfaces. Common events:
Event |
Payload (simplified) |
Producer |
Notes |
|
|
taskUpdated |
{ taskId } |
QuestManager |
Any CRUD affecting a task/epic. |
|
|
rewardsUpdated |
{ xp, level } |
Rewards |
XP / level change triggers status UI refresh. |
|
|
badgesUpdated |
{ newBadges[] } |
Badges |
Fired only when new badges unlocked. |
|
|
pomodoroStateChanged |
{ state } |
PomodoroTimer |
'focus' |
'break' |
'idle' transitions. |
pomodoroTick |
{ remainingMs } |
PomodoroTimer |
Approx 1s cadence for timer display. |
|
|
pomodoroPaused / pomodoroResumed |
{...} |
PomodoroTimer |
Pause/resume lifecycle. |
|
|
streakChanged |
{ currentDays, longest } |
Streak logic |
Streak progress for badges/UI. |
|
|
aiBreakdownStarted |
{ title, targetId, streaming } |
AIController |
NEW: deterministic hook emitted synchronously when a breakdown run begins (enables reliable cancellation tests + potential UI spinners). |
|
|
aiBreakdownChunk |
{ parentTitle, chunk, isFinal? } |
AIController |
Streamed textual fragments; isFinal marks summary/terminal chunk. |
|
|
aiSuggestionReady |
{ message, type } |
AIController |
Final AI summary (used for status bar + feed). |
|
|
aiQueueDepthChanged |
{ depth } |
AIController |
Queue size after enqueue / completion. |
|
|
storageFlushed |
{ timestamp } |
StateStore |
Emitted after atomic write for future metrics. |
|
|
If you introduce a new event: (1) add it to types.ts
, (2) document it here & in docs/architecture-modules.md
, (3) add a focused test if it influences logic.
npm install
License
MIT – see repository root LICENSE
.
Demo GIF Generation
If images/demo.gif
did not load above, generate it locally:
npm run generate:gif
By default it searches ../docs/videos/
for FocusQuest_Sept_25.mp4
(or FocusQuest.mp4
). To specify a file:
npm run generate:gif -- ../docs/videos/YourRecording.mp4
The script creates a palette for higher quality with fewer colors, outputs to extension/images/demo.gif
(included in the published package).