v0.2.1 — Security scanning, health grades, 7-view dashboard, CodeFlow-parity analysis, platform verification#6
Merged
Merged
Conversation
…to schema, fix subgraph merge, surface loader errors, remove dead GraphPhase value https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…t, optional params, CI coverage
… docs, e2e cleanup, UI label
… skill trigger phrases
…phase2 note, exclude worktrees from scanner
Add an explicit 12s timeout on the /cascade-ask request and catch the timeout error so the test doesn't fail when the claude bridge is available in the environment (e.g. CI with claude CLI installed). https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…hangelog - fix(bridge): claude and copilot bridges now spawn non-blocking using spawn() instead of spawnSync(). /cascade-ask returns 200 immediately; dashboard polls /cascade-response as with windsurf. Fixes 30s+ event-loop freeze that caused parallel e2e tests to fail. - fix(mcp): sprang_query tokenizes multi-word queries and searches node IDs (file paths). "schema types" now returns results; "src/auth" finds file:src/auth.ts even when label is just "auth.ts". - test(mcp): 3 new query tests for multi-word, path, and ranking - test(e2e): 9 new tests — path traversal security, absolute path rejection, allowlist enforcement, DELETE /cascade-response, risk overlay R-key toggle, Learn empty state, bridge status shape, high-risk node in health, Sigma canvas - fix(ci): add .devin/config.json to manifest validation step - docs: add v0.2.1 CHANGELOG entry https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
Self-scan annotations are test artifacts in the sprang source repo. Projects that USE Sprang should commit their own .sprang/annotations/*.md. .gitkeep remains tracked to preserve the directory in fresh clones. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
- New types: SecurityCategory, SecurityWarning, DetectedPattern, HistorySnapshot, HealthGrade - SmellCategory extended with name_duplicate - SprangNode gets security_warnings[] and detected_patterns[] fields - GraphStats gets security_summary field - New utilities: health-grade (A-F scoring), history (snapshot log), similarity (LCS), mermaid (diagram gen) - Dashboard types.ts mirrors all schema additions - SmellBadge + HealthView updated for name_duplicate category https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…2.1) - health-grade.test.ts: grade boundary tests (A/B/C/D/F), penalty calculation, gradeColor returns, score clamping, and stats-derived defaults - similarity.test.ts: lcsLength identity/zero/empty/symmetric cases, lcsSimilarity ratio tests, structuralFingerprint strips comments/normalizes literals https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…gram CLI Core agents: - SecurityScannerAgent: 20 deterministic regex patterns across 8 categories (hardcoded_secret, sql_injection, xss_risk, unsafe_eval, unsafe_exec, unsafe_deserialization, path_traversal, weak_crypto), no LLM required - file-analyzer: detectPatterns() adds singleton/factory/observer/react_hook detected_patterns to graph nodes - smell-detector: detectNameDuplicates() using LCS similarity (3+ files) - phase2 orchestrator: security-scanner added to Phase 2 group1 parallel run CLI commands: - sprang open [path] [--port] [--no-browser]: launch dashboard for any repo - sprang diagram [path] [--output] [--format]: Mermaid flowchart from graph Dashboard: - HealthGrade component: A–F badge with spring entrance animation, score/100 - Sparkline component: pure SVG area chart, gradient fill, endpoint dot - HealthView: health grade badge + score sparkline, security issues section, detected patterns section with green badges - NodePanel: security warnings (severity badges + snippet), detected patterns - App.tsx: history state loaded from /health-history.json, analyze callback - vite.config.ts: /analyze, /analyze-status, /health-history.json endpoints https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…ix e2e nav - .gitignore: add cascade-response.json, claude-session.json, diff-overlay.json (session-local state that should never be committed) - annotations/: commit first real annotation (types.ts schema node, from claude -p test) - e2e/app.spec.ts: use navTab() helper for health view navigation (consistent with rest of suite) https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…arklines, CLI commands Merges all v0.2.1 feature work into the v0.2.1 branch: Core (packages/core): - Schema: SecurityWarning, SecurityCategory, DetectedPattern, HistorySnapshot, HealthGrade types; security_summary on GraphStats; security_warnings + detected_patterns on SprangNode; name_duplicate SmellCategory - SecurityScannerAgent: 20 deterministic regex patterns, 8 categories, no LLM - file-analyzer: detectPatterns() — singleton, factory, observer, react_hook, etc. - smell-detector: detectNameDuplicates() via LCS similarity (3+ files threshold) - phase2 orchestrator: security-scanner in parallel group1 - Utilities: health-grade.ts, history.ts, similarity.ts (LCS), mermaid.ts - Tests: 27 new unit tests for health-grade and similarity utilities CLI (packages/cli): - sprang open [path] [--port] [--no-browser]: launch dashboard for any repo - sprang diagram [path] [--output] [--format]: Mermaid flowchart from graph Dashboard (packages/dashboard): - HealthGrade component: A–F animated badge with score/100 - Sparkline component: pure SVG area chart with gradient fill - HealthView: grade badge + sparkline trend, security section, pattern badges - NodePanel: security warnings with snippet, detected pattern badges - App.tsx: history state, /health-history.json loading, analyze callback - vite.config.ts: /analyze, /analyze-status, /health-history.json endpoints - e2e: tests 37–40 for new endpoints and health grade badge (49 total) .gitignore: add agent-conversation.md, copilot-session.json, cascade-bridge-active, diff-overlay.json, packages/dashboard/.sprang/ All 606 unit tests pass. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…LI commands Versions: - All packages bumped 0.2.0 → 0.2.1 README: - Test badges: 555 unit → 606, 36 e2e → 49 - Test summary table: updated per-package counts (core 431, mcp 63) - CLI section: add sprang open, sprang diagram, sprang merge - CLI tree: add merge | open | diagram AGENTS.md: - sprang_health output schema updated: health_grade, health_score, grade_color, grade_breakdown, security_summary, history fields documented CLAUDE.md: - sprang_health tool row updated to mention grade and security_summary - New "CLI Commands" section added with all 9 commands + descriptions for sprang open and sprang diagram CHANGELOG.md: - v0.2.1 heading updated to 2026-06-08 - Added full "Added (new features)" section covering: SecurityScannerAgent, health grade, sprang_health extension, run history, pattern detection, name_duplicate smell, LCS utilities, mermaid generation, sprang open, sprang diagram, HealthGrade component, Sparkline component, HealthView/ NodePanel additions, on-demand analysis endpoints, 27 new unit tests, 4 new e2e tests - Fixed section summarized from the 2026-06-06 session packages/mcp/src/tools/sprang_health.ts: - Added health_grade, health_score, grade_color, grade_breakdown to return - Added security_summary from graph.stats - Added history (last 30 snapshots) via loadHistory() - Updated SprangHealthResult interface to match packages/mcp/src/server.ts: - sprang_health description updated to mention grade, security, history sprang-health skill/command/workflow (all copies): - description updated to include "health grade", "security issues" triggers - Steps updated: lead with grade badge, report security findings, show history trend https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…d timestamp alias - Add .cascade-trigger-session and other bridge transient files to DEFAULT_EXCLUDES so they no longer appear as graph nodes when scanning a project using the bridge - Add timestamp alias to SprangRespondResult (alongside written_at) for consistent response shape across all MCP tools - Fix e2e test 40: add missing }); closing brace for health view grade badge test - Bump .claude-plugin and .copilot-plugin manifests to version 0.2.1 - Update README: e2e count 36→49, expand test coverage description, add health grade section under MCP tools (A–F formula, security_summary categories, history tracking) - Add sprang_health health grade and security_summary to MCP tools table - Add sprang/annotations/file-packages-core-src-schema-types.ts.md https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…n apply - Vite code splitting: manualChunks splits sigma/graphology, xyflow/elkjs, framer-motion, and radix-ui into separate vendor chunks; main bundle drops from 2.4 MB to 142 KB - React.lazy + Suspense for DomainView and ArchitectureView so the 1.5 MB React Flow + ELK bundle only downloads when those tabs open - chunkSizeWarningLimit 1600 to silence expected large lazy-chunk warnings - merge.py: apply risk_score, risk_factors, decision_context, and structural_warnings from risk-scores.json into nodes during assembly - merge.py: load final-domains.json (or domains.json) as the domains array so the domain phase output is included in knowledge-graph.json - Add types.ts annotation marking it as foundation/high-risk - All 606 unit tests and 49/49 e2e tests pass https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
sprang-analyze workflow (7 phases now, was 6): - Add Phase 5: Domain Mapping — writes final-domains.json; this was entirely missing, causing domains: [] in every analysis run - Fix Phase 6: risk-scores.json format now includes decision_context and structural_warnings alongside risk_score/risk_factors so merge.py applies git history to nodes correctly - Fix resume check: 'phase: enriched' → 'phase: complete' (correct enum value) - Fix Phase 3/4 filenames: write final-layers.json and final-tours.json directly instead of layers.json/tour.json + a fragile Phase 6 copy step - Fix Phase 4 tour format: Tour object array (with id/title/description/steps) not a flat step array — the flat format is silently discarded by the dashboard - Fix Phase 6 merge.py path: simplified to two known locations, removed the multi-path search that referenced non-existent home dirs - Fix Phase 5 smell detection: remove the broken 'npx sprang health' CLI call (outputs text, not JSON); instruct direct graph analysis instead - Update all phase progress strings to 7-phase numbering ([1/7]…[7/7]) merge.py: - Set stats.phase2_completed_at from phase6-done.json timestamp Claude bridge: - Add sprang_respond to ALLOWED_MCP_TOOLS so the bridge can call it when routed through the dashboard Ask Agent panel Rules: - sprang-context: add graceful handling when graph not yet built (sprang_node returns null → skip the check, proceed normally) - cascade-messaging: document that sprang_respond needs both 'response' AND 'question' fields; add example call Commands: - sprang.md: fallback to 'node packages/cli/dist/index.js scan .' when npx sprang is not available (source repo case) https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…se count, rules sprang-analyze: - Fix phase count: header now says "8 phases (Phase 0 through Phase 7)" consistently across workflow, SKILL.md, and Claude command (was "7 phases" — off by one) sprang-onboard (SKILL.md + Claude command): - Fix persona mapping: non-technical→pm, experienced→senior before calling sprang_tour; sprang_tour only accepts junior/senior/pm so presenting 'non-technical' or 'experienced' to the user without mapping them caused invalid tool calls sprang-knowledge workflow: - Fix Phase 3 output filename: knowledge-layers.json → final-layers.json - Fix Phase 4 output filename: knowledge-tour.json → final-tours.json, and wrap the flat step array in a Tour object (merge.py and the Zod schema require Tour objects, not raw step arrays) - Fix Phase 5 load references to match the corrected Phase 3/4 filenames - Tour format now consistent with sprang-analyze workflow sprang-team (SKILL.md, Claude command, Devin workflow): - Fix staleness detection: most nodes won't have decision_context.last_changed (only nodes that went through the git-layer phase do); fall back to graph.stats.generated_at as a "last analyzed" proxy so staleness comparisons work for all nodes, not just the few with git history .windsurf/rules/ (new): - Create the .windsurf/rules/ directory with all three rule files (sprang-context.md, cascade-messaging.md, sprang-highrisk.md) — this directory was in the original plan as a Windsurf-native fallback but was never actually created https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
- CHANGELOG: document all 15 analysis pipeline/workflow fixes from 2026-06-08 session (merge.py risk scores, domain phase, tour format, resume check, filename mismatches, persona mapping, windsurf rules, sprang_respond in allowedTools, Vite code splitting) - README: agentic install + manual setup now copy rules to both .devin/rules/ and .windsurf/rules/ (the latter was missing, meaning Windsurf/Cascade never loaded always-on rules automatically) - README: Copilot bridge table now shows correct flag --prompt instead of -p to match copilot.ts implementation https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…erienced All 5 personas now work end-to-end across MCP tool, dashboard selector, onboarding commands, and documentation. - sprang_tour now accepts: junior | senior | experienced | pm | non-technical non-technical: entry-points and domain nodes only (executives/stakeholders) pm: domain and service nodes (product managers) junior: all steps with language lessons (default) senior/experienced: skip intro step (experienced engineers) - PersonaSelector: 4 options — Business / Product / Learn / Deep Dive - LearnPanel: persona hints for all 5 values - /sprang-onboard command: documents all 4 personas with correct MCP values - Dashboard types.ts: Persona type expanded to include senior and pm - 2 new MCP tests: experienced alias, non-technical filter (65 total) - README: persona table in MCP tools section, updated tour description, updated PersonaSelector component description, test count 606 → 608 - CHANGELOG: test count updated https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…, rules table For new users who've never heard of Sprang: - Platform comparison table at top of Installation — shows at a glance what Claude Code / Windsurf / Copilot each get (MCP, commands, rules, hooks, Ask Agent, session continuity) - Claude Code "What the plugin activates" table: added .claude/rules/ (sprang-context, sprang-highrisk, cascade-messaging) and AGENTS.md — was missing entirely; new users couldn't know always-on rules exist - Claude Code: added "What Claude does automatically" block — mirrors the equivalent block in the Windsurf section - GitHub Copilot: expanded .github/copilot-instructions.md description, added AGENTS.md row, added "What Copilot does automatically" block, added Ask Agent / copilot CLI bridge note, added honest capability comparison vs Claude Code / Windsurf https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
README corrections: - Claude Code reads CLAUDE.md (not AGENTS.md) — fixed in plugin table, platform comparison table, and What Claude does automatically block - Windsurf/Devin Desktop and GitHub Copilot both read AGENTS.md — clarified in Copilot section and platform comparison table - Platform comparison table: added "Auto-loaded instructions" row showing CLAUDE.md (Claude Code) vs AGENTS.md (Windsurf + Copilot) e2e tests — 49 → 56 (+7): - Test 50: keyboard shortcut '1' → graph view - Test 51: keyboard shortcut '2' → health view - Test 52: keyboard shortcut '3' → domains view - Test 53: learn view persona selector shows all 4 options - Test 54: start tour button advances to first step + shows step counter - Test 55: tour step advance and exit button - Test 56: health view security section renders with sql_injection finding (uses new mockGraphWithSecurity fixture with security_warnings) CHANGELOG updated: complete persona system + e2e additions documented Test counts: 608 unit + 56 e2e — all passing https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
1. CommonJS require() not detected as imports — add CJS_REQUIRE_RE regex
alongside IMPORT_FROM_RE in extractTsJsImports(). Projects using
require('./path') now get proper import edges (diff_impact blast radius,
diagram arrows, query connectivity all affected).
2. sprang query CLI missing positional path arg — query accepted path only
via -p flag while health/status/scan used positional [path]. Added
optional second positional argument; -p still works as alias.
3. Mermaid flat diagram showed no edges — slice(0, 20) picked alphabetically
first 20 file nodes, missing edge targets (lib/ files). Now sorts by
degree (most connected first) up to 30 nodes so entry-point/dependency
relationships appear in the diagram.
All 436 core + 27 CLI + 65 MCP = 528 unit tests pass.
https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
Documents 3 bugs found and fixed during real-world e2e test against codeflow repo: CommonJS require() import detection, sprang query CLI positional path, and mermaid flat diagram edge generation. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
New visualization modes: - Graph3DCanvas.tsx — WebGL 3D force-graph (react-force-graph) with risk heat coloring and auto-rotation; 2D/3D toggle pill in GraphView toolbar - TreemapView.tsx — D3 hierarchy treemap; tile size = lines of code, color = risk score; staggered entry animation; zoom controls - MatrixView.tsx — file-to-file dependency adjacency matrix sorted by layer rank; row/col hover highlights; scroll-to-zoom - graphTransform.ts — shared data adapters for 3D, treemap, matrix Animation upgrades: - View transitions: opacity+y-slide (was opacity-only) across all 5→7 views - HealthView table rows: staggered fade+slide-in on mount (useReducedMotion aware) - StatCard: AnimatedCount spring counter; hover-lift already present - FileExplorer: AnimatePresence height collapse on folder expand/collapse; chevron rotates 90° via motion.span - LayerCardNode: scale+opacity stagger on mount based on colorIndex - ArchitectureView: motion.div fade+scale reveal when ELK layout resolves - GraphCanvas: AnimatePresence pulse ring (scale+opacity loop) on selected node New components: - AnimatedCount.tsx — useSpring/useTransform counter; respects reducedMotion Store: adds graphViewMode: '2d' | '3d' + setGraphViewMode action Types: MobileView gains 'treemap' | 'matrix' Nav: 7 tabs (Graph, Health, Domains, Architecture, Treemap, Matrix, Learn) Keyboard: t=treemap, m=matrix, 7=learn (was l/5)
…64/64) - Replace react-force-graph with direct 3d-force-graph + react-kapsule imports in Graph3DCanvas to eliminate AFRAME/AR bundle contamination - Lazy-load Graph3DCanvas so THREE.js stays out of the eager bundle - Add manualChunks vendor-3d isolation for the entire 3D stack (~1290kB), co-locate events polyfill with Sigma.js in vendor-graph to prevent EventEmitter initialization order corruption - Inline getRiskColor in graphTransform.ts to break cross-chunk circular dep - Fix TreemapView empty-state check: d3-hierarchy root with children:[] has no .children property so it was counted as a file; filter by nodeId instead - Add e2e tests for treemap/matrix tabs, keyboard shortcuts (T/5, M/6, L/7), security findings section, and treemap empty state (all 64 tests now pass) - Update KeyboardShortcutsHelp with correct shortcut labels for new views https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
Treemap/matrix tabs + empty-state tests added 7 new e2e tests, bringing the total from 57 to 64. Badge, inline text, and test table all updated. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…new tests (654 unit / 72 e2e) Until now only the Claude Code path was exercised for real (claude -p); the Windsurf and Copilot integrations were covered solely by stubbed unit tests. This adds three layers of real validation: - Platform bridge e2e suite (e2e/bridge.spec.ts, 8 tests) — a dedicated playwright.bridge.config.ts boots two preview servers with mock claude and copilot CLIs prepended to PATH (e2e/mock-bin/), so the real spawn → stdout parse → session persist → cascade-response.json pipeline runs end-to-end through POST /agent-ask and GET /agent-response. Covers detection priority (windsurf marker > claude > copilot), --resume session continuity for both CLIs, the --allowedTools MCP allowlist, the Windsurf .cascade-trigger-session protocol with atomic write, and DELETE /agent-response session clearing. New npm script test:e2e:bridge; isolated SPRANG_ROOT per server (gitignored). - Platform parity suite (packages/cli/tests/platform-parity.test.ts, 22 tests) — locks cross-platform invariants: 6 manifests parse and reference real paths, plugin versions match, 11 skills in sync between skills/ and .windsurf/skills/ with Windsurf trigger phrases, 11 workflows + 11 Claude commands, 3 rules byte-identical between .windsurf/rules/ and .devin/rules/, hooks.json wiring, executable Claude hooks, copilot-instructions.md lists all 9 MCP tools, and the cascade-messaging .vsix artifact exists. - Windsurf conversation hook real execution (packages/cli/tests/windsurf-hook.test.ts, 8 tests) — runs save-conversation.py via real python3 against fixture transcripts: append format, last-exchange-only, multi-response join, and silent no-op on every malformed-input path. CI gains a bridge e2e step. README badges, test table, and test structure updated; CHANGELOG documents the new verification layer.
…and attributions - Add "The Leap" section near top: Kierkegaard's qualitative spring concept explained concisely — why incremental tools can't answer "what breaks?" - Add "Not just codebases" subsection emphasising knowledge-base support - Add "What existing tools don't do" comparison table (Grep/LSP vs LLM context vs Sourcegraph vs Sprang) with key differentiators - Add "Workflows in practice" section: 5 real-world scenarios with full command sequences (Day 1, refactoring, PM review, PR review, Obsidian) - Add supported languages row to capabilities table (12 languages listed) - Fix factual errors throughout: - 5 views → 7 views (Graph/Health/Domains/Architecture/Treemap/Matrix/Learn) - Keyboard shortcuts: add Treemap (t/5) and Matrix (m/6), fix Learn to l/7 - e2e test tree: app.spec.ts 56 → 64, shortcuts 1-5 → 1-7 - Replace closing footnote with proper "Attributions" section crediting Understand Anything (Egonex/Lum1104) and CodeFlow (braedonsaunders) - Update TOC: add The Leap, Workflows in practice, Attributions entries - Sync AGENTS.md and CLAUDE.md: 7-view list with Treemap/Matrix, correct keyboard shortcuts (1-7 / g h d a t m l), Learn tab corrected to L/7 https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…opilot-instructions pnpm build install.sh and install.ps1: - After linking skills for windsurf/copilot, print complete "Next steps" with the exact MCP config JSON (REPO_DIR already substituted), rules/ hooks/workflows copy commands, and link to full docs. Previously said "Run /sprang now" — misleading because MCP config and project rules still needed to be set up separately. README agentic install (Windsurf / Devin Desktop): - Step 5 now writes .devin/config.json with the actual absolute SPRANG_DIR path rather than copying the repo template. The template has a relative packages/mcp/dist/server.js that only resolves from inside the Sprang repo; copying it verbatim into a user project silently breaks Devin Desktop MCP connectivity. .github/copilot-instructions.md: - Replace bare "pnpm build" with "cd <sprang-repo> && pnpm install && pnpm build" — the original would run the user's own project build if they were in their project directory. CHANGELOG.md: - Add "Fixed (installer and agentic install)" and "Added (documentation)" sections documenting all v0.2.1 installer fixes and the README improvements (The Leap section, workflow examples, comparison table, 7-view count fixes, attributions). https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
The bridge e2e suite mocks claude/copilot CLIs and asserts detectBridge resolves to claude/copilot. When run from inside Windsurf/Devin Desktop, the inherited WINDSURF_CASCADE_TERMINAL_KIND env var leaked into the spawned preview servers and forced detection to windsurf, failing the assertions. CI was unaffected (no such var on GitHub runners). Prefix both webServer commands with 'env -u WINDSURF_CASCADE_TERMINAL_KIND' so detection is deterministic regardless of host. All 8 bridge tests pass locally and in CI (72 e2e total).
Security scanner (two compounding bugs, headline v0.2.1 feature was dead): - phase2.ts defined loadSecurityScanner() but never invoked it — the agent was missing from the Group 1 run batch and progress map, so a full scan never populated stats.security_summary (health security penalty always 0, dashboard security section always empty). Wired into Group 1. - security-scanner.ts filtered/read node.filePath, which is never populated; the canonical field is node.location.file (used by every other agent). Switched to node.location?.file so it actually scans files. - New regression test (phase2-security.test.ts) runs the real Phase 1 -> 2 pipeline against a vulnerable file and asserts security_summary + warnings. ESLint: - Add eslint.config.js (flat, ESLint v9): JS + TS recommended, react-hooks for dashboard, _-prefix ignore, no-control-regex/no-empty-object-type off for the MCP sanitizer/no-arg-input cases. pnpm lint had no config and always errored; now passes clean (0 errors) and is wired into CI. - Fixed ~60 findings: dead imports/code (incl. unused buildOutEdges/ computeLayerDepths machinery), ternary-statements -> if/else, 2 useless regex escapes. Added eslint-plugin-react-hooks dep. Test harness: - playwright.bridge.config.ts: env -u WINDSURF_CASCADE_TERMINAL_KIND so the claude/copilot bridge tests are deterministic when run inside Windsurf/Devin. Totals: 656 unit (449 core + 85 dashboard + 65 mcp + 57 cli), 72 e2e.
`sprang --version` printed a stale hardcoded '0.1.0' while the package is at 0.2.1 (README documents 0.2.1). Read the version from package.json at runtime (../package.json relative to dist/index.js) so it tracks the published version automatically and never drifts again.
runner.ts forked Phase 2 via new URL('./phase2-runner.js', import.meta.url).
After bundling, runner.ts is flattened into core/dist/index.js (dist root)
while phase2-runner stays at dist/orchestrator/phase2-runner.js — so the
resolved path (dist/phase2-runner.js) did not exist. With stdio:'ignore'
the ENOENT was completely silent, so 'sprang scan' always left the graph at
phase=skeleton with no layers/risk/security/domains/tours enrichment.
Fix: resolve whichever candidate path actually exists (source vs dist
layout), add a child 'error' handler to surface spawn failures, and fall
back to running Phase 2 inline if the runner script can't be located.
Verified end-to-end: background fork now completes — phase=complete, 9
layers, 17 security findings — with zero API keys.
The /analyze endpoint (instant analysis from a local path or GitHub URL) spawned 'npx sprang scan', but sprang is not published to npm — so npx hit the registry and got a 404, and with stdio:'ignore' it failed silently. The clone succeeded but no graph was ever produced (landing screen spun forever). Resolve the CLI via a priority chain so it works in every scenario: 1. local monorepo build (node <repo>/packages/cli/dist/index.js) 2. 'sprang' on PATH (global install / pnpm link) 3. 'npx -y sprang' (published-to-npm fallback, for when we publish) Also surface spawn errors via a child 'error' handler instead of failing silently. Verified end-to-end: POST /analyze with a GitHub URL clones + scans and the graph (230 nodes) is served within ~3s.
1. New analysis: no way to return to the landing screen once a graph was
loaded. Added a nav button (forceLanding state) that re-shows
LandingScreen so the user can analyze another project; clears when the
new graph loads.
2. 3D graph churned erratically / hard to select nodes:
- toForceGraphData() was recomputed every render -> react-kapsule got a
new graphData object each time -> continuous d3 simulation re-heat.
Now memoized on [graph, showRiskOverlay].
- auto-rotation setInterval cleanup was returned from the setTimeout
callback (never ran) -> leaked intervals fighting the camera; only
stopped on mousedown. Now cleaned up from the effect and stops on
pointerdown/touchstart/wheel.
…ublish server item - README badges/dev-commands/test-summary: 654 -> 656 unit (core 447 -> 449) - README: note the New analysis button on the instant-analysis section - CHANGELOG: add Planned (pre-publish) — standalone dashboard server so /analyze + graph routes work for npm-installed users (not just vite preview)
ESLint v9 flat config does not read .gitignore, so 'eslint .' descended into the gitignored .claude/worktrees/ agent worktree copies and re-reported the whole repo's lint state once per worktree (hundreds of spurious errors locally). CI was unaffected (fresh checkout has no worktrees) but it broke 'pnpm lint' for anyone using the worktree harness. Added '.claude/worktrees/**' to the config ignores. Also: document the fix in CHANGELOG and add the phase2-security.test.ts line to the README core test tree (was missing; keeps the 449/656 counts traceable). https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
- Rename @sprang/cli → sprang (unscoped public package) - tsup.config.ts for CLI + MCP with noExternal: ['@sprang/core'] so the published tarball is fully self-contained; @sprang/core runtime deps promoted to direct CLI dependencies so npm installs them - scripts/pack-assets.mjs copies the compiled dashboard SPA, standalone server, and MCP server into packages/cli/dist/ before npm publish - Standalone dashboard HTTP server (src/server/standalone.ts) — serves the pre-built React SPA + all 9 API routes without requiring Vite; route handlers extracted from vite.config.ts into src/server/routes.ts (Connect-compatible, works in both Vite and standalone contexts) - sprang open now spawns the standalone server instead of npx vite preview; resolves server path for both monorepo and npm global install layouts - sprang init command writes .mcp.json with absolute MCP server path - .github/workflows/publish.yml — tag-triggered publish with full CI gates, version guards (tag == package.json == all 4 package versions), and npm publish --provenance --access public (NPM_TOKEN secret) - pack-and-install CI job: npm pack → install into clean tempdir → assert sprang --version, sprang scan (phase:complete), MCP JSON-RPC handshake - eslint.config.js: add bin/*.js and scripts/*.mjs to Node globals config - pnpm-lock.yaml: sync tsup into @sprang/dashboard devDependencies https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
- Move @sprang/core to devDependencies in CLI to prevent workspace:* from appearing in the published tarball (npm install would fail with EUNSUPPORTEDPROTOCOL) - Add CJS build target to MCP tsup config with splitting:false and bundle all deps (@sprang/core + @modelcontextprotocol/sdk) so the standalone mcp-server.cjs works without any peer installs - Update pack-assets.mjs to copy server.cjs → dist/mcp-server.cjs and remove any stale mcp-server.js left from previous builds - Update init.ts to resolve mcp-server.cjs in both npm and monorepo layouts - Update CI smoke test to use mcp-server.cjs and accept 'skeleton' phase from --phase1-only scans Verified end-to-end: npm install tarball → sprang --version, sprang scan, and MCP JSON-RPC handshake all pass from the installed package. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
Add CHANGELOG entry for the npm publish automation, standalone dashboard server, MCP CJS bundle, sprang init command, pack-assets script, and pack-and-install CI smoke test. Add Quick install (npm) section at the top of the README Installation section so users who install via npm see the sprang init → sprang scan → sprang open workflow immediately. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
Generated by `npm pack` during the pack-and-install smoke test and local testing. No need to commit these — they are re-created by the CI workflow. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
The standalone installer in bin/ still referenced the old mcp-server.js path. Updated to mcp-server.cjs to match the CJS standalone bundle now produced by the MCP tsup config. https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
…, verify published flow - Delete packages/cli/bin/install.js (orphaned duplicate of 'sprang init'; never invoked, not a bin entry). Drop 'bin/' from package files whitelist. sprang init (src/commands/init.ts) is now the single source of truth. - publish.yml: version-check only the genuinely versioned manifests (.claude-plugin/plugin.json, .copilot-plugin/plugin.json) and make it a hard failure; marketplace.json + .devin/config.json carry no version. - README: fix stale @sprang/cli references (package is now unscoped 'sprang'). - CHANGELOG: document hardening + full published-package verification. Verified: npm name 'sprang' available (404); packed tarball installs clean; --version, init, scan, MCP initialize, and standalone 'sprang open' server (SPA + /knowledge-graph.json + /bridge-status) all work. 656 tests pass.
Two bugs left stale magenta pulse rings stranded in empty space when
switching node selection in the 2D Sigma graph:
1. The exit animation inherited transition={{ repeat: Infinity }}, so the
old ring's exit never completed and AnimatePresence never unmounted it
(froze in place). Give exit its own finite transition (0.2s, repeat 0)
and move the looping pulse into the animate prop.
2. Ring position was computed once on selection, so pan/zoom left it frozen
at stale viewport coords. Recompute from the node's graph coords on every
Sigma afterRender via a selectedNodeIdRef, keeping it glued to the node.
Verified in-browser: clicking through nodes no longer leaves orphaned rings;
ring tracks the node during pan/zoom. 656 tests pass.
…1 date - CLAUDE.md: 'pnpm --filter @sprang/cli test' -> 'pnpm --filter sprang test' (the CLI package was renamed to unscoped 'sprang'; the old filter matched nothing). CHANGELOG hits are historical entries and left intact. - CHANGELOG: correct the [0.2.1] header date to the actual release date (2026-06-17); subsections already span 06-13..06-17. Verified for release: all 4 package versions + both plugin manifests = 0.2.1; 9 MCP tools, 11 slash commands (matches README badges); README npm-install flow accurate; cascade-messaging-0.1.0.vsix references correct. 656 tests pass.
- Lead the install section with 'npm install -g sprang' (durable) and add a callout explaining why global is preferred for 'sprang init': it writes the MCP server's absolute path into .mcp.json, and the npx cache path can be pruned by npm and break the config. npx still works for one-off commands. - Swap the top-of-readme pnpm-install badge for an npm install badge to match the npm-first install path.
…rides pnpm audit flagged 6 advisories (1 high, 5 moderate), all transitive: - hono <4.12.25 (1 high CORS reflect, 4 moderate) via @modelcontextprotocol/sdk - got <11.8.5 (moderate) via 3d-force-graph > three-bmfont-text > nice-color-palettes Neither is a declared dependency of the published 'sprang' package: the MCP SDK (and its hono) is bundled into mcp-server.cjs, and got is a build-time transitive of the dashboard's 3D stack that never reaches the browser bundle. So end users running 'npm install -g sprang' were never exposed. Added pnpm.overrides to force patched versions anyway, so the bundled hono in mcp-server.cjs is patched and the dev tree is clean. After: 'pnpm audit --prod' = no known vulnerabilities. Remaining 3 dev-only advisories (esbuild via tsup, markdown-it via @vscode/vsce in the gitignored cascade-messaging extension) are build tooling, never shipped. Verified: build/typecheck/lint pass, 656 tests pass, MCP server still answers the JSON-RPC initialize handshake with the patched bundled hono.
- Add MIT LICENSE at repo root (was referenced by cli package.json 'files' and declared in package.json, but the file did not exist — npm publish would have shipped no license and shown a blank license on the package page). - pack-assets.mjs now copies root LICENSE + README.md into packages/cli/ at pack/publish time (single source of truth stays at root). Verified the cli tarball now includes LICENSE (1.1kB) and README.md. - gitignore the copied packages/cli/LICENSE and packages/cli/README.md. cascade-messaging extension (gitignored, distributed as .vsix) hardened separately and locally: upgraded esbuild 0.25->0.28.1, dropped @vscode/vsce from devDeps (packaging uses npx), added .vscodeignore + LICENSE + README. Result: clean 'npm install' (0 deprecated warnings, 0 vulnerabilities) and a 6-file .vsix (no node_modules, no source) — zero shipped-dependency vulns. build/typecheck/lint pass, 656 tests pass, pnpm audit --prod clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v0.2.1 — Security scanning, health grades, 7-view dashboard, CodeFlow-parity analysis, npm distribution
This release makes Sprang publishable to npm as a single self-contained package, adds a full deterministic analysis layer (security, health grading, call graph, design patterns, layer violations), three new visualization modes, a zero-friction landing screen, real platform verification, and a large batch of correctness fixes. All analysis is deterministic — no API key required.
Quality gate: 656 unit tests + 72 e2e tests passing · typecheck clean · lint clean ·
pnpm audit --prodclean.npm distribution (makes the release shippable)
sprangis now a single self-contained npm package — CLI renamed@sprang/cli→ unscopedsprang,@sprang/corebundled inline (tsupnoExternal), all platform integration files included via thefileswhitelist. Published with provenance.dist/dashboard-server.js) — route handlers extracted fromvite.config.tsinto a framework-agnosticregisterRoutes()+http.createServerstandalone server.sprang openworks with no Vite/pnpm.dist/mcp-server.cjs) — self-contained, runs vianode mcp-server.cjswith no peer installs.sprang init— writes/merges.mcp.jsonpointing at the bundled MCP server (--yesfor non-interactive).pack-assets.mjs— copies dashboard SPA, standalone server, MCP bundle, and LICENSE + README into the tarball..github/workflows/publish.yml) — onv*tags: full CI → tag/version sync check (hard fail) → publish--provenance --access publicviaNPM_TOKEN.--version,scan --phase1-only, and the MCPinitializehandshake.New analysis features (deterministic, zero API key)
SecurityScannerAgent— 20 regex patterns across 8 categories (secrets, SQLi, XSS, unsafe eval/exec/deserialization, path traversal, weak crypto); findings stored on nodes, summarized instats.security_summary, feed the health grade.calcHealthGrade()from 5 penalty factors (dead code, circular deps, god nodes, coupling, security); exposed viasprang_health(health_grade,health_score,grade_color,grade_breakdown,history)..sprang/history.jsonsnapshot per scan (max 50, atomic write).callsedges (internal + external),internalCalls/externalCalls/callerCount/isUnusedper function.context_provider,decorator,event_emitter.importsedges aslayer_violationsmells feeding the grade.sprang diagramCLI command.Dashboard — 7 views + instant analysis
sprang open --auto-scan.prefers-reduced-motionsupport.sprang_touraccepts junior / senior / experienced / pm / non-technical.Documentation
Platform verification (real, not mocked)
claude/copilotCLIs on PATH; really spawns, parses, persists sessions, writes responses. Covers detection priority, full ask pipeline,--resumecontinuity, Windsurf trigger-file protocol, session clearing.save-conversation.pyvia realpython3.Notable fixes
sprang --versionread a hardcoded0.1.0→ now readspackage.json.ENOENTon the forked worker path) → scans were skeleton-only; now resolves the correct path with inline fallback (phase: complete, layers/risk/security populated).npx sprang404'd) → CLI-resolution priority chain (local build → PATH → npx).node.location.file.repeat: Infinityso old rings never unmounted; position frozen on pan/zoom → finite exit transition +afterRendertracking.phase2_completed_atnot applied.sprang-analyzeworkflow — missing domain phase, wrong intermediate filenames, invalid resume phase value, flat tour array..devin/config.jsonpath, e2e env leak (WINDSURF_CASCADE_TERMINAL_KIND), CommonJSrequire()imports,sprang querypositional path.Tooling & security hardening
pnpm lintwas previously broken); ~60 findings fixed.bin/install.js; tightened publish manifest-version check to a hard failure.hono≥4.12.25,got≥11.8.5) — neither is a declared dep of the published package, but the bundled hono is now patched.pnpm audit --prod: no known vulnerabilities.LICENSE(was referenced byfiles/package.jsonbut missing); shipped in the tarball alongside README.Release mechanics
Merging this PR puts v0.2.1 on
main. Taggingv0.2.1then triggerspublish.yml(CI → version sync check →npm publish --provenance). All four package versions + both plugin manifests are pinned at0.2.1;NPM_TOKENis configured.