fix: v2.0.1 release — fix broken installs, CI, security, and docs

Critical fixes:
- npm postinstall.js: BINARY_VERSION '1.1.3' → '2.0.1' (every install was 404ing)
- npm package name: corrected error messages to 'vestige-mcp-server'
- README: npm install command pointed to wrong package
- MSRV: bumped from 1.85 to 1.91 (uses floor_char_boundary from 1.91)
- CI: removed stale 'develop' branch from test.yml triggers

Security hardening:
- CSP: restricted connect-src from wildcard 'ws: wss:' to localhost-only
- Added X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy headers
- Added frame-ancestors 'none', base-uri 'self', form-action 'self' to CSP
- Capped retention_distribution endpoint from 10k to 1k nodes
- Added debug logging for WebSocket connections without Origin header

Maintenance:
- All clippy warnings fixed (58 total: redundant closures, collapsible ifs, no-op casts)
- All versions harmonized to 2.0.1 across Cargo.toml and package.json
- CLAUDE.md updated to match v2.0.1 (21 tools, 29 modules, 1238 tests)
- docs/CLAUDE-SETUP.md updated deprecated function names
- License corrected to AGPL-3.0-only in root package.json

1,238 tests passing, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam Valladares 2026-03-01 20:20:14 -06:00
parent b03df324da
commit c6090dc2ba
51 changed files with 343 additions and 490 deletions

View file

@ -586,11 +586,10 @@ impl ContextCapture {
}
// Java Spring
if let Ok(content) = fs::read_to_string(self.project_root.join("pom.xml")) {
if content.contains("spring") {
if let Ok(content) = fs::read_to_string(self.project_root.join("pom.xml"))
&& content.contains("spring") {
frameworks.push(Framework::Spring);
}
}
// Ruby Rails
if self.file_exists("config/routes.rb") {
@ -613,30 +612,27 @@ impl ContextCapture {
/// Detect the project name from config files
fn detect_project_name(&self) -> Result<Option<String>> {
// Try Cargo.toml
if let Ok(content) = fs::read_to_string(self.project_root.join("Cargo.toml")) {
if let Some(name) = self.extract_toml_value(&content, "name") {
if let Ok(content) = fs::read_to_string(self.project_root.join("Cargo.toml"))
&& let Some(name) = self.extract_toml_value(&content, "name") {
return Ok(Some(name));
}
}
// Try package.json
if let Ok(content) = fs::read_to_string(self.project_root.join("package.json")) {
if let Some(name) = self.extract_json_value(&content, "name") {
if let Ok(content) = fs::read_to_string(self.project_root.join("package.json"))
&& let Some(name) = self.extract_json_value(&content, "name") {
return Ok(Some(name));
}
}
// Try pyproject.toml
if let Ok(content) = fs::read_to_string(self.project_root.join("pyproject.toml")) {
if let Some(name) = self.extract_toml_value(&content, "name") {
if let Ok(content) = fs::read_to_string(self.project_root.join("pyproject.toml"))
&& let Some(name) = self.extract_toml_value(&content, "name") {
return Ok(Some(name));
}
}
// Try go.mod
if let Ok(content) = fs::read_to_string(self.project_root.join("go.mod")) {
if let Some(line) = content.lines().next() {
if line.starts_with("module ") {
if let Ok(content) = fs::read_to_string(self.project_root.join("go.mod"))
&& let Some(line) = content.lines().next()
&& line.starts_with("module ") {
let name = line
.trim_start_matches("module ")
.split('/')
@ -647,8 +643,6 @@ impl ContextCapture {
return Ok(Some(name));
}
}
}
}
// Fall back to directory name
Ok(self
@ -734,8 +728,8 @@ impl ContextCapture {
// Check test directories
for test_dir in test_dirs {
let test_path = self.project_root.join(test_dir);
if test_path.exists() {
if let Ok(entries) = fs::read_dir(&test_path) {
if test_path.exists()
&& let Ok(entries) = fs::read_dir(&test_path) {
for entry in entries.filter_map(|e| e.ok()) {
let entry_path = entry.path();
if let Some(entry_stem) = entry_path.file_stem() {
@ -746,7 +740,6 @@ impl ContextCapture {
}
}
}
}
}
// For Rust, look for mod.rs in same directory
@ -799,9 +792,9 @@ impl ContextCapture {
/// Detect the module a file belongs to
fn detect_module(&self, path: &Path) -> Option<String> {
// For Rust, use the parent directory name relative to src/
if path.extension().map(|e| e == "rs").unwrap_or(false) {
if let Ok(relative) = path.strip_prefix(&self.project_root) {
if let Ok(src_relative) = relative.strip_prefix("src") {
if path.extension().map(|e| e == "rs").unwrap_or(false)
&& let Ok(relative) = path.strip_prefix(&self.project_root)
&& let Ok(src_relative) = relative.strip_prefix("src") {
// Get the module path
let components: Vec<_> = src_relative
.parent()?
@ -813,16 +806,13 @@ impl ContextCapture {
return Some(components.join("::"));
}
}
}
}
// For TypeScript/JavaScript, use the parent directory
if path
.extension()
.map(|e| e == "ts" || e == "tsx" || e == "js" || e == "jsx")
.unwrap_or(false)
{
if let Ok(relative) = path.strip_prefix(&self.project_root) {
&& let Ok(relative) = path.strip_prefix(&self.project_root) {
// Skip src/ or lib/ prefix
let relative = relative
.strip_prefix("src")
@ -836,7 +826,6 @@ impl ContextCapture {
}
}
}
}
None
}
@ -874,14 +863,12 @@ impl ContextCapture {
fn extract_toml_value(&self, content: &str, key: &str) -> Option<String> {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with(&format!("{} ", key))
|| trimmed.starts_with(&format!("{}=", key))
{
if let Some(value) = trimmed.split('=').nth(1) {
if (trimmed.starts_with(&format!("{} ", key))
|| trimmed.starts_with(&format!("{}=", key)))
&& let Some(value) = trimmed.split('=').nth(1) {
let value = value.trim().trim_matches('"').trim_matches('\'');
return Some(value.to_string());
}
}
}
None
}

View file

@ -274,11 +274,10 @@ impl GitAnalyzer {
if let Some(path) = delta.new_file().path() {
files.push(path.to_path_buf());
}
if let Some(path) = delta.old_file().path() {
if !files.contains(&path.to_path_buf()) {
if let Some(path) = delta.old_file().path()
&& !files.contains(&path.to_path_buf()) {
files.push(path.to_path_buf());
}
}
}
}
@ -492,11 +491,10 @@ impl GitAnalyzer {
.single()
.unwrap_or_else(Utc::now);
if let Some(since_time) = since {
if commit_time < since_time {
if let Some(since_time) = since
&& commit_time < since_time {
continue;
}
}
let message = commit.message().map(|m| m.to_string()).unwrap_or_default();

View file

@ -209,8 +209,8 @@ impl PatternDetector {
.collect();
for pattern in relevant_patterns {
if let Some(confidence) = self.calculate_match_confidence(code, &code_lower, pattern) {
if confidence >= 0.3 {
if let Some(confidence) = self.calculate_match_confidence(code, &code_lower, pattern)
&& confidence >= 0.3 {
matches.push(PatternMatch {
pattern: pattern.clone(),
confidence,
@ -218,7 +218,6 @@ impl PatternDetector {
suggestions: self.generate_suggestions(pattern, code),
});
}
}
}
// Sort by confidence

View file

@ -337,14 +337,13 @@ impl CodebaseWatcher {
}
// Detect patterns if enabled
if config.detect_patterns {
if let Ok(content) = std::fs::read_to_string(path) {
if config.detect_patterns
&& let Ok(content) = std::fs::read_to_string(path) {
let language = Self::detect_language(path);
if let Ok(detector) = detector.try_read() {
let _ = detector.detect_patterns(&content, &language);
}
}
}
}
FileEventKind::Deleted => {
// File was deleted, remove from session
@ -576,13 +575,12 @@ impl ManualEventHandler {
}
// Detect patterns
if self.config.detect_patterns {
if let Ok(content) = std::fs::read_to_string(path) {
if self.config.detect_patterns
&& let Ok(content) = std::fs::read_to_string(path) {
let language = CodebaseWatcher::detect_language(path);
let detector = self.detector.read().await;
let _ = detector.detect_patterns(&content, &language);
}
}
Ok(())
}