Skip to content

feat: v0.4.0 — add streaming, explain, project scaffolding, session persistence, CI/CD#1

Merged
Alvaro5 merged 2 commits into
mainfrom
claude/review-and-enhance-project-6jwFO
Apr 6, 2026
Merged

feat: v0.4.0 — add streaming, explain, project scaffolding, session persistence, CI/CD#1
Alvaro5 merged 2 commits into
mainfrom
claude/review-and-enhance-project-6jwFO

Conversation

@Alvaro5

@Alvaro5 Alvaro5 commented Apr 5, 2026

Copy link
Copy Markdown
Owner
  • Add CLAUDE.md with build/test/lint commands and architecture overview
  • Create GitHub Actions CI/CD pipeline (fmt, clippy, test, build)
  • Fix .gitignore to allow .github/ directory
  • Fix broken doc links in README (7 non-existent files removed)
  • Mark RAG as complete in FEATURES.md

New features:

  • Streaming LLM responses: token-by-token display in REPL with fallback
  • Code explanation mode: /explain command + /api/explain dashboard endpoint
  • Multi-file project generation: /project command with JSON blueprint parsing,
    path validation, and scaffolding to generated/<project_name>/
  • Conversation persistence: /session save/load/list commands with JSON serialization
  • Dashboard endpoints for all new features

Also includes: version bump to 0.4.0, new ProjectBlueprint struct with
6 unit tests, updated help text, and updated command tab-completion.

https://claude.ai/code/session_01LJJePD8fgBfCKTt8HmpHFp

…ersistence, CI/CD

- Add CLAUDE.md with build/test/lint commands and architecture overview
- Create GitHub Actions CI/CD pipeline (fmt, clippy, test, build)
- Fix .gitignore to allow .github/ directory
- Fix broken doc links in README (7 non-existent files removed)
- Mark RAG as complete in FEATURES.md

New features:
- Streaming LLM responses: token-by-token display in REPL with fallback
- Code explanation mode: /explain command + /api/explain dashboard endpoint
- Multi-file project generation: /project command with JSON blueprint parsing,
  path validation, and scaffolding to generated/<project_name>/
- Conversation persistence: /session save/load/list commands with JSON serialization
- Dashboard endpoints for all new features

Also includes: version bump to 0.4.0, new ProjectBlueprint struct with
6 unit tests, updated help text, and updated command tab-completion.

https://claude.ai/code/session_01LJJePD8fgBfCKTt8HmpHFp
Copilot AI review requested due to automatic review settings April 5, 2026 22:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR bumps the project to v0.4.0 and adds several end-user features (streaming output, code explanation, multi-file project scaffolding, and session persistence) across both the REPL and the dashboard, along with CI/CD and documentation updates.

Changes:

  • Add streaming-capable LLM generation path, plus /explain, /project, and /session commands in the REPL and corresponding dashboard endpoints.
  • Introduce a ProjectBlueprint JSON schema parser + scaffolder for multi-file project generation.
  • Add GitHub Actions CI pipeline and update docs/versioning for the v0.4.0 release.

Reviewed changes

Copilot reviewed 17 out of 20 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
tests/integration_tests.rs Formatting/whitespace normalization in integration tests.
src/utils.rs Adds ProjectBlueprint/ProjectFile structs, JSON extraction/parsing, and basic path validation + unit tests.
src/rag.rs Formatting tweaks in RAG embedding/retrieval.
src/python_exec.rs Adds scaffold_project helper; extensive formatting-only changes elsewhere.
src/logger.rs Formatting updates to session metrics display + test whitespace cleanup.
src/lib.rs Reorders module exports.
src/interface.rs Adds commands: /explain, /project, /session, and REPL-side “streaming” generation path; lots of formatting.
src/dashboard/templates.rs Import ordering cleanup.
src/dashboard/state.rs Adds use_streaming to runtime settings; enum formatting cleanup.
src/dashboard/server.rs Registers new dashboard endpoints for explain/project/session persistence.
src/dashboard/routes.rs Adds explain/project/session persistence endpoints and various formatting refactors.
src/config.rs Adds use_streaming + sessions_dir config fields and defaults.
src/api.rs Adds explanation + project generation prompts/APIs; adds streaming response parsing helper.
README.md Updates version/features list and documents new v0.4.0 functionality.
FEATURES.md Marks several items (RAG/CI/explain/session/streaming/project) as DONE and updates descriptions.
CLAUDE.md Adds developer guide with build/test/lint commands and architecture overview.
Cargo.toml Version bump to 0.4.0.
Cargo.lock Lockfile version update.
.gitignore Stops ignoring .github/ so workflows can be committed.
.github/workflows/ci.yml Adds CI jobs for fmt, clippy, test, and release build.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/interface.rs
Comment on lines +1034 to +1036
let filename = format!("{}.json", name.replace(' ', "_"));
let filepath = sessions_dir.join(&filename);
match fs::write(

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Session filenames are derived directly from the user-provided name and joined onto sessions_dir. As written, names containing path separators, .., or absolute paths (e.g. /tmp/x) can escape sessions_dir and overwrite arbitrary files. Sanitize/validate the name (e.g., allow only [A-Za-z0-9_-]) and reject any name that produces an absolute path or contains parent-dir components.

Copilot uses AI. Check for mistakes.
Comment thread src/api.rs
Comment on lines +572 to +580
// Read the full body and parse SSE events
let text_body = resp
.text()
.await
.context("Failed to read streaming response")?;
let mut chunks = Vec::new();
let mut full_content = String::new();

for line in text_body.lines() {

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generate_code_stream calls resp.text().await and then iterates over text_body.lines(). This buffers the entire response before returning any chunks, so the caller cannot display tokens in real time (and memory usage scales with full output). To make streaming actually work, parse the SSE incrementally from resp.bytes_stream() (or similar) and surface chunks as they arrive (e.g., via a callback/channel/stream), rather than returning only after full body read.

Copilot uses AI. Check for mistakes.
Comment thread src/utils.rs
Comment on lines +434 to +442
for file in &bp.files {
if file.path.contains("..") {
return Err(anyhow!(
"Unsafe path detected: '{}' contains '..'",
file.path
));
}
if file.path.starts_with('/') {
return Err(anyhow!(

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path validation for project files is too weak and also platform-specific: contains("..") can false-positive on benign names and still misses path traversal via components, and starts_with('/') doesn’t catch Windows absolute paths (e.g. C:\... or \\server\share). Consider validating with std::path::Path components (reject Component::ParentDir / Component::RootDir / prefixes) and also validate project_name (it is used as a directory name but currently only checked for empty).

Copilot uses AI. Check for mistakes.
Comment thread src/python_exec.rs
Comment on lines +1298 to +1305
for file in &blueprint.files {
let file_path = project_dir.join(&file.path);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Failed to create directory {:?}", parent))?;
}
fs::write(&file_path, &file.content)
.with_context(|| format!("Failed to write file {:?}", file_path))?;

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scaffold_project writes each file.path by joining onto project_dir without any additional containment check. If a ProjectBlueprint reaches here without strong validation (or validation misses a case like Windows absolute paths), this can write outside the intended directory. Add a defensive check here too (e.g., reject absolute paths/prefixes and ensure the joined path stays within project_dir after normalization/canonicalization).

Copilot uses AI. Check for mistakes.
Comment thread src/dashboard/routes.rs
Comment on lines +1607 to +1608
"messages": session.messages,
"last_generated_code": session.last_generated_code,

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint builds the JSON payload by moving session.messages / session.last_generated_code out of a borrowed &ChatSession from the HashMap, which won’t compile. Clone the fields (or serialize from references) when building the JSON value.

Suggested change
"messages": session.messages,
"last_generated_code": session.last_generated_code,
"messages": session.messages.clone(),
"last_generated_code": session.last_generated_code.clone(),

Copilot uses AI. Check for mistakes.
Comment thread src/dashboard/routes.rs
Comment on lines +1611 to +1613
let filename = format!("{}.json", req.name.replace(' ', "_"));
let filepath = sessions_dir.join(&filename);
match std::fs::write(

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saved-session filenames are derived from req.name and joined onto sessions_dir without sanitization. A crafted name containing separators/../absolute paths can escape the sessions directory. Validate/sanitize req.name before constructing filename/filepath, and reject any resulting absolute path or parent-dir component.

Copilot uses AI. Check for mistakes.
Comment thread src/config.rs
Comment on lines +115 to 120
assert_eq!(
cfg.rag_embedding_model,
"sentence-transformers/all-MiniLM-L6-v2"
);
assert_eq!(cfg.rag_chunk_size, 1024);
}

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default-config test was updated for formatting, but it no longer asserts defaults for newly added fields (use_streaming, sessions_dir). Adding assertions here would prevent silent regressions in defaults when these settings are used by REPL/session persistence.

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +32 to +35
- **Streaming Responses** ⚡: Real-time token-by-token code generation in REPL and dashboard via SSE
- **Code Explanation** 📖: `/explain` command and dashboard button for step-by-step code breakdowns
- **Multi-File Projects** 📁: `/project` command generates entire project structures with multiple files
- **Session Persistence** 💾: Save and load conversation sessions across restarts

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README claims “real-time token-by-token” streaming in the REPL/dashboard, but the current generate_code_stream implementation buffers the full HTTP body (resp.text().await) before returning. Either adjust the implementation to truly stream incremental chunks, or soften the README wording to match current behavior.

Copilot uses AI. Check for mistakes.
Comment thread src/interface.rs
Comment on lines +1031 to +1032
"messages": conversation_history,
"last_generated_code": last_generated_code,

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The /session save JSON payload currently includes conversation_history and last_generated_code by value. With serde_json::json!, this will move those variables and make them unusable for the rest of the REPL loop. Clone (or serialize from references) to avoid moving REPL state during save.

Suggested change
"messages": conversation_history,
"last_generated_code": last_generated_code,
"messages": &conversation_history,
"last_generated_code": &last_generated_code,

Copilot uses AI. Check for mistakes.
Comment thread src/interface.rs
Comment on lines +1029 to +1030
"provider": config.provider,
"model": config.model,

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session_data includes "provider": config.provider and "model": config.model. Because config is a shared &AppConfig, this attempts to move String fields out of a borrowed value and won’t compile. Use config.provider.clone() / config.model.clone() (or store as &str) instead.

Suggested change
"provider": config.provider,
"model": config.model,
"provider": config.provider.clone(),
"model": config.model.clone(),

Copilot uses AI. Check for mistakes.
@Alvaro5
Alvaro5 merged commit 802c257 into main Apr 6, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants