Skip to content

v0.2.1 — Security scanning, health grades, 7-view dashboard, CodeFlow-parity analysis, platform verification#6

Merged
FavioVazquez merged 67 commits into
mainfrom
v0.2.1
Jun 17, 2026
Merged

v0.2.1 — Security scanning, health grades, 7-view dashboard, CodeFlow-parity analysis, platform verification#6
FavioVazquez merged 67 commits into
mainfrom
v0.2.1

Conversation

@FavioVazquez

@FavioVazquez FavioVazquez commented Jun 11, 2026

Copy link
Copy Markdown
Owner

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 --prod clean.


npm distribution (makes the release shippable)

  • sprang is now a single self-contained npm package — CLI renamed @sprang/cli → unscoped sprang, @sprang/core bundled inline (tsup noExternal), all platform integration files included via the files whitelist. Published with provenance.
  • Standalone dashboard server (dist/dashboard-server.js) — route handlers extracted from vite.config.ts into a framework-agnostic registerRoutes() + http.createServer standalone server. sprang open works with no Vite/pnpm.
  • MCP server CJS standalone bundle (dist/mcp-server.cjs) — self-contained, runs via node mcp-server.cjs with no peer installs.
  • sprang init — writes/merges .mcp.json pointing at the bundled MCP server (--yes for non-interactive).
  • pack-assets.mjs — copies dashboard SPA, standalone server, MCP bundle, and LICENSE + README into the tarball.
  • Automated publish workflow (.github/workflows/publish.yml) — on v* tags: full CI → tag/version sync check (hard fail) → publish --provenance --access public via NPM_TOKEN.
  • Pack-and-install CI smoke test — packs, installs into a clean dir, asserts --version, scan --phase1-only, and the MCP initialize handshake.

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 in stats.security_summary, feed the health grade.
  • Health letter grade (A–F)calcHealthGrade() from 5 penalty factors (dead code, circular deps, god nodes, coupling, security); exposed via sprang_health (health_grade, health_score, grade_color, grade_breakdown, history).
  • Run history.sprang/history.json snapshot per scan (max 50, atomic write).
  • Function-to-function call graphcalls edges (internal + external), internalCalls/externalCalls/callerCount/isUnused per function.
  • 9 design-pattern detectors — added context_provider, decorator, event_emitter.
  • Architecture layer-violation detection — flags upward-dependency imports edges as layer_violation smells feeding the grade.
  • Mermaid diagram generation + sprang diagram CLI command.

Dashboard — 7 views + instant analysis

  • Landing screen (CodeFlow-style) — point-and-analyze: local path or GitHub URL, server-side validation, shallow clone to temp; sprang open --auto-scan.
  • 3D graph view (react-force-graph), Treemap view (d3-hierarchy), Matrix view (file adjacency) — bringing the dashboard to 7 views (Graph/Health/Domains/Architecture/Treemap/Matrix/Learn).
  • Health view — grade badge, score sparkline, security section, detected-patterns section.
  • Animation pass — spring counters, staggered table/card reveals, view transitions; full prefers-reduced-motion support.
  • "New analysis" button — return to landing from a loaded graph.
  • Complete persona systemsprang_tour accepts junior / senior / experienced / pm / non-technical.

Documentation

  • "The Leap" (Kierkegaard) section, "Not just codebases" callout, "What existing tools don't do" comparison, "Workflows in practice" (5 scenarios), supported-languages row, attributions (Understand Anything, CodeFlow), 7-view count corrected throughout.

Platform verification (real, not mocked)

  • Bridge e2e suite (8 tests) — boots preview servers with mock claude/copilot CLIs on PATH; really spawns, parses, persists sessions, writes responses. Covers detection priority, full ask pipeline, --resume continuity, Windsurf trigger-file protocol, session clearing.
  • Platform parity suite (22 tests) — locks cross-platform manifest/skill/rule/hook invariants across all 6 platform configs.
  • Windsurf hook real-execution tests (8 tests) — runs save-conversation.py via real python3.

Notable fixes

  • sprang --version read a hardcoded 0.1.0 → now reads package.json.
  • Background Phase 2 never ran (silent ENOENT on the forked worker path) → scans were skeleton-only; now resolves the correct path with inline fallback (phase: complete, layers/risk/security populated).
  • Landing-screen "instant analysis" did nothing (npx sprang 404'd) → CLI-resolution priority chain (local build → PATH → npx).
  • SecurityScannerAgent never ran in Phase 2 and read a never-populated field → wired into Group 1, switched to node.location.file.
  • 3D graph churned (graphData rebuilt every render; rotation interval leaked) → memoized + proper cleanup.
  • 2D pulse ring orphans — exit animation inherited repeat: Infinity so old rings never unmounted; position frozen on pan/zoom → finite exit transition + afterRender tracking.
  • merge.py — risk scores / domains / phase2_completed_at not applied.
  • sprang-analyze workflow — missing domain phase, wrong intermediate filenames, invalid resume phase value, flat tour array.
  • Installer next-steps, agentic install .devin/config.json path, e2e env leak (WINDSURF_CASCADE_TERMINAL_KIND), CommonJS require() imports, sprang query positional path.

Tooling & security hardening

  • ESLint flat config added + wired into CI (pnpm lint was previously broken); ~60 findings fixed.
  • Removed orphaned bin/install.js; tightened publish manifest-version check to a hard failure.
  • Patched transitive advisories via pnpm overrides (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.
  • Added MIT LICENSE (was referenced by files/package.json but missing); shipped in the tarball alongside README.

Release mechanics

Merging this PR puts v0.2.1 on main. Tagging v0.2.1 then triggers publish.yml (CI → version sync check → npm publish --provenance). All four package versions + both plugin manifests are pinned at 0.2.1; NPM_TOKEN is configured.

Note: the CHANGELOG's "Planned (pre-publish): standalone dashboard server" item was completed in this release (see "npm distribution") — safe to disregard as outstanding.

…to schema, fix subgraph merge, surface loader errors, remove dead GraphPhase value

https://claude.ai/code/session_0128XjiTpgrCTFgmNHF1SQuF
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
@FavioVazquez FavioVazquez self-assigned this Jun 13, 2026
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.
@FavioVazquez FavioVazquez merged commit 4d430cc into main Jun 17, 2026
6 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.

1 participant