Skip to content

Commit edeaf10

Browse files
authored
🤖 Configure ESLint to only show errors, add manual dead code check (#352)
## Summary Configures ESLint to treat warnings as errors and adds a manual dead code checking command. Also removes 506 lines of actual dead code identified by ts-prune. ## Changes ### ESLint Configuration - Added `--max-warnings 0` flag to treat all warnings as errors - Fixed outstanding warnings in `ChatInput.tsx` (missing deps) and `telemetry/utils.ts` (unsafe any access) ### Dead Code Checking - Added `make check-deadcode` target for manual dead code detection using ts-prune - **Not included in `static-check`** - advisory only, not a hard requirement - Filters out test/debug/storybook files and legitimate public API exports ### Dead Code Removal (506 lines) - `TypewriterText` component (unused) - `ToolsTab` component (unused) - Duplicate git functions in `git.ts` (implementations exist in `gitService.ts`) - Various unused utility functions and types across the codebase ## Why Manual-Only Dead Code Checking? Automatic dead code checking in CI is too brittle: - Line-number-based allowlists break on any code change - Hard to distinguish legitimate public API exports from actual dead code - Some exports are intentionally unused (public API surface, type definitions) - Dead code is technical debt but not a correctness issue Manual checking with `make check-deadcode` is still valuable for cleanup efforts without blocking CI. _Generated with `cmux`_
1 parent 88daab3 commit edeaf10

File tree

22 files changed

+30
-506
lines changed

22 files changed

+30
-506
lines changed

‎Makefile‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,17 @@ typecheck: node_modules/.installed src/version.ts ## Run TypeScript type checkin
155155
"$(TSGO) --noEmit" \
156156
"$(TSGO) --noEmit -p tsconfig.main.json"
157157

158+
check-deadcode: node_modules/.installed ## Check for potential dead code (manual only, not in static-check)
159+
@echo "Checking for potential dead code with ts-prune..."
160+
@echo "(Note: Some unused exports are legitimate - types, public APIs, entry points, etc.)"
161+
@echo ""
162+
@bun x ts-prune -i '(test|spec|mock|bench|debug|storybook)' \
163+
| grep -v "used in module" \
164+
| grep -v "src/App.tsx.*default" \
165+
| grep -v "src/types/" \
166+
| grep -v "telemetry/index.ts" \
167+
|| echo "✓ No obvious dead code found"
168+
158169
## Testing
159170
test-integration: node_modules/.installed ## Run all tests (unit + integration)
160171
@bun test src

‎scripts/lint.sh‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ ESLINT_PATTERN='src/**/*.{ts,tsx}'
2626

2727
if [ "$1" = "--fix" ]; then
2828
echo "Running bun x eslint with --fix..."
29-
bun x eslint --cache "$ESLINT_PATTERN" --fix
29+
bun x eslint --cache --max-warnings 0 "$ESLINT_PATTERN" --fix
3030
else
3131
echo "Running eslint..."
32-
bun x eslint --cache "$ESLINT_PATTERN"
32+
bun x eslint --cache --max-warnings 0 "$ESLINT_PATTERN"
3333
echo "ESLint checks passed!"
3434
fi

‎src/components/ChatInput.tsx‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -227,21 +227,24 @@ export const ChatInput: React.FC<ChatInputProps> = ({
227227
// Method to restore text to input (used by compaction cancel)
228228
const restoreText = useCallback(
229229
(text: string) => {
230-
setInput(text);
230+
setInput(() => text);
231231
focusMessageInput();
232232
},
233-
[focusMessageInput]
233+
[focusMessageInput, setInput]
234234
);
235235

236236
// Method to append text to input (used by Code Review notes)
237-
const appendText = useCallback((text: string) => {
238-
setInput((prev) => {
239-
// Add blank line before if there's existing content
240-
const separator = prev.trim() ? "\n\n" : "";
241-
return prev + separator + text;
242-
});
243-
// Don't focus - user wants to keep reviewing
244-
}, []);
237+
const appendText = useCallback(
238+
(text: string) => {
239+
setInput((prev) => {
240+
// Add blank line before if there's existing content
241+
const separator = prev.trim() ? "\n\n" : "";
242+
return prev + separator + text;
243+
});
244+
// Don't focus - user wants to keep reviewing
245+
},
246+
[setInput]
247+
);
245248

246249
// Provide API to parent via callback
247250
useEffect(() => {

‎src/components/Messages/MarkdownRenderer.tsx‎

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,3 @@ export const PlanMarkdownContainer = styled.div`
3232
color: var(--color-plan-mode-hover);
3333
}
3434
`;
35-
36-
interface PlanMarkdownRendererProps {
37-
content: string;
38-
className?: string;
39-
}
40-
41-
export const PlanMarkdownRenderer: React.FC<PlanMarkdownRendererProps> = ({
42-
content,
43-
className,
44-
}) => {
45-
return (
46-
<PlanMarkdownContainer className={className}>
47-
<MarkdownCore content={content} />
48-
</PlanMarkdownContainer>
49-
);
50-
};

‎src/components/Messages/TypewriterText.tsx‎

Lines changed: 0 additions & 113 deletions
This file was deleted.

‎src/components/RightSidebar/ToolsTab.tsx‎

Lines changed: 0 additions & 25 deletions
This file was deleted.

‎src/git.ts‎

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -153,99 +153,6 @@ export async function createWorktree(
153153
}
154154
}
155155

156-
export async function removeWorktree(
157-
projectPath: string,
158-
workspacePath: string,
159-
options: { force: boolean } = { force: false }
160-
): Promise<WorktreeResult> {
161-
try {
162-
// Remove the worktree (from the main repository context)
163-
using proc = execAsync(
164-
`git -C "${projectPath}" worktree remove "${workspacePath}" ${options.force ? "--force" : ""}`
165-
);
166-
await proc.result;
167-
return { success: true };
168-
} catch (error) {
169-
const message = error instanceof Error ? error.message : String(error);
170-
return { success: false, error: message };
171-
}
172-
}
173-
174-
export async function pruneWorktrees(projectPath: string): Promise<WorktreeResult> {
175-
try {
176-
using proc = execAsync(`git -C "${projectPath}" worktree prune`);
177-
await proc.result;
178-
return { success: true };
179-
} catch (error) {
180-
const message = error instanceof Error ? error.message : String(error);
181-
return { success: false, error: message };
182-
}
183-
}
184-
185-
export async function moveWorktree(
186-
projectPath: string,
187-
oldPath: string,
188-
newPath: string
189-
): Promise<WorktreeResult> {
190-
try {
191-
// Check if new path already exists
192-
if (fs.existsSync(newPath)) {
193-
return {
194-
success: false,
195-
error: `Target path already exists: ${newPath}`,
196-
};
197-
}
198-
199-
// Create parent directory for new path if needed
200-
const parentDir = path.dirname(newPath);
201-
if (!fs.existsSync(parentDir)) {
202-
fs.mkdirSync(parentDir, { recursive: true });
203-
}
204-
205-
// Move the worktree using git (from the main repository context)
206-
using proc = execAsync(`git -C "${projectPath}" worktree move "${oldPath}" "${newPath}"`);
207-
await proc.result;
208-
return { success: true, path: newPath };
209-
} catch (error) {
210-
const message = error instanceof Error ? error.message : String(error);
211-
return { success: false, error: message };
212-
}
213-
}
214-
215-
export async function listWorktrees(projectPath: string): Promise<string[]> {
216-
try {
217-
using proc = execAsync(`git -C "${projectPath}" worktree list --porcelain`);
218-
const { stdout } = await proc.result;
219-
const worktrees: string[] = [];
220-
const lines = stdout.split("\n");
221-
222-
for (const line of lines) {
223-
if (line.startsWith("worktree ")) {
224-
const path = line.slice("worktree ".length);
225-
if (path !== projectPath) {
226-
// Exclude main worktree
227-
worktrees.push(path);
228-
}
229-
}
230-
}
231-
232-
return worktrees;
233-
} catch (error) {
234-
console.error("Error listing worktrees:", error);
235-
return [];
236-
}
237-
}
238-
239-
export async function isGitRepository(projectPath: string): Promise<boolean> {
240-
try {
241-
using proc = execAsync(`git -C "${projectPath}" rev-parse --git-dir`);
242-
await proc.result;
243-
return true;
244-
} catch {
245-
return false;
246-
}
247-
}
248-
249156
/**
250157
* Get the main repository path from a worktree path
251158
* @param worktreePath Path to a git worktree

‎src/services/tools/fileCommon.ts‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as crypto from "crypto";
21
import type * as fs from "fs";
32
import * as path from "path";
43
import { createPatch } from "diff";
@@ -19,15 +18,7 @@ export const MAX_FILE_SIZE = 1024 * 1024; // 1MB
1918
* Compute a 6-character hexadecimal lease from file content.
2019
* The lease changes when file content is modified.
2120
* Uses a deterministic hash so leases are consistent across processes.
22-
*
23-
* @param content - File content as string or Buffer
24-
* @returns 6-character hexadecimal lease string
2521
*/
26-
export function leaseFromContent(content: string | Buffer): string {
27-
// Use deterministic SHA-256 hash of content so leases are consistent
28-
// across processes and restarts
29-
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 6);
30-
}
3122

3223
/**
3324
* Generate a unified diff between old and new content using jsdiff.

‎src/telemetry/utils.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import { VERSION } from "../version";
99
* Get base telemetry properties included with all events
1010
*/
1111
export function getBaseTelemetryProperties(): BaseTelemetryProperties {
12+
const gitDescribe: string = VERSION.git_describe;
1213
return {
13-
version: VERSION.git_describe,
14+
version: gitDescribe,
1415
platform: window.api?.platform || "unknown",
1516
electronVersion: window.api?.versions?.electron || "unknown",
1617
};

‎src/types/errors.ts‎

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,6 @@ export type SendMessageError =
1414
| { type: "invalid_model_string"; message: string }
1515
| { type: "unknown"; raw: string };
1616

17-
/**
18-
* Type guard to check if error is an API key error
19-
*/
20-
export function isApiKeyError(
21-
error: SendMessageError
22-
): error is { type: "api_key_not_found"; provider: string } {
23-
return error.type === "api_key_not_found";
24-
}
25-
26-
/**
27-
* Type guard to check if error is an unknown error
28-
*/
29-
export function isUnknownError(error: SendMessageError): error is { type: "unknown"; raw: string } {
30-
return error.type === "unknown";
31-
}
32-
3317
/**
3418
* Stream error types - categorizes errors during AI streaming
3519
* Used across backend (StreamManager) and frontend (StreamErrorMessage)

0 commit comments

Comments
 (0)