| Feature | Files | Status |
|---|---|---|
| Multi-turn LLM sessions (Anthropic, OpenAI, Google) | src/provider/, src/session/mod.rs |
✅ |
| File tools: read, write, edit, glob, grep | src/tools/ |
✅ |
| Shell execution (bash tool) with permission prompts | src/tools/bash.rs, src/permission/ |
✅ |
| LSP integration (type structure, status tracking) | src/lsp/mod.rs |
✅ |
| HTTP API server with SSE event streaming | src/server/ |
✅ |
| SQLite storage (sessions, messages, parts, todos) | src/storage/mod.rs |
✅ |
| Session management: fork, compact, revert, abort | src/session/mod.rs, src/server/routes/session.rs |
✅ |
| Snapshot/revert via git stash | src/snapshot/mod.rs |
✅ |
| MCP server (tools over stdio) | src/mcp/mod.rs |
✅ |
| Plugin system ($QUICKCODE_HOME/plugin/) | src/plugin/mod.rs |
✅ |
| Permission rules (allow/deny/ask) | src/permission/mod.rs |
✅ |
| TUI: ratatui terminal UI with chat, welcome, error overlays | src/tui/mod.rs |
✅ |
| Config: JSONC loading, provider API keys, agent overrides | src/config/mod.rs |
✅ |
Basic auth middleware (QUICKCODE_SERVER_PASSWORD) |
src/server/mod.rs |
✅ |
| Subtask agent tool (spawn sub-agents) | src/tools/subtask.rs |
✅ |
| Todo tool | src/tools/todo_tool.rs |
✅ |
| Apply-patch tool | src/tools/apply_patch.rs |
✅ |
| VCS: git diff, status endpoints | src/vcs.rs, src/server/routes/vcs.rs |
✅ |
Goal: Replace bare ENV-var setup with an interactive first-run wizard.
AppMode::Onboarding { step: OnboardingStep }added to TUIOnboardingStepenum:SelectProvider → EnterApiKey → SelectModel → Complete- First-run detection: checks
GET /providerfor any authenticated provider render_onboarding(): provider picker, masked API key input (Tab to reveal), model picker, completion screensave_provider_key(): callsPUT /auth/{provider_id}and writes to$QUICKCODE_HOME/config.jsoncsave_default_model(): callsPATCH /configto update runtime modelPATCH /configendpoint added tosrc/server/routes/config.rs- Files:
src/tui/mod.rs,src/server/routes/config.rs,src/server/mod.rs
Goal: Each agent can have its own provider_id/model_id, switchable at runtime.
AgentRegistry.agentschanged fromHashMaptoArc<RwLock<HashMap<String, AgentInfo>>>- All read methods (
get,list,list_primary,default_agent) are nowasync set_model(agent_name, model)andset_temperature(agent_name, temp)mutation methods addedPATCH /agent/:nameendpoint withUpdateAgentBody { model, temperature, top_p }- TUI
AgentModelPickermode: pressmon agent in agent list to switch model - All call sites in
session/mod.rsandserver/routes/agent.rsupdated to.await - Files:
src/agents/mod.rs,src/session/mod.rs,src/server/routes/agent.rs,src/server/mod.rs
Goal: Multiple sessions run concurrently; TUI shows all active sessions.
AgentPanestruct:session_id,agent_name,title,messages,thinking,spinner_frame,input,cursor_pos,scroll,max_scroll,token_summary,error,pasted_blocksTuiAppusespanes: Vec<AgentPane>+active_pane: usizereplacing single-session fieldsspawn_agent_session(agent_name)creates a new session and appends a new paneCtrl+Nkey binding to spawn additional agent sessions- Bus event routing updated: all handlers dispatch to correct pane by
session_id - Files:
src/tui/mod.rs
Goal: List all agents, switch between sessions, see token usage in header.
AppMode::AgentList: table of agents with name/mode/model/active columns; F2 to openAppMode::SessionSwitcher: table of active panes with agent/title/status/tokens; F3/Ctrl+W to openAppMode::AgentModelPicker: model picker for an agent, opened withmfrom agent listAppMode::TaskList: F4 opens SPEC task list- Chat header updated:
[pane/total agent_name] title Ntokensformat - Status bar updated with F2/F3/F4 hints
fetch_agents()helper callsGET /agent- Files:
src/tui/mod.rs
Goal: Paste code without breaking TUI; large pastes become placeholders.
EnableBracketedPasteadded before event loop;DisableBracketedPasteon cleanupEvent::Paste(text)handler: pastes ≤3 lines / ≤200 chars go inline; larger become[Pasted Text #N, M lines]PastedBlock { index, content, line_count }stored inpane.pasted_blocksexpand_pasted_blocks()replaces placeholders with real content before sending- Placeholder text rendered in dim/italic style in input box
- Files:
src/tui/mod.rs
Goal: Track tokens used per session per agent; display live counter.
- 4 new columns on
sessiontable:total_input_tokens,total_output_tokens,total_cost_usd,token_by_agent TEXT - Migration guards in
init_db()(ALTER TABLE ADD COLUMN) for existing databases session_add_tokens(db, session_id, input, output, cost, agent)— atomic+=SQL updatesession_get_token_summary(db, session_id)— returnsTokenSummarywith per-agent breakdownTokenSummaryandAgentTokenUsagestructs added tosrc/models/mod.rs- Token accumulation wired into
StreamEvent::FinishStephandler insession/mod.rs GET /session/:id/statsendpoint returnsTokenSummary- Live counter displayed in TUI chat header via
pane.token_summary - Files:
src/models/mod.rs,src/storage/mod.rs,src/session/mod.rs,src/server/routes/session.rs,src/server/mod.rs,src/tui/mod.rs
Goal: List all remaining unimplemented spec items as queryable tasks.
src/server/routes/tasks.rs:SpecTaskstruct + 14 embedded backlog itemsGET /tasksendpoint returns the full list- TUI
TaskListmode (F4): 3-column table (section, title, priority) with cursor highlight SPEC_TASKS_DISPLAYstatic array embedded in TUI for offline viewing- Files:
src/server/routes/tasks.rs,src/server/routes/mod.rs,src/server/mod.rs,src/tui/mod.rs
pulldown-cmark = "0.10"added to Cargo.tomlsrc/tui/markdown.rs:markdown_to_lines()converts markdown to styled ratatuiLineobjects- Handles: headings (cyan+bold), bold, italic, inline code (yellow), fenced code blocks (yellow), bullet lists, paragraphs, horizontal rules
- 4 unit tests (plain text, bold, code block, empty input)
src/tui/mod.rs:mod markdown;added;Role::Assistantrendering callsmarkdown_to_lines()instead of plain line split- Files:
Cargo.toml,src/tui/markdown.rs,src/tui/mod.rs
src/provider/copilot.rs: FullLlmProviderimplementation using Copilot API- Token auto-detection:
GITHUB_TOKENenv →~/.config/github-copilot/hosts.json→gh auth token - Short-lived API token cache with auto-refresh (60s buffer before expiry)
- OpenAI-compatible API at
https://api.githubcopilot.com/chat/completions - Models:
gpt-4o,gpt-4o-mini,claude-3.5-sonnet
- Token auto-detection:
src/provider/mod.rs:CopilotProvider::detect()called inProviderRegistry::new(),pub mod copilot;added- Files:
src/provider/copilot.rs,src/provider/mod.rs
src/provider/mock.rs:MockProviderimplementingLlmProvider— returns pre-configured responses without networknew(responses: Vec<String>)andwith_response(text)constructors- Stream emits
TextDeltathenFinishStepwith 10/5 token usage
src/provider/mod.rs:new_empty()andnew_with_providers(Vec<Box<dyn LlmProvider>>)addedsrc/session/mod.rs:#[cfg(test)]module withtest_fork_sessionintegration test- Files:
src/provider/mock.rs,src/provider/mod.rs,src/session/mod.rs
- 14 new tests added to
src/server/mod.rs#[cfg(test)]block:- Session: delete, update title, list messages (empty), get stats
- Agents: list (200 + array), PATCH model
- Config: PATCH model
- Misc: list permissions, list tasks (14 items), get path, list questions, list skills, list commands, get VCS
- Files:
src/server/mod.rs
src/mcp.rs:McpServerextended with 6 optional context fields (db,bus,permission,questions,providers,agents)with_session_support()constructor wires all context fieldsbuild_tool_context()helper builds ephemeralToolContextfor MCP callstools/callhandler now callstool.execute(args, &ctx).awaitinstead of returning stub
src/main.rs:McpServer::new()replaced withMcpServer::with_session_support()- Files:
src/mcp.rs,src/main.rs
Rulestruct gains#[serde(default)] pub always: bool;ActionderivesPartialEqevaluate_rulesupdated with 2-pass logic: always-allow short-circuits first, then last-match-wins- All
Rule { }literals in tools, config, storage updated withalways: false - 2 new tests:
test_always_allow_bypasses_deny,test_always_deny_does_not_short_circuit - Files:
src/models/mod.rs,src/permission/mod.rs,src/tools/mod.rs,src/tools/subtask.rs,src/config/mod.rs,src/storage/mod.rs
src/provider/gitlab.rs:GitLabProviderusing OpenAI-compatible GitLab AI gateway- Auto-detects
GITLAB_TOKENorCI_JOB_TOKEN; configurable viaGITLAB_AI_BASE_URL - Models: Claude 3.5 Sonnet, Claude 3 Haiku, Mistral 7B Instruct
- Reuses OpenAI SSE parser
- Auto-detects
src/provider/mod.rs: auto-detection + registration inProviderRegistry::new()- Files:
src/provider/gitlab.rs,src/provider/mod.rs
hmac = "0.12",async-stream = "0.3",url = "2"added to Cargo.tomlsign_request_v4(): full AWS SigV4 signing with HMAC-SHA256 (no AWS SDK dependency)parse_bedrock_event_stream(): binary AWS EventStream frame parser yieldsTextDelta+FinishStepBedrockProvider::stream()replaced — callsconverse-streamendpoint with signed requests- Files:
Cargo.toml,src/provider/bedrock.rs
src/server/routes/ws.rs:ws_eventshandler usingWebSocketUpgrade; relays all bus events as JSON text frames; handles client disconnect gracefullyGET /wsroute added alongsideGET /event(SSE)- Files:
src/server/routes/ws.rs,src/server/routes/mod.rs,src/server/mod.rs
notify = "6"added to Cargo.tomlBusEvent::SkillsReloadedadded to bus event enum- Background watcher task in
main.rs: watches$QUICKCODE_HOME/skill/with 500ms debounce, publishesSkillsReloadedon changes is_session_eventinsession/mod.rsandhandle_tui_eventintui/mod.rsupdated for exhaustive match- Files:
Cargo.toml,src/bus.rs,src/main.rs,src/session/mod.rs,src/tui/mod.rs
- In
OpenAIProvider::stream(): detectsprovider_id.contains("litellm")orLITELLM_PROXYenv var - Injects a
_noopToolDefinitionwhen tools list is empty and LiteLLM is detected - Files:
src/provider/openai.rs
fuzzy-matcher = "0.3"added to Cargo.tomlAppMode::FilePicker { query, all_files, results, cursor }added to TUIscan_files_sync(root): walks directory withignorecrate (respects .gitignore), caps at 10k entriesfilter_files(all, query):SkimMatcherV2fuzzy scoring, returns top 50 matchesCtrl+Pkey binding in Chat mode opens the picker- Full key handling: Up/Down navigate, Enter inserts path into input, Esc cancels, typing re-filters
render_file_picker(): centered popup overlay (80%×60%) with query input box + highlighted file list- Files:
Cargo.toml,src/tui/mod.rs
syntect = { version = "5", features = ["default-syntaxes", "default-themes", "parsing", "regex-fancy"] }added to Cargo.tomlsrc/tui/highlight.rs:highlight_code(code, lang)using syntectHighlightLineswithbase16-ocean.darktheme; converts syntectColor→ratatui Color::Rgb; falls back to plain yellow on errorsrc/tui/markdown.rs: code blocks now buffer text intocode_buffer, capture fenced language tag, callhighlight_code()atEnd(CodeBlock)instead of rendering plain yellow spansmod highlight;added tosrc/tui/mod.rs- Files:
Cargo.toml,src/tui/highlight.rs,src/tui/markdown.rs,src/tui/mod.rs
Spec Appendix B: Bash tool output must be truncated at 30 KB.
MAX_OUTPUT_BYTES = 30 * 1024 and truncate_combined() were already in place — verified correct.
Files: src/tools/bash.rs
Spec Appendix B: Max 100 glob results, max 100 grep matches.
Changed MAX_RESULTS / MAX_MATCHES from 1000 → 100. Added cap integration tests.
Files: src/tools/glob_tool.rs, src/tools/grep_tool.rs
Spec §9.3: Updated basic_auth_middleware to read QUICKCODE_SERVER_USERNAME
(default "quickcode") and validate both username and password.
Files: src/server/mod.rs
Spec §9.1 item 3: Added ~/.quickcode/quickcode.jsonc load step in load_config().
Files: src/config/mod.rs
Spec Appendix B: Added Content-Length pre-check and streaming byte cap before body decode.
Files: src/tools/web_fetch.rs
Spec §9.3: Renamed OPENCODE_* → QUICKCODE_* for all three models-cache env vars.
Files: src/provider/models_cache.rs
Spec §3.8 / §6.7: Added step_snapshot_id tracking; calls Snapshot::patch() at
FinishStep and writes a PatchPart to DB + bus.
Files: src/session/mod.rs
Spec §7.10: Extended LspClient reader to handle textDocument/publishDiagnostics
notifications. Added notify_open(), get_stored_diagnostics(), and
get_file_diagnostics(). Added lsp: Option<Arc<LspManager>> to ToolContext.
Replaced the 100 ms stub in write.rs; added equivalent call in edit.rs.
Files: src/lsp/mod.rs, src/tools/mod.rs, src/tools/write.rs, src/tools/edit.rs,
src/session/mod.rs, src/mcp.rs
Spec §5.1: share_session generates https://share.quickcode.dev/s/<id>, persists
it via session_update, and returns it idempotently.
Files: src/server/routes/session.rs
Spec §7.7: Added permission: Option<Vec<Rule>> to AgentOverride. Session processing
now extends permission_rules with agent-specific rules from config.
Files: src/config/mod.rs, src/session/mod.rs
Spec §7.1 / §11.8: resolve_model_variant() (already existed) is now called after
model info lookup to use the best variant's model ID.
Files: src/session/mod.rs
Spec §8.1: Replaced tokio::process::Command with portable_pty. Spawns bash -c
inside a PTY slave; reads merged stdout+stderr from master until exit or timeout.
Files: src/tools/bash.rs
Spec §5.1: Added resolve_workspace() helper; list_sessions filters by workspace;
create_session accepts workspace in body/header/query and sets session.workspace_id.
Files: src/server/routes/session.rs
Added to src/tools/mod.rs exec_tests:
test_write_tool_on_disk_verify— confirms file exists on disk after writetest_edit_tool_basic_replace— writes then edits, verifies on-disk contenttest_bash_tool_permission_denied— deny rule blocks bash executiontest_glob_result_cap— glob never returns more than 100 resultstest_grep_result_cap— grep never returns more than 100 matches Doom-loop tests already existed insrc/permission/mod.rs. Files:src/tools/mod.rs
All PLAN.md gaps implemented. 84 tests passing.
The only intentionally deferred item is spec-8.1-tree-sitter (tree-sitter AST parsing)
which requires per-language grammar bindings and is out of scope.
- 84 unit + integration tests passing (
cargo test) - Full server route coverage: all major endpoints tested with correct status codes
- Markdown renderer unit tests (including updated syntect code block test)
- Session lifecycle tests (fork, create) using MockProvider
- Permission always-allow rules: 2 dedicated tests
- Tool execution: bash, write on-disk, edit, glob, grep (gap-14)
- Permission deny rule enforcement (gap-14)
- Result cap enforcement: glob ≤100, grep ≤100 (gap-14)
- New
memorytable in SQLite schema (idempotent,id TEXT PRIMARY KEYwith deterministic sha2-derived key) - Storage CRUD:
memory_set,memory_get,memory_delete,memory_delete_by_session,memory_list - Four new tools registered in
ToolRegistry:memory_write— upsert by(scope, project_id, session_id, key)memory_read— retrieve by coordinatesmemory_delete— remove a single entrymemory_list— list all accessible entries, optionally filtered by scope and wildcard pattern
- Three scopes:
session— short-term, lives within a single session (project_id + session_id)project— long-term, shared across all sessions of a projectglobal— long-term, not tied to any project
POST /project/initroute added: scans project files (README, manifests, etc.), calls LLM to generateQUICKCODE.md, writes it to the worktree root, setsproject.time_initialized- Falls back to a static template if no LLM provider is authenticated
QUICKCODE.mdcontent is automatically injected into every session's system prompt (step 4a inSessionProcessor::process)- Persisted project-scope memory entries are also injected into the system prompt (step 4b)
ToolContextgainedproject_id: ProjectIdfield; all construction sites updated (session/mod.rs,mcp.rs, test helpers)session::default_modelmadepub(used by the init route)storage::SCHEMAmadepub(used by memory test helper)- 6 new integration tests in
tools::memory_tests(write/read session, project, global scope; delete; list+pattern; invalid scope) - Test count: 84 → 90