diff --git a/CLAUDE.md b/CLAUDE.md index 21ec6a8..04c6fbb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -393,20 +393,3 @@ The extension supports all current Claude models: - Handle model-specific capabilities and limitations - Provide fallbacks for deprecated model versions - Support both alias and full model names - -## Future Enhancements - -### Planned Features - -- MCP (Model Context Protocol) server integration -- Visual session management with history -- Advanced tool permission management -- Team/shared configuration support -- Performance monitoring and usage analytics - -### Architecture Evolution - -- Plugin system for custom integrations -- Advanced caching for improved performance -- Real-time collaboration features -- Integration with other AI development tools diff --git a/Makefile b/Makefile index b60fe1d..1bd3884 100644 --- a/Makefile +++ b/Makefile @@ -110,11 +110,8 @@ test-watch: # Run linting and fix issues lint: - @echo "🔍 Running ESLint..." - @npm run lint -- --fix || true - @echo "" - @echo "📋 Checking for remaining issues..." - @npm run lint + @echo "🔍 Running ESLint with auto-fix..." + @npm run lint -- --fix @echo "✅ Linting complete" # Run all validation diff --git a/README.md b/README.md index 36b79e2..c6814a1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A Visual Studio Code extension that provides a seamless interface for executing Claude Code commands directly from your development environment. +[Visual Studio Market place](https://marketplace.visualstudio.com/items?itemName=Codingworkflow.claude-runner) + ## Features - **Model Selection**: Choose from all available Claude models (Opus 4, Sonnet 4, Sonnet 3.7, Haiku 3.5) diff --git a/VERSION b/VERSION index 6c6aa7c..6da28dd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0 \ No newline at end of file +0.1.1 \ No newline at end of file diff --git a/assets/README.md b/assets/README.md deleted file mode 100644 index 1e0f589..0000000 --- a/assets/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Icon Requirements - -This directory should contain: - -1. **icon.png** (128x128px) - Main extension icon - - - Should represent Claude/AI + Terminal/Command concepts - - Use colors that work well in both light and dark themes - - Suggested: Terminal window with AI/brain symbol - -2. **icon-32.png** (32x32px) - Activity bar icon - - Smaller version of the main icon - - Should be clearly visible at small sizes - - High contrast for visibility - -## Icon Design Suggestions - -- **Color scheme**: Use blue/purple for AI, combined with terminal green -- **Symbolism**: - - Terminal/command prompt symbol - - AI/brain/chat bubble - - Play button (for execution) -- **Style**: Modern, flat design that matches VS Code aesthetic - -## Placeholder Usage - -Until proper icons are created, VS Code will use default icons with the specified theme icons in package.json (terminal, play, run, gear, etc.). diff --git a/assets/marketplace/currentuse.png b/assets/marketplace/currentuse.png new file mode 100644 index 0000000..605ff31 Binary files /dev/null and b/assets/marketplace/currentuse.png differ diff --git a/jest.config.js b/jest.config.js index 4c0a64d..3978d1a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,7 +7,15 @@ module.exports = { "**/?(*.)+(spec|test).+(ts|tsx|js)", ], transform: { - "^.+\\.(ts|tsx)$": "ts-jest", + "^.+\\.(ts|tsx)$": [ + "ts-jest", + { + useESM: false, + tsconfig: { + types: ["jest", "node"], + }, + }, + ], }, moduleNameMapper: { "\\.(css|less|scss|sass)$": "identity-obj-proxy", @@ -15,12 +23,4 @@ module.exports = { }, setupFilesAfterEnv: ["/src/test/setup.ts"], collectCoverageFrom: ["src/**/*.{ts,tsx}", "!src/**/*.d.ts", "!src/test/**"], - globals: { - "ts-jest": { - useESM: false, - tsconfig: { - types: ["jest", "node"], - }, - }, - }, }; diff --git a/package.json b/package.json index 5513972..915146c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-runner", "displayName": "Claude Runner", "description": "Execute Claude Code commands directly from VS Code with an intuitive interface", - "version": "0.1.0", + "version": "0.1.1", "publisher": "Codingworkflow", "private": true, "license": "GPL-3.0", @@ -17,8 +17,7 @@ }, "categories": [ "Other", - "AI", - "Development Tools" + "Machine Learning" ], "keywords": [ "claude", @@ -221,7 +220,7 @@ "prepare-marketplace": "node scripts/prepare-marketplace.js", "optimize-images": "node scripts/optimize-images.js", "quality": "npm run lint && npm run type-check && npm run format:check", - "quality:fix": "npm run lint --fix && npm run format", + "quality:fix": "npm run lint -- --fix && npm run format", "validate": "npm run quality && npm run test:unit", "ci": "npm run clean && npm run quality && npm run compile && npm run test:unit" }, @@ -265,6 +264,7 @@ "glob": "^10.3.10", "js-yaml": "^4.1.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "rxjs": "^7.8.2" } } diff --git a/src/components/App.tsx b/src/components/App.tsx index b7041bb..287c386 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -196,7 +196,9 @@ const App: React.FC = ({ > Pipeline - {showAdvancedTabs && ( + {(showAdvancedTabs || + activeTab === "usage" || + activeTab === "logs") && ( <> + + ); }; diff --git a/src/components/panels/UsageReportPanel.tsx b/src/components/panels/UsageReportPanel.tsx index 73c430d..273d154 100644 --- a/src/components/panels/UsageReportPanel.tsx +++ b/src/components/panels/UsageReportPanel.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef, useCallback } from "react"; import Card from "../common/Card"; import { useVSCodeAPI } from "../hooks/useVSCodeAPI"; @@ -6,7 +6,7 @@ interface UsageReportPanelProps { disabled?: boolean; } -type Period = "today" | "week" | "month"; +type Period = "hourly" | "today" | "week" | "month"; interface UsageReport { date: string; @@ -31,34 +31,100 @@ const UsageReportPanel: React.FC = ({ disabled = false, }) => { const [selectedPeriod, setSelectedPeriod] = useState("today"); + const [totalHours, setTotalHours] = useState(5); + // Get UTC hour instead of local timezone hour + const [startHour, setStartHour] = useState(new Date().getUTCHours()); + const [limitType, setLimitType] = useState<"input" | "output" | "cost">( + "output", + ); + const [limitValue, setLimitValue] = useState(0); + const [autoRefresh, setAutoRefresh] = useState(false); const [report, setReport] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const timeoutRef = useRef(null); + const refreshIntervalRef = useRef(null); const { requestUsageReport } = useVSCodeAPI(); - const loadReport = (period: Period) => { - setLoading(true); - setError(null); - - // Clear any existing timeout - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); + const getCurrentValue = (): number => { + if (!report) { + return 0; + } + switch (limitType) { + case "input": + return report.totals.inputTokens; + case "output": + return report.totals.outputTokens; + case "cost": + return report.totals.costUSD; + default: + return 0; } + }; - requestUsageReport(period); + const loadReport = useCallback( + (period: Period, hours?: number, start?: number, silent = false) => { + if (!silent) { + setLoading(true); + } + setError(null); - // Add timeout to handle cases where extension doesn't respond - timeoutRef.current = setTimeout(() => { - setLoading(false); - setError("Request timed out. Please try again."); - }, 30000); // 30 second timeout - }; + // Clear any existing timeout + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + if (period === "hourly" && hours !== undefined && start !== undefined) { + requestUsageReport(period, hours, start); + } else { + requestUsageReport(period); + } + + // Add timeout to handle cases where extension doesn't respond + timeoutRef.current = setTimeout(() => { + setLoading(false); + setError("Request timed out. Please try again."); + }, 30000); // 30 second timeout + }, + [requestUsageReport], + ); useEffect(() => { - loadReport(selectedPeriod); - }, [selectedPeriod]); + if (selectedPeriod === "hourly") { + loadReport(selectedPeriod, totalHours, startHour); + } else { + loadReport(selectedPeriod); + } + }, [selectedPeriod, totalHours, startHour]); + + // Auto-refresh effect + useEffect(() => { + if (autoRefresh) { + refreshIntervalRef.current = setInterval( + () => { + // Use silent mode for auto-refresh to avoid loading spinner + if (selectedPeriod === "hourly") { + loadReport(selectedPeriod, totalHours, startHour, true); + } else { + loadReport(selectedPeriod, undefined, undefined, true); + } + }, + 5 * 60 * 1000, + ); // 5 minutes + } else { + if (refreshIntervalRef.current) { + clearInterval(refreshIntervalRef.current); + refreshIntervalRef.current = null; + } + } + + return () => { + if (refreshIntervalRef.current) { + clearInterval(refreshIntervalRef.current); + } + }; + }, [autoRefresh, selectedPeriod, totalHours, startHour, loadReport]); // Listen for usage report data from extension useEffect(() => { @@ -106,6 +172,18 @@ const UsageReportPanel: React.FC = ({ return "Last 7 Days"; case "month": return "Last 30 Days"; + case "hourly": { + const endHour = startHour + totalHours; + const startDisplay = + startHour < 0 + ? `-${Math.abs(startHour).toString().padStart(2, "0")}` + : startHour.toString().padStart(2, "0"); + const endDisplay = + endHour < 0 + ? `-${Math.abs(endHour).toString().padStart(2, "0")}` + : endHour.toString().padStart(2, "0"); + return `${totalHours} Hours (${startDisplay}:00 – ${endDisplay}:00 UTC)`; + } } }; @@ -127,10 +205,154 @@ const UsageReportPanel: React.FC = ({ + + {selectedPeriod === "hourly" && ( +
+
+
+ + { + const newValue = Math.max( + 1, + Math.min(24, parseInt(e.target.value) || 1), + ); + setTotalHours(newValue); + }} + disabled={disabled || loading} + className="dropdown" + style={{ + width: "50px", + textAlign: "center", + marginLeft: "8px", + }} + /> +
+
+ + +
+
+
+
+ + +
+
+ + setLimitValue(parseInt(e.target.value) || 0)} + disabled={disabled || loading} + className="dropdown" + style={{ + width: "80px", + textAlign: "center", + marginLeft: "8px", + }} + /> +
+
+
+ setAutoRefresh(e.target.checked)} + disabled={disabled || loading} + /> + +
+ {limitValue > 0 && report && ( +
+
+ {limitType === "input" && + `Input tokens: ${report.totals.inputTokens} / ${limitValue}`} + {limitType === "output" && + `Output tokens: ${report.totals.outputTokens} / ${limitValue}`} + {limitType === "cost" && + `Cost: $${report.totals.costUSD.toFixed(2)} / $${limitValue}`} +
+
+
limitValue + ? "var(--vscode-errorForeground)" + : "#4CAF50", + transition: "width 0.3s ease", + }} + /> +
+
+ )} +
+ )} + {loading && (

Loading usage data...

@@ -141,7 +363,13 @@ const UsageReportPanel: React.FC = ({

Error: {error}