feat: add Coderrr Insights dashboard for productivity tracking and Skills for better workflow#123
feat: add Coderrr Insights dashboard for productivity tracking and Skills for better workflow#123Akash-nath29 merged 6 commits intodevfrom
Conversation
Fix README Project Architecture
feat: add Coderrr Insights dashboard for productivity tracking
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🚀 Thanks for opening a Pull Request! A maintainer will review this soon. Meanwhile: Your contribution helps make this project better! |
There was a problem hiding this comment.
Pull request overview
This PR adds an Insights dashboard and metrics plumbing, strengthens file operation safety and backend planning rules, and updates command execution to run in separate terminals so Coderrr stays responsive. It also introduces project customization docs (Skills.md, Coderrr.md, and memory) and wires a new coderrr insights CLI command plus a basic Jest test for the insights module.
Changes:
- Implemented an
InsightsManagerwithinsightsUIand a newcoderrr insightsCLI subcommand, plus a Jest test for recording sessions. - Enhanced command execution via
executeInSeparateTerminal(Windows/macOS/Linux) and added process tracking/cleanup inAgent, while tightening file patching semantics and documentation in both the backend system prompt andfileOps. - Expanded README and added
docs/insights-guide.mdto document project customization, cross-session memory, and how to view insights; bumped package version to 1.1.1.
Reviewed changes
Copilot reviewed 11 out of 13 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test-custom-prompt.js | Fixes the Agent require path so the custom prompt test imports the correct module. |
| test/insights.test.js | Adds a Jest test that exercises insights.recordSession/getStats but currently writes to the real ~/.coderrr/insights.json. |
| src/utils/metrics.js | Introduces a calculateSavings helper for estimating time saved from sessions, though it is not yet used anywhere. |
| src/insightsUI.js | Implements a Chalk-based terminal dashboard to display totals and recent sessions from the insights store. |
| src/insights.js | Adds InsightsManager that persists sessions and aggregate totals to ~/.coderrr/insights.json with basic rotation. |
| src/fileOps.js | Documents and implements more flexible patching (line-ending normalization and fuzzy line-by-line matching) while enforcing protected paths for Coderrr config. |
| src/executor.js | Extends the command executor with executeInSeparateTerminal plus OS-specific terminal spawners and monitoring/termination utilities, and keeps legacy plan execution helpers. |
| src/agent.js | Tracks processes started in separate terminals for cleanup on exit and passes completed step context into self-healing prompts, updating validation for fixed steps. |
| package-lock.json | Updates the lockfile to version 1.1.1 and syncs dependency metadata with package.json. |
| docs/insights-guide.md | New guide describing Coderrr Insights features and instructing users to run coderrr insights to open the dashboard. |
| bin/coderrr.js | Wires a new insights subcommand that invokes displayInsights, leaving existing CLI commands untouched. |
| backend/main.py | Strengthens system instructions with protected file rules, explicit patch_file guidance, and an expanded JSON schema including oldContent/newContent. |
| README.md | Documents separate terminal execution, Skills/Coderrr customization, and cross-session memory behaviors to align with the new functionality. |
| end tell | ||
| `; | ||
|
|
||
| exec(`osascript -e '${appleScript}'`, (error, stdout, stderr) => { |
There was a problem hiding this comment.
The AppleScript command construction here is likely to break on macOS because the script is passed to osascript inside single quotes, but the script itself contains single-quoted path values (e.g. '${shellScript}', '${pidFile}'). When expanded, this results in unescaped single quotes inside a single-quoted shell string, causing the osascript -e '...' invocation to fail. To make this robust, either avoid single quotes inside the AppleScript string (use escaped double quotes for paths) or change how the script is passed to osascript so that path values are properly escaped for the surrounding shell quoting.
| exec(`osascript -e '${appleScript}'`, (error, stdout, stderr) => { | |
| const osaCommand = `osascript -e ${JSON.stringify(appleScript)}`; | |
| exec(osaCommand, (error, stdout, stderr) => { |
| const calculateSavings = (sessions) => { | ||
| if (!sessions || sessions.length === 0) return "0 minutes"; | ||
|
|
||
| // We estimate that each file operation or self-healing fix | ||
| // saves a developer roughly 10 minutes of manual work. | ||
| const ESTIMATED_MINS_SAVED_PER_ACTION = 10; | ||
|
|
||
| const totalActions = sessions.reduce((acc, session) => { | ||
| const fileOps = session.filesChanged || 0; | ||
| const healings = session.healings || 0; | ||
| return acc + fileOps + healings; | ||
| }, 0); | ||
|
|
||
| const totalMinutes = totalActions * ESTIMATED_MINS_SAVED_PER_ACTION; | ||
|
|
||
| if (totalMinutes >= 60) { | ||
| const hours = (totalMinutes / 60).toFixed(1); | ||
| return `${hours} hours`; | ||
| } | ||
|
|
||
| return `${totalMinutes} minutes`; | ||
| }; | ||
|
|
||
| module.exports = { | ||
| calculateSavings |
There was a problem hiding this comment.
This new metrics utility is not referenced anywhere in the codebase, so calculateSavings is effectively dead code right now. Either integrate it into the insights dashboard (e.g., to show estimated time saved) or remove the file until it is actually used, to keep the utils folder from accumulating unused helpers.
| const calculateSavings = (sessions) => { | |
| if (!sessions || sessions.length === 0) return "0 minutes"; | |
| // We estimate that each file operation or self-healing fix | |
| // saves a developer roughly 10 minutes of manual work. | |
| const ESTIMATED_MINS_SAVED_PER_ACTION = 10; | |
| const totalActions = sessions.reduce((acc, session) => { | |
| const fileOps = session.filesChanged || 0; | |
| const healings = session.healings || 0; | |
| return acc + fileOps + healings; | |
| }, 0); | |
| const totalMinutes = totalActions * ESTIMATED_MINS_SAVED_PER_ACTION; | |
| if (totalMinutes >= 60) { | |
| const hours = (totalMinutes / 60).toFixed(1); | |
| return `${hours} hours`; | |
| } | |
| return `${totalMinutes} minutes`; | |
| }; | |
| module.exports = { | |
| calculateSavings | |
| // Placeholder: metrics helpers can be added here when integrated | |
| // into the insights dashboard or other parts of the application. | |
| module.exports = { | |
| // Intentionally empty: add metrics utilities here when used. |
| const insights = require('../src/insights'); | ||
| const fs = require('fs'); | ||
|
|
||
| describe('Insights Module', () => { | ||
| test('should record and retrieve a session', () => { | ||
| const initialTasks = insights.getStats().totals.tasks; | ||
| insights.recordSession({ task: 'Test Task', success: true, filesChanged: 2, healings: 1 }); | ||
| const updatedStats = insights.getStats(); | ||
| expect(updatedStats.totals.tasks).toBe(initialTasks + 1); | ||
| }); |
There was a problem hiding this comment.
This Jest test writes to the real ~/.coderrr/insights.json file and never cleans it up, which means running the test suite mutates the user's actual insights data and makes the test dependent on prior runs. It would be better to make the storage path configurable (e.g., via an environment variable or DI) so tests can point insights at a temporary directory, and then clean up any test data; also note that the imported fs module is not used and can be removed.
|
|
||
| // Start monitoring output if requested | ||
| let outputWatcher = null; | ||
| let lastReadPosition = 0; |
There was a problem hiding this comment.
Unused variable lastReadPosition.
| let lastReadPosition = 0; |
| ui.info('The terminal window will show the command output.'); | ||
| } else { | ||
| const errorMsg = result.error || 'Unknown error'; | ||
| stepResult = `Failed to start command: "${step.command}". Error: ${errorMsg}`; |
There was a problem hiding this comment.
The value assigned to stepResult here is unused.
| stepResult = `Failed to start command: "${step.command}". Error: ${errorMsg}`; |
| let terminalProcess = null; | ||
| let childPid = null; | ||
|
|
||
| try { | ||
| if (platform === 'win32') { | ||
| // Windows: Use start command with cmd.exe | ||
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; | ||
| childPid = result.pid; | ||
| } else if (platform === 'darwin') { | ||
| // macOS: Use osascript to open Terminal.app | ||
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; | ||
| childPid = result.pid; | ||
| } else { | ||
| // Linux: Try gnome-terminal, konsole, or xterm | ||
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; |
There was a problem hiding this comment.
The value assigned to terminalProcess here is unused.
| let terminalProcess = null; | |
| let childPid = null; | |
| try { | |
| if (platform === 'win32') { | |
| // Windows: Use start command with cmd.exe | |
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| childPid = result.pid; | |
| } else if (platform === 'darwin') { | |
| // macOS: Use osascript to open Terminal.app | |
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| childPid = result.pid; | |
| } else { | |
| // Linux: Try gnome-terminal, konsole, or xterm | |
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| let childPid = null; | |
| try { | |
| if (platform === 'win32') { | |
| // Windows: Use start command with cmd.exe | |
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| childPid = result.pid; | |
| } else if (platform === 'darwin') { | |
| // macOS: Use osascript to open Terminal.app | |
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| childPid = result.pid; | |
| } else { | |
| // Linux: Try gnome-terminal, konsole, or xterm | |
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); |
| let terminalProcess = null; | ||
| let childPid = null; | ||
|
|
||
| try { | ||
| if (platform === 'win32') { | ||
| // Windows: Use start command with cmd.exe | ||
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; | ||
| childPid = result.pid; | ||
| } else if (platform === 'darwin') { | ||
| // macOS: Use osascript to open Terminal.app | ||
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; | ||
| childPid = result.pid; | ||
| } else { | ||
| // Linux: Try gnome-terminal, konsole, or xterm | ||
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; |
There was a problem hiding this comment.
The value assigned to terminalProcess here is unused.
| let terminalProcess = null; | |
| let childPid = null; | |
| try { | |
| if (platform === 'win32') { | |
| // Windows: Use start command with cmd.exe | |
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| childPid = result.pid; | |
| } else if (platform === 'darwin') { | |
| // macOS: Use osascript to open Terminal.app | |
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| childPid = result.pid; | |
| } else { | |
| // Linux: Try gnome-terminal, konsole, or xterm | |
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| let childPid = null; | |
| try { | |
| if (platform === 'win32') { | |
| // Windows: Use start command with cmd.exe | |
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| childPid = result.pid; | |
| } else if (platform === 'darwin') { | |
| // macOS: Use osascript to open Terminal.app | |
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| childPid = result.pid; | |
| } else { | |
| // Linux: Try gnome-terminal, konsole, or xterm | |
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); |
| let terminalProcess = null; | ||
| let childPid = null; | ||
|
|
||
| try { | ||
| if (platform === 'win32') { | ||
| // Windows: Use start command with cmd.exe | ||
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; | ||
| childPid = result.pid; | ||
| } else if (platform === 'darwin') { | ||
| // macOS: Use osascript to open Terminal.app | ||
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; | ||
| childPid = result.pid; | ||
| } else { | ||
| // Linux: Try gnome-terminal, konsole, or xterm | ||
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); | ||
| terminalProcess = result.process; |
There was a problem hiding this comment.
The value assigned to terminalProcess here is unused.
| let terminalProcess = null; | |
| let childPid = null; | |
| try { | |
| if (platform === 'win32') { | |
| // Windows: Use start command with cmd.exe | |
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| childPid = result.pid; | |
| } else if (platform === 'darwin') { | |
| // macOS: Use osascript to open Terminal.app | |
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| childPid = result.pid; | |
| } else { | |
| // Linux: Try gnome-terminal, konsole, or xterm | |
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| terminalProcess = result.process; | |
| let childPid = null; | |
| try { | |
| if (platform === 'win32') { | |
| // Windows: Use start command with cmd.exe | |
| const result = await this.spawnWindowsTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| childPid = result.pid; | |
| } else if (platform === 'darwin') { | |
| // macOS: Use osascript to open Terminal.app | |
| const result = await this.spawnMacTerminal(normalizedCommand, cwd, outputFile, pidFile); | |
| childPid = result.pid; | |
| } else { | |
| // Linux: Try gnome-terminal, konsole, or xterm | |
| const result = await this.spawnLinuxTerminal(normalizedCommand, cwd, outputFile, pidFile); |
This pull request introduces several major improvements to Coderrr, focusing on enhanced command execution, robust file operation rules, and expanded documentation for project customization and insights. The most significant updates include running shell commands in separate terminal windows to keep Coderrr responsive, enforcing strict rules for file patching (especially to protect critical configuration files), and providing detailed user documentation for customizing project behavior and tracking coding insights.
Command Execution and Process Management
executeInSeparateTerminal, preventing long-running or blocking commands from freezing Coderrr. The agent tracks running processes and ensures they are properly cleaned up on exit, improving stability and user experience. (src/agent.js[1] [2]src/agent.js[1] [2] [3] [4]File Operation Safety and Rules
Coderrr.md,Skills.md, and the.coderrr/directory—these can never be deleted, even if the user requests a full cleanup. (backend/main.py[1] [2]patch_fileactions are clarified and enforced: the old content must be an exact match from the file, and the agent must always read the file before patching. Guidance is provided on when to useupdate_fileversuspatch_fileto preserve user code. (backend/main.py[1] [2]Documentation and User Guidance
Skills.mdandCoderrr.mdfor persistent and task-specific instructions, as well as how cross-session memory works. (README.md[1] [2] [3]docs/insights-guide.mddocument is introduced, detailing how users can view analytics and productivity statistics for their Coderrr sessions. (docs/insights-guide.mddocs/insights-guide.mdR1-R14)User Experience Improvements
README.md[1] [2]src/fileOps.jsis documented to support fuzzy matching and normalization, reducing errors due to line ending or whitespace differences. (src/fileOps.jssrc/fileOps.jsR140-R145)These changes collectively make Coderrr safer, more robust, and easier to customize for advanced users, while also improving the reliability of file operations and command execution.