fecode-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +33 -0
- package/dist/App.js +2224 -0
- package/dist/approvalResolver.d.ts +12 -0
- package/dist/approvalResolver.js +112 -0
- package/dist/commands.d.ts +11 -0
- package/dist/commands.js +33 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +15175 -0
- package/dist/ui/AppShell.d.ts +14 -0
- package/dist/ui/AppShell.js +25 -0
- package/dist/ui/ApprovalPrompt.d.ts +31 -0
- package/dist/ui/ApprovalPrompt.js +55 -0
- package/dist/ui/BlockedView.d.ts +13 -0
- package/dist/ui/BlockedView.js +7 -0
- package/dist/ui/CommandPalette.d.ts +8 -0
- package/dist/ui/CommandPalette.js +18 -0
- package/dist/ui/CurrentStepView.d.ts +16 -0
- package/dist/ui/CurrentStepView.js +28 -0
- package/dist/ui/DiagnosticsView.d.ts +45 -0
- package/dist/ui/DiagnosticsView.js +23 -0
- package/dist/ui/ExecutionTimeline.d.ts +15 -0
- package/dist/ui/ExecutionTimeline.js +51 -0
- package/dist/ui/ExecutionView.d.ts +42 -0
- package/dist/ui/ExecutionView.js +20 -0
- package/dist/ui/Header.d.ts +15 -0
- package/dist/ui/Header.js +47 -0
- package/dist/ui/HelpView.d.ts +3 -0
- package/dist/ui/HelpView.js +35 -0
- package/dist/ui/MessageBubble.d.ts +9 -0
- package/dist/ui/MessageBubble.js +58 -0
- package/dist/ui/PlanStep.d.ts +16 -0
- package/dist/ui/PlanStep.js +71 -0
- package/dist/ui/PlanView.d.ts +26 -0
- package/dist/ui/PlanView.js +24 -0
- package/dist/ui/ProgressBar.d.ts +10 -0
- package/dist/ui/ProgressBar.js +14 -0
- package/dist/ui/RecoveryView.d.ts +20 -0
- package/dist/ui/RecoveryView.js +19 -0
- package/dist/ui/ReplanView.d.ts +16 -0
- package/dist/ui/ReplanView.js +21 -0
- package/dist/ui/ResumeView.d.ts +15 -0
- package/dist/ui/ResumeView.js +25 -0
- package/dist/ui/RiskNotice.d.ts +10 -0
- package/dist/ui/RiskNotice.js +21 -0
- package/dist/ui/RunHistoryView.d.ts +15 -0
- package/dist/ui/RunHistoryView.js +39 -0
- package/dist/ui/StatusBar.d.ts +16 -0
- package/dist/ui/StatusBar.js +104 -0
- package/dist/ui/TaskInput.d.ts +15 -0
- package/dist/ui/TaskInput.js +8 -0
- package/dist/ui/ThinkingBlock.d.ts +8 -0
- package/dist/ui/ThinkingBlock.js +13 -0
- package/dist/ui/ThinkingIndicator.d.ts +7 -0
- package/dist/ui/ThinkingIndicator.js +20 -0
- package/dist/ui/TurnView.d.ts +13 -0
- package/dist/ui/TurnView.js +11 -0
- package/dist/ui/WorkspaceStatus.d.ts +20 -0
- package/dist/ui/WorkspaceStatus.js +23 -0
- package/dist/ui/index.d.ts +26 -0
- package/dist/ui/index.js +26 -0
- package/package.json +42 -0
- package/skills/accessibility/SKILL.md +98 -0
- package/skills/css/SKILL.md +90 -0
- package/skills/frontend-debugging/SKILL.md +201 -0
- package/skills/frontend-design/SKILL.md +358 -0
- package/skills/frontend-performance/SKILL.md +89 -0
- package/skills/frontend-testing/SKILL.md +87 -0
- package/skills/nextjs/SKILL.md +75 -0
- package/skills/react/SKILL.md +213 -0
- package/skills/responsive-design/SKILL.md +190 -0
- package/skills/svelte/SKILL.md +86 -0
- package/skills/tailwind/SKILL.md +74 -0
- package/skills/ui-review/SKILL.md +245 -0
- package/skills/vue/SKILL.md +72 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
export const ThinkingBlock = ({ durationMs, tokenCount, summary }) => {
|
|
4
|
+
if (!durationMs || isNaN(durationMs) || durationMs <= 0)
|
|
5
|
+
return null;
|
|
6
|
+
const seconds = (durationMs / 1000).toFixed(1);
|
|
7
|
+
const tokenText = typeof tokenCount === "number" && !isNaN(tokenCount) && tokenCount > 0
|
|
8
|
+
? `, ${tokenCount} tokens`
|
|
9
|
+
: "";
|
|
10
|
+
const header = `Thought for ${seconds}s${tokenText}`;
|
|
11
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: "gray", dimColor: true, children: "\u25B8 " }), _jsx(Text, { color: "gray", dimColor: true, italic: true, children: header })] }), summary && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", dimColor: true, italic: true, wrap: "wrap", children: summary.split("\n")[0] }) }))] }));
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=ThinkingBlock.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
5
|
+
const INTERVAL_MS = 80;
|
|
6
|
+
export const ThinkingIndicator = ({ isActive, label = "Working..." }) => {
|
|
7
|
+
const [frame, setFrame] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (!isActive)
|
|
10
|
+
return;
|
|
11
|
+
const timer = setInterval(() => {
|
|
12
|
+
setFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
|
|
13
|
+
}, INTERVAL_MS);
|
|
14
|
+
return () => clearInterval(timer);
|
|
15
|
+
}, [isActive]);
|
|
16
|
+
if (!isActive)
|
|
17
|
+
return null;
|
|
18
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: "cyan", children: [SPINNER_FRAMES[frame], " "] }), _jsx(Text, { color: "cyan", children: label })] }));
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=ThinkingIndicator.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface TurnViewProps {
|
|
3
|
+
prompt: string;
|
|
4
|
+
response: string;
|
|
5
|
+
status: "thinking" | "streaming" | "done" | "error" | "cancelled";
|
|
6
|
+
error?: string;
|
|
7
|
+
isLast?: boolean;
|
|
8
|
+
thinkingMs?: number;
|
|
9
|
+
thinkingTokens?: number;
|
|
10
|
+
thinkingSummary?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const TurnView: React.FC<TurnViewProps>;
|
|
13
|
+
//# sourceMappingURL=TurnView.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { MessageBubble } from "./MessageBubble.js";
|
|
4
|
+
import { ThinkingBlock } from "./ThinkingBlock.js";
|
|
5
|
+
const SEPARATOR = "─".repeat(48);
|
|
6
|
+
export const TurnView = ({ prompt, response, status, error, isLast = false, thinkingMs, thinkingTokens, thinkingSummary }) => {
|
|
7
|
+
const isStreaming = status === "streaming" || status === "thinking";
|
|
8
|
+
const hasError = status === "error";
|
|
9
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [_jsx(MessageBubble, { role: "user", content: prompt }), _jsx(Box, { height: 0 }), thinkingMs !== undefined && thinkingMs > 0 && (_jsx(ThinkingBlock, { durationMs: thinkingMs, tokenCount: thinkingTokens, summary: thinkingSummary })), _jsx(MessageBubble, { role: "agent", content: response, isStreaming: isStreaming && !response && !hasError, error: hasError ? (error || "An error occurred.") : undefined }), status === "cancelled" && (_jsx(Box, { marginLeft: 2, marginTop: 0, children: _jsx(Text, { color: "gray", dimColor: true, children: "\u2298 Cancelled" }) })), !isLast && (_jsx(Box, { marginTop: 1, marginBottom: 0, children: _jsx(Text, { color: "gray", dimColor: true, children: SEPARATOR }) }))] }));
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=TurnView.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface WorkspaceStatusProps {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
branch?: string | null;
|
|
5
|
+
isClean?: boolean;
|
|
6
|
+
riskLevel?: string;
|
|
7
|
+
checkpointRequired?: boolean;
|
|
8
|
+
approvalRequired?: boolean;
|
|
9
|
+
modifiedFiles?: string[];
|
|
10
|
+
stagedFiles?: string[];
|
|
11
|
+
untrackedFiles?: string[];
|
|
12
|
+
hasDrift?: boolean;
|
|
13
|
+
driftReason?: string;
|
|
14
|
+
isStale?: boolean;
|
|
15
|
+
staleReason?: string;
|
|
16
|
+
reconciliationState?: string;
|
|
17
|
+
formattedOutput?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const WorkspaceStatus: React.FC<WorkspaceStatusProps>;
|
|
20
|
+
//# sourceMappingURL=WorkspaceStatus.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
export const WorkspaceStatus = ({ cwd = process.cwd(), branch, isClean = true, riskLevel = "low", checkpointRequired = false, approvalRequired = false, modifiedFiles = [], stagedFiles = [], untrackedFiles = [], hasDrift = false, driftReason, isStale = false, staleReason, reconciliationState, formattedOutput }) => {
|
|
4
|
+
if (formattedOutput) {
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, marginY: 1, children: [_jsx(Box, { marginBottom: 0, children: _jsx(Text, { bold: true, color: "cyan", children: "Git & Workspace Status" }) }), _jsx(Text, { color: "white", children: formattedOutput })] }));
|
|
6
|
+
}
|
|
7
|
+
const getRiskColor = (risk) => {
|
|
8
|
+
switch (risk.toLowerCase()) {
|
|
9
|
+
case "critical":
|
|
10
|
+
return "red";
|
|
11
|
+
case "elevated":
|
|
12
|
+
case "high":
|
|
13
|
+
return "yellow";
|
|
14
|
+
case "low":
|
|
15
|
+
return "green";
|
|
16
|
+
case "normal":
|
|
17
|
+
default:
|
|
18
|
+
return "cyan";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, marginY: 1, children: [_jsxs(Box, { justifyContent: "space-between", marginBottom: 0, children: [_jsx(Text, { bold: true, color: "cyan", children: "Git & Workspace Status" }), _jsxs(Box, { children: [_jsx(Text, { color: "gray", children: "Risk: " }), _jsx(Text, { bold: true, color: getRiskColor(riskLevel), children: riskLevel.toUpperCase() })] })] }), _jsxs(Box, { marginTop: 0, children: [_jsx(Text, { color: "gray", children: "Directory: " }), _jsx(Text, { color: "white", children: cwd })] }), branch !== undefined && (_jsxs(Box, { marginTop: 0, justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: "gray", children: "Branch: " }), _jsx(Text, { color: "cyan", children: branch || "(detached / none)" }), _jsx(Text, { color: "gray", children: " | State: " }), _jsx(Text, { color: isClean ? "green" : "yellow", children: isClean ? "clean" : "modified" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "gray", children: "Checkpoint: " }), _jsx(Text, { color: checkpointRequired ? "yellow" : "gray", children: checkpointRequired ? "REQUIRED" : "OPTIONAL" }), _jsx(Text, { color: "gray", children: " | Approval: " }), _jsx(Text, { color: approvalRequired ? "yellow" : "gray", children: approvalRequired ? "REQUIRED" : "NONE" })] })] })), hasDrift && (_jsx(Box, { marginTop: 0, children: _jsxs(Text, { color: "yellow", bold: true, children: ["\u26A0 Workspace drift: ", driftReason || "State changed outside session"] }) })), isStale && (_jsx(Box, { marginTop: 0, children: _jsxs(Text, { color: "red", bold: true, children: ["\u26A0 Plan stale: ", staleReason || "Prerequisites or files modified"] }) })), reconciliationState && (_jsxs(Box, { marginTop: 0, children: [_jsx(Text, { color: "gray", children: "Reconciliation: " }), _jsx(Text, { color: "white", children: reconciliationState })] })), modifiedFiles.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 0, children: [_jsxs(Text, { color: "gray", children: ["Modified (", modifiedFiles.length, "):"] }), modifiedFiles.slice(0, 4).map((f, i) => (_jsxs(Text, { color: "yellow", children: [" ", "M ", f] }, `mod-${i}`))), modifiedFiles.length > 4 && (_jsxs(Text, { color: "gray", children: [" ... and ", modifiedFiles.length - 4, " more"] }))] })), stagedFiles.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 0, children: [_jsxs(Text, { color: "gray", children: ["Staged (", stagedFiles.length, "):"] }), stagedFiles.slice(0, 4).map((f, i) => (_jsxs(Text, { color: "green", children: [" ", "A ", f] }, `stg-${i}`)))] })), untrackedFiles.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 0, children: [_jsxs(Text, { color: "gray", children: ["Untracked (", untrackedFiles.length, "):"] }), untrackedFiles.slice(0, 4).map((f, i) => (_jsxs(Text, { color: "gray", children: [" ", "? ", f] }, `unt-${i}`)))] }))] }));
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=WorkspaceStatus.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * from "./Header.js";
|
|
2
|
+
export * from "./StatusBar.js";
|
|
3
|
+
export * from "./AppShell.js";
|
|
4
|
+
export * from "./ProgressBar.js";
|
|
5
|
+
export * from "./CurrentStepView.js";
|
|
6
|
+
export * from "./TaskInput.js";
|
|
7
|
+
export * from "./PlanStep.js";
|
|
8
|
+
export * from "./PlanView.js";
|
|
9
|
+
export * from "./ExecutionView.js";
|
|
10
|
+
export * from "./ExecutionTimeline.js";
|
|
11
|
+
export * from "./ApprovalPrompt.js";
|
|
12
|
+
export * from "./RiskNotice.js";
|
|
13
|
+
export * from "./BlockedView.js";
|
|
14
|
+
export * from "./RecoveryView.js";
|
|
15
|
+
export * from "./ReplanView.js";
|
|
16
|
+
export * from "./ResumeView.js";
|
|
17
|
+
export * from "./DiagnosticsView.js";
|
|
18
|
+
export * from "./RunHistoryView.js";
|
|
19
|
+
export * from "./WorkspaceStatus.js";
|
|
20
|
+
export * from "./HelpView.js";
|
|
21
|
+
export * from "./ThinkingIndicator.js";
|
|
22
|
+
export * from "./MessageBubble.js";
|
|
23
|
+
export * from "./TurnView.js";
|
|
24
|
+
export * from "./CommandPalette.js";
|
|
25
|
+
export * from "./ThinkingBlock.js";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * from "./Header.js";
|
|
2
|
+
export * from "./StatusBar.js";
|
|
3
|
+
export * from "./AppShell.js";
|
|
4
|
+
export * from "./ProgressBar.js";
|
|
5
|
+
export * from "./CurrentStepView.js";
|
|
6
|
+
export * from "./TaskInput.js";
|
|
7
|
+
export * from "./PlanStep.js";
|
|
8
|
+
export * from "./PlanView.js";
|
|
9
|
+
export * from "./ExecutionView.js";
|
|
10
|
+
export * from "./ExecutionTimeline.js";
|
|
11
|
+
export * from "./ApprovalPrompt.js";
|
|
12
|
+
export * from "./RiskNotice.js";
|
|
13
|
+
export * from "./BlockedView.js";
|
|
14
|
+
export * from "./RecoveryView.js";
|
|
15
|
+
export * from "./ReplanView.js";
|
|
16
|
+
export * from "./ResumeView.js";
|
|
17
|
+
export * from "./DiagnosticsView.js";
|
|
18
|
+
export * from "./RunHistoryView.js";
|
|
19
|
+
export * from "./WorkspaceStatus.js";
|
|
20
|
+
export * from "./HelpView.js";
|
|
21
|
+
export * from "./ThinkingIndicator.js";
|
|
22
|
+
export * from "./MessageBubble.js";
|
|
23
|
+
export * from "./TurnView.js";
|
|
24
|
+
export * from "./CommandPalette.js";
|
|
25
|
+
export * from "./ThinkingBlock.js";
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fecode-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "FeCode - Interactive Terminal Coding Assistant",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fe": "./dist/index.js",
|
|
8
|
+
"fecode": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**/*.js",
|
|
12
|
+
"dist/**/*.d.ts",
|
|
13
|
+
"!dist/**/*.map",
|
|
14
|
+
"skills"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20.0.0"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"fe": "tsx src/index.tsx",
|
|
21
|
+
"dev": "tsx src/index.tsx",
|
|
22
|
+
"build": "node bundle.mjs",
|
|
23
|
+
"test": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@google/genai": "^2.16.0",
|
|
27
|
+
"dotenv": "^16.6.1",
|
|
28
|
+
"ink": "^5.1.0",
|
|
29
|
+
"ink-text-input": "^6.0.0",
|
|
30
|
+
"openai": "^4.77.0",
|
|
31
|
+
"react": "^18.3.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@fecode/agent": "*",
|
|
35
|
+
"@fecode/models": "*",
|
|
36
|
+
"@fecode/shared": "*",
|
|
37
|
+
"@types/react": "^18.3.12",
|
|
38
|
+
"ink-testing-library": "^4.0.0",
|
|
39
|
+
"tsx": "^4.19.2",
|
|
40
|
+
"typescript": "^5.7.2"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accessibility
|
|
3
|
+
description: Practical web accessibility implementation, ARIA semantics, keyboard navigation, and focus management.
|
|
4
|
+
category: accessibility
|
|
5
|
+
version: 2.1.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- building interactive components
|
|
11
|
+
- fixing accessibility defects
|
|
12
|
+
- writing semantic HTML
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
### Project Detection & Existing Rules
|
|
17
|
+
- **Inspect package.json & Configuration**: Check if the project uses specific accessibility linters (`eslint-plugin-jsx-a11y`) or UI component libraries (like Radix or Headless UI) that handle accessibility primitives.
|
|
18
|
+
- **Follow existing patterns**: Integrate with existing accessibility patterns in the project.
|
|
19
|
+
|
|
20
|
+
### Core Mental Model
|
|
21
|
+
- Accessibility means the interface remains usable across keyboard, screen readers, different input methods, visual abilities, cognitive contexts, and motion preferences.
|
|
22
|
+
- It is not merely "use semantic HTML"—it is about communicating state, identity, and behavior to all users.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
### Semantic HTML
|
|
27
|
+
Before adding ARIA:
|
|
28
|
+
- Ask whether native HTML already provides the correct semantics.
|
|
29
|
+
- Prefer native elements (`<button>`, `<nav>`, `<main>`) over recreating them with `<div>` and ARIA roles.
|
|
30
|
+
|
|
31
|
+
### Buttons vs Links
|
|
32
|
+
- Use **buttons** (`<button>`) for actions that change state or trigger behavior on the page.
|
|
33
|
+
- Use **links** (`<a>`) for navigation to new URLs or anchors.
|
|
34
|
+
- Do not create clickable divs when a native element exists.
|
|
35
|
+
|
|
36
|
+
### Keyboard
|
|
37
|
+
- Every interactive feature must have an intentional keyboard interaction.
|
|
38
|
+
- Check tab order (`tabindex="0"` for interactive elements only, avoid positive `tabindex`).
|
|
39
|
+
- Check activation (Space/Enter).
|
|
40
|
+
- Verify focus visibility.
|
|
41
|
+
- Prevent keyboard traps.
|
|
42
|
+
|
|
43
|
+
### Focus
|
|
44
|
+
Before removing outlines or focus indicators:
|
|
45
|
+
- Provide an equivalent, clearly visible focus state for keyboard users (`:focus-visible`).
|
|
46
|
+
|
|
47
|
+
### Forms
|
|
48
|
+
- Ensure inputs have associated labels (`<label for="...">` or `aria-labelledby`).
|
|
49
|
+
- Associate error messaging with inputs using `aria-describedby` or `aria-errormessage`.
|
|
50
|
+
- Provide useful validation messaging and indicate required state (`aria-required="true"` or `required` attribute).
|
|
51
|
+
|
|
52
|
+
### ARIA
|
|
53
|
+
- Use ARIA to communicate semantics that native HTML cannot express (e.g. `aria-expanded`, `aria-controls`).
|
|
54
|
+
- **Do not add redundant ARIA** (e.g. `<button role="button">`).
|
|
55
|
+
- **Do not use ARIA to repair incorrect markup** when simply changing to the correct native HTML tag solves the problem.
|
|
56
|
+
|
|
57
|
+
### Dynamic UI
|
|
58
|
+
- For dialogs, menus, popovers, and loading states: focus management must be intentional (e.g., trapping focus inside a modal, returning focus to the trigger on close).
|
|
59
|
+
- Use live regions (`aria-live`) for dynamic validation errors or status updates.
|
|
60
|
+
|
|
61
|
+
### Motion
|
|
62
|
+
- Respect `prefers-reduced-motion` media queries for animations.
|
|
63
|
+
- Do not make motion the only way information is communicated.
|
|
64
|
+
|
|
65
|
+
### Color
|
|
66
|
+
- Do not communicate meaning through color alone (e.g., use an icon or text alongside a red border for an error state).
|
|
67
|
+
- Ensure sufficient color contrast.
|
|
68
|
+
|
|
69
|
+
## Anti-Patterns
|
|
70
|
+
|
|
71
|
+
- **Clickable divs**
|
|
72
|
+
- *What*: `<div onClick={handleClick}>Submit</div>`.
|
|
73
|
+
- *Why*: Divs are not focusable by default and do not respond to Enter/Space keys, making them inaccessible to keyboard/screen reader users.
|
|
74
|
+
- *Instead*: Use a `<button>`.
|
|
75
|
+
- **Missing labels**
|
|
76
|
+
- *What*: `<input type="text" placeholder="Search" />` without a label.
|
|
77
|
+
- *Why*: Screen readers may not read the placeholder, leaving users blind to the input's purpose.
|
|
78
|
+
- *Instead*: Provide a visible `<label>` or `aria-label`.
|
|
79
|
+
- **Invisible focus**
|
|
80
|
+
- *What*: `outline: none;` without providing a replacement `:focus` or `:focus-visible` style.
|
|
81
|
+
- *Why*: Keyboard users lose track of where they are on the page.
|
|
82
|
+
- *Instead*: Keep the default outline or provide a high-contrast custom focus ring.
|
|
83
|
+
- **Unnecessary ARIA**
|
|
84
|
+
- *What*: Adding `role="navigation"` to a `<nav>` element.
|
|
85
|
+
- *Why*: It is redundant and adds noise for screen readers.
|
|
86
|
+
- *Instead*: Just use `<nav>`.
|
|
87
|
+
|
|
88
|
+
## Workflow
|
|
89
|
+
|
|
90
|
+
### Debugging
|
|
91
|
+
- Determine if the issue is a missing semantic element, missing keyboard support, or incorrect ARIA state.
|
|
92
|
+
- Inspect the Accessibility Tree in the browser (if available).
|
|
93
|
+
|
|
94
|
+
### Verification
|
|
95
|
+
- Verify keyboard navigation manually or via tests.
|
|
96
|
+
- Inspect semantic markup.
|
|
97
|
+
- Run automated accessibility checks (`eslint-plugin-jsx-a11y`, `axe-core`) where available in the project.
|
|
98
|
+
- Do not claim automated accessibility verification succeeded unless a real tool was executed.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: css
|
|
3
|
+
description: Cascading Style Sheets architecture, layout, specificty, and modern responsive mechanics.
|
|
4
|
+
category: styling
|
|
5
|
+
version: 2.1.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- writing CSS
|
|
11
|
+
- modifying layouts
|
|
12
|
+
- resolving visual defects
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
### Project Detection & Existing Rules
|
|
17
|
+
- **Inspect package.json & Stylesheets**: Determine if the project uses plain CSS, CSS Modules, Sass/Less, or utility frameworks (e.g. Tailwind).
|
|
18
|
+
- **Follow existing patterns**: Do not introduce a styling architecture that the project does not use (like dropping in Tailwind classes into a CSS Modules project).
|
|
19
|
+
- **Respect naming conventions**: Use existing class naming conventions (e.g. BEM).
|
|
20
|
+
|
|
21
|
+
### Core Mental Model
|
|
22
|
+
- **Cascade & Inheritance**: Rules apply based on cascade origin, specificity, and source order. Properties like color and font inherit, while layout properties do not.
|
|
23
|
+
- **Specificity**: Browsers calculate selector weight. Inline > ID > Class/Attribute/Pseudo-class > Element/Pseudo-element.
|
|
24
|
+
- **Source Order**: Later rules win when specificity is equal.
|
|
25
|
+
- **Box Model**: Elements have content, padding, border, and margin. Use `box-sizing: border-box`.
|
|
26
|
+
- **Containing Blocks**: Absolute positioning is relative to the nearest positioned ancestor.
|
|
27
|
+
- **Layout vs Decoration**: Separate properties that affect document flow (flex, grid, width) from decorative properties (color, background).
|
|
28
|
+
|
|
29
|
+
## Rules
|
|
30
|
+
|
|
31
|
+
### Layout
|
|
32
|
+
Before using `position: absolute`:
|
|
33
|
+
- Ask whether normal document flow, flexbox, or CSS grid solves the layout more robustly.
|
|
34
|
+
|
|
35
|
+
### Flexbox vs Grid
|
|
36
|
+
- **Flexbox**: Use for 1-dimensional layouts (a row or a column) where content dictates the size.
|
|
37
|
+
- **Grid**: Use for 2-dimensional layouts where the structural grid dictates the layout.
|
|
38
|
+
|
|
39
|
+
### Fixed vs Fluid
|
|
40
|
+
- Prefer fluid constraints (`%`, `vw`, `vh`, `fr`) when content and viewport can vary.
|
|
41
|
+
- Avoid rigid fixed widths (`width: 500px`) that will break on mobile devices.
|
|
42
|
+
|
|
43
|
+
### z-index
|
|
44
|
+
Before increasing `z-index`:
|
|
45
|
+
- Inspect stacking contexts. A high `z-index` inside a lower stacking context will not overlay elements in a higher stacking context.
|
|
46
|
+
- Do not solve every layering problem by setting a huge `z-index` (e.g. `z-index: 9999`).
|
|
47
|
+
|
|
48
|
+
### Specificity
|
|
49
|
+
Before adding `!important`:
|
|
50
|
+
- Determine which selector or cascade rule is causing the conflict.
|
|
51
|
+
- Avoid specificity escalation by refactoring the selector to match the target's specificity organically.
|
|
52
|
+
|
|
53
|
+
### Responsive CSS
|
|
54
|
+
- Prefer content-driven breakpoints over excessive device-specific media queries.
|
|
55
|
+
|
|
56
|
+
### Modern CSS
|
|
57
|
+
- Use custom properties (CSS variables), `clamp()`, `min()`, `max()`, container queries, logical properties, and modern color functions where appropriate.
|
|
58
|
+
- **Browser Support**: Do not force modern features when the project's browser support does not allow them. Check existing CSS files for compatibility clues.
|
|
59
|
+
|
|
60
|
+
## Anti-Patterns
|
|
61
|
+
|
|
62
|
+
- **!important everywhere**
|
|
63
|
+
- *What*: Slapping `!important` on properties that don't apply or just to win a specificity war.
|
|
64
|
+
- *Why*: It breaks the cascade and makes future maintenance a nightmare of specificity wars.
|
|
65
|
+
- *Instead*: Find the conflicting rule and match or slightly exceed its specificity organically.
|
|
66
|
+
- **Arbitrary z-index escalation**
|
|
67
|
+
- *What*: Using `z-index: 99999`.
|
|
68
|
+
- *Why*: It leads to unpredictable layering and arms races between components.
|
|
69
|
+
- *Instead*: Manage stacking contexts carefully and use a sensible, planned z-index scale.
|
|
70
|
+
- **Fixed widths for fluid content**
|
|
71
|
+
- *What*: Setting `width: 800px` on a container.
|
|
72
|
+
- *Why*: It causes horizontal scrolling or cropping on narrow viewports.
|
|
73
|
+
- *Instead*: Use `max-width: 800px; width: 100%`.
|
|
74
|
+
- **Deeply nested selectors**
|
|
75
|
+
- *What*: Writing `.card .body .title span { ... }`.
|
|
76
|
+
- *Why*: It inflates specificity, making it very hard to override styles later.
|
|
77
|
+
- *Instead*: Use flatter selector structures (e.g., BEM `.card__title`).
|
|
78
|
+
|
|
79
|
+
## Workflow
|
|
80
|
+
|
|
81
|
+
### Debugging
|
|
82
|
+
- Inspect computed styles in the browser (if available) to see which rules are winning.
|
|
83
|
+
- Check the box model (padding, border, margin) to understand element sizing.
|
|
84
|
+
- Inspect layout dimensions, parent constraints, and overflow settings.
|
|
85
|
+
- Verify stacking contexts when diagnosing z-index issues.
|
|
86
|
+
- Do not claim browser inspection is available unless a browser tool is actively being used.
|
|
87
|
+
|
|
88
|
+
### Verification
|
|
89
|
+
- Ensure the layout responds correctly across narrow and wide simulated viewports.
|
|
90
|
+
- Run project CSS linting (e.g. Stylelint) and build steps to verify syntax.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-debugging
|
|
3
|
+
description: Systematic frontend diagnosis and repair methodology. Apply when a UI component, layout, interaction, or state is broken or behaving unexpectedly. This skill teaches structured debugging — reproduce, inspect, isolate, hypothesize, minimal change, verify — rather than guessing and editing code that looks wrong.
|
|
4
|
+
category: frontend
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Frontend Debugging
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
- A UI component renders incorrectly or not at all
|
|
12
|
+
- A layout breaks at a specific viewport width or state
|
|
13
|
+
- An interactive element does not respond as expected
|
|
14
|
+
- A data display shows wrong, missing, or stale information
|
|
15
|
+
- A runtime error appears in the console
|
|
16
|
+
- A CSS or styling rule does not apply as expected
|
|
17
|
+
- A state transition produces unexpected UI behavior
|
|
18
|
+
|
|
19
|
+
## When not to use
|
|
20
|
+
- Purely backend API issues with no UI impact
|
|
21
|
+
- Writing new features from scratch (use frontend-design instead)
|
|
22
|
+
- Reviewing code quality without a specific failure (use ui-review instead)
|
|
23
|
+
|
|
24
|
+
## Instructions
|
|
25
|
+
- Do not edit code because something "looks wrong" — form a concrete diagnosis first.
|
|
26
|
+
- Use the existing FeCode tools (list_directory, read_file, search_files) to gather information before hypothesizing.
|
|
27
|
+
- Classify the problem type before attempting a fix: rendering, styling, state, data, event, or environment.
|
|
28
|
+
- Prefer the smallest possible change that fixes the actual root cause over refactoring the surrounding code.
|
|
29
|
+
- Verify the fix resolves the original symptom and does not introduce regressions.
|
|
30
|
+
- Never suppress or hide an error to make verification pass.
|
|
31
|
+
|
|
32
|
+
## Core Debugging Principle
|
|
33
|
+
|
|
34
|
+
Follow this sequence:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Symptom
|
|
38
|
+
→ Reproduce
|
|
39
|
+
→ Inspect
|
|
40
|
+
→ Isolate
|
|
41
|
+
→ Hypothesize
|
|
42
|
+
→ Minimal change
|
|
43
|
+
→ Verify
|
|
44
|
+
→ Re-check
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Do not skip steps. Skipping reproduction and inspection leads to guessing, which leads to changes that mask symptoms rather than fix causes.
|
|
48
|
+
|
|
49
|
+
## Step 1: Understand the Symptom
|
|
50
|
+
|
|
51
|
+
Before touching any file:
|
|
52
|
+
- What exactly is broken? (visual glitch, wrong output, no output, crash, interaction failure)
|
|
53
|
+
- Where does it happen? (specific page, component, viewport, state, user action)
|
|
54
|
+
- When does it happen? (always, sometimes, specific condition, after a particular action)
|
|
55
|
+
- Is it consistent or intermittent?
|
|
56
|
+
- What is the expected behavior vs. the observed behavior?
|
|
57
|
+
|
|
58
|
+
Do not proceed until you can state the symptom precisely.
|
|
59
|
+
|
|
60
|
+
## Step 2: Reproduce or Establish the Failing Condition
|
|
61
|
+
|
|
62
|
+
If you cannot reproduce the problem, you cannot verify the fix.
|
|
63
|
+
|
|
64
|
+
- Identify the exact path through the application that triggers the failure
|
|
65
|
+
- Identify whether the failure depends on a specific viewport, data state, user action, or environment
|
|
66
|
+
- For intermittent failures, identify the conditions that increase the likelihood
|
|
67
|
+
|
|
68
|
+
If browser tools are available, inspect:
|
|
69
|
+
- Console errors and stack traces
|
|
70
|
+
- Network requests and failures
|
|
71
|
+
- DOM state at the moment of failure
|
|
72
|
+
- Applied CSS rules
|
|
73
|
+
|
|
74
|
+
Do not invent browser output when browser tools are unavailable. Work from code inspection instead.
|
|
75
|
+
|
|
76
|
+
## Step 3: Investigate the Repository
|
|
77
|
+
|
|
78
|
+
Use FeCode tools to gather information before forming a hypothesis:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
list_directory — understand file structure
|
|
82
|
+
search_files — find component, class name, or error message
|
|
83
|
+
read_file — inspect the relevant component, styles, data source
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Inspect in this order:
|
|
87
|
+
1. The component where the symptom appears
|
|
88
|
+
2. Its parent component (props, context, state passed down)
|
|
89
|
+
3. Associated styles or class names
|
|
90
|
+
4. Data source or API response shape
|
|
91
|
+
5. State management (local state, context, store)
|
|
92
|
+
6. Routing and navigation (if the issue is page-level)
|
|
93
|
+
7. Configuration (if the issue looks environmental)
|
|
94
|
+
|
|
95
|
+
Read the actual code — do not rely on assumptions about what it probably does.
|
|
96
|
+
|
|
97
|
+
## Step 4: Classify the Problem
|
|
98
|
+
|
|
99
|
+
Before hypothesizing a fix, identify what category the problem belongs to:
|
|
100
|
+
|
|
101
|
+
| Category | Indicators |
|
|
102
|
+
|----------|-----------|
|
|
103
|
+
| **Rendering** | Component not appearing, wrong element rendered, conditional rendering issue |
|
|
104
|
+
| **Styling/CSS** | Element appears but looks wrong — position, size, color, visibility |
|
|
105
|
+
| **State** | UI shows stale data, updates don't propagate, wrong value displayed |
|
|
106
|
+
| **Data** | Fetched data is wrong, missing, or in an unexpected shape |
|
|
107
|
+
| **Event/Interaction** | Handler not firing, wrong handler attached, event not propagating |
|
|
108
|
+
| **Environment** | Works locally but not in another environment; configuration or API key issue |
|
|
109
|
+
|
|
110
|
+
Solving the wrong category wastes time. A styling fix will not resolve a state problem.
|
|
111
|
+
|
|
112
|
+
## CSS and Layout Debugging
|
|
113
|
+
|
|
114
|
+
When a CSS issue is suspected, reason systematically through:
|
|
115
|
+
|
|
116
|
+
- **Box model**: `width`, `height`, `padding`, `border`, `margin`
|
|
117
|
+
- **Display**: `block`, `inline`, `flex`, `grid`, `inline-flex`, `none`
|
|
118
|
+
- **Position**: `static`, `relative`, `absolute`, `fixed`, `sticky`
|
|
119
|
+
- **Overflow**: `visible`, `hidden`, `scroll`, `auto` — and which ancestor creates the clipping context
|
|
120
|
+
- **Stacking contexts**: `z-index` only works within the same stacking context
|
|
121
|
+
- **Flex sizing**: `flex-grow`, `flex-shrink`, `flex-basis`, `min-width: 0`
|
|
122
|
+
- **Grid sizing**: explicit vs. implicit tracks, `fr` units, `minmax()`
|
|
123
|
+
- **Specificity**: which rule is actually winning, and why
|
|
124
|
+
- **Inheritance**: some properties inherit unexpectedly
|
|
125
|
+
|
|
126
|
+
**Common CSS traps:**
|
|
127
|
+
- `overflow: hidden` on a parent clips children with `position: sticky`
|
|
128
|
+
- `min-width` defaults on flex items prevent shrinking — add `min-width: 0`
|
|
129
|
+
- `z-index` on an element inside a stacking context with `z-index: 0` cannot escape it
|
|
130
|
+
- `height: 100%` requires an ancestor with an explicit height
|
|
131
|
+
|
|
132
|
+
## State and Data Debugging
|
|
133
|
+
|
|
134
|
+
- Confirm that the data source (API, store, prop, context) is actually returning what you expect
|
|
135
|
+
- Confirm that state updates trigger re-renders where needed
|
|
136
|
+
- Confirm that async operations (fetch, suspense, streaming) are completing before the component tries to render
|
|
137
|
+
- Distinguish between "data is wrong" and "data is correct but rendered incorrectly"
|
|
138
|
+
|
|
139
|
+
## Step 5: Form a Concrete Hypothesis
|
|
140
|
+
|
|
141
|
+
Before changing any file, state your hypothesis explicitly:
|
|
142
|
+
|
|
143
|
+
> "I believe the problem is X, caused by Y, and the fix is Z."
|
|
144
|
+
|
|
145
|
+
If you cannot form a specific hypothesis, gather more information rather than trying random edits.
|
|
146
|
+
|
|
147
|
+
## Step 6: Make the Minimal Change
|
|
148
|
+
|
|
149
|
+
- Fix the root cause, not the symptom
|
|
150
|
+
- Do not refactor the entire component to fix a single CSS property
|
|
151
|
+
- Do not add workaround overrides when the underlying issue can be fixed directly
|
|
152
|
+
- Do not restructure state management when the display logic is wrong
|
|
153
|
+
|
|
154
|
+
Prefer surgical edits. The more code you change, the more you risk introducing regressions.
|
|
155
|
+
|
|
156
|
+
## Step 7: Verify
|
|
157
|
+
|
|
158
|
+
After making the fix:
|
|
159
|
+
1. Re-read the changed code — does it actually address the root cause?
|
|
160
|
+
2. Run any relevant tests or build checks
|
|
161
|
+
3. Mentally or actually reproduce the original failing scenario
|
|
162
|
+
4. Check adjacent states: does fixing one state break another?
|
|
163
|
+
5. Check any related components that may share the affected code
|
|
164
|
+
|
|
165
|
+
## Debugging Workflow
|
|
166
|
+
|
|
167
|
+
1. Understand the symptom precisely.
|
|
168
|
+
2. Reproduce or establish the failing condition.
|
|
169
|
+
3. Use list_directory, search_files, and read_file to gather information.
|
|
170
|
+
4. Read the component, its parents, its styles, and its data source.
|
|
171
|
+
5. Classify the problem type.
|
|
172
|
+
6. Form a concrete hypothesis about the root cause.
|
|
173
|
+
7. Make the smallest appropriate change.
|
|
174
|
+
8. Verify: re-read, run checks, reproduce the original scenario.
|
|
175
|
+
9. Confirm the original failure is resolved.
|
|
176
|
+
10. Check for regressions in related states and components.
|
|
177
|
+
|
|
178
|
+
## Avoid
|
|
179
|
+
|
|
180
|
+
- Editing code because it "looks like" the problem without verifying through inspection.
|
|
181
|
+
- Changing multiple files simultaneously before understanding the issue — each change obscures what fixed what.
|
|
182
|
+
- Rewriting a component to fix a problem that was in a single property.
|
|
183
|
+
- Adding CSS overrides that mask the real issue rather than correcting it.
|
|
184
|
+
- Hiding errors with try/catch or null checks without addressing the underlying cause.
|
|
185
|
+
- Disabling TypeScript or lint checks to make verification pass — if they flag something, investigate it.
|
|
186
|
+
- Declaring success before reproducing the original scenario with the fix applied.
|
|
187
|
+
- Inventing browser console output when browser tools are unavailable.
|
|
188
|
+
|
|
189
|
+
## Examples
|
|
190
|
+
|
|
191
|
+
### Example: Diagnosing layout overflow before editing
|
|
192
|
+
|
|
193
|
+
Wrong approach: "The component looks too wide, I'll add `overflow: hidden` to the parent."
|
|
194
|
+
|
|
195
|
+
Correct approach: Identify which specific element is causing overflow. Check `min-width` on flex children (common culprit). Inspect the box model of the overflowing element. Fix the constraint that prevents correct shrinking, not the visibility of the overflow.
|
|
196
|
+
|
|
197
|
+
### Example: State vs. rendering classification
|
|
198
|
+
|
|
199
|
+
Symptom: "The user's name shows the old value after they update their profile."
|
|
200
|
+
|
|
201
|
+
Classify first: Is the API returning the old value? Is the store not updating? Is the component not re-rendering? Is it re-rendering but showing a stale closure value? Each of these has a different fix. Read the data flow before editing.
|