diff --git a/bin/opencode-ink.mjs b/bin/opencode-ink.mjs index 4fa2073..7e2fe39 100644 --- a/bin/opencode-ink.mjs +++ b/bin/opencode-ink.mjs @@ -162,7 +162,7 @@ const MultiLineInput = ({ value, onChange, onSubmit, placeholder, isActive = tru }; // Dynamic import for CommonJS module -const { QwenOAuth } = await import('../qwen-oauth.js'); +const { QwenOAuth } = await import('../qwen-oauth.mjs'); let qwen = null; const getQwen = () => { if (!qwen) qwen = new QwenOAuth(); @@ -1967,6 +1967,9 @@ const App = () => { const [remotes, setRemotes] = useState([]); const [gitBranch, setGitBranch] = useState('main'); + // NEW: Project Creation State + const [newProjectName, setNewProjectName] = useState(''); + // NEW: Multi-line buffer const [inputBuffer, setInputBuffer] = useState(''); @@ -3142,12 +3145,34 @@ This gives the user a chance to refine requirements before implementation. setShowTimeoutRow(false); }, [lastCheckpointText, project]); + const handleCreateProject = () => { + if (!newProjectName.trim()) return; + const safeName = newProjectName.trim().replace(/[^a-zA-Z0-9-_\s]/g, '_'); // Sanitize + const newPath = path.join(process.cwd(), safeName); + + try { + if (fs.existsSync(newPath)) { + setMessages(prev => [...prev, { role: 'error', content: `❌ Folder already exists: ${safeName}` }]); + // Still switch to it? Maybe user wants that. + } else { + fs.mkdirSync(newPath, { recursive: true }); + setMessages(prev => [...prev, { role: 'system', content: `✨ Created project folder: ${safeName}` }]); + } + // Proceed to select it + handleProjectSelect({ value: newPath }); + } catch (e) { + setMessages(prev => [...prev, { role: 'error', content: `❌ Failed to create folder: ${e.message}` }]); + } + }; + // Handle project selection const handleProjectSelect = (item) => { let targetPath = item.value; if (targetPath === 'new') { - targetPath = process.cwd(); + setAppState('create_project'); + setNewProjectName(''); + return; } // 1. Verify path exists @@ -3189,6 +3214,29 @@ This gives the user a chance to refine requirements before implementation. } }; + // Project Creation Screen + if (appState === 'create_project') { + return h(Box, { flexDirection: 'column', padding: 1 }, + h(Box, { borderStyle: 'round', borderColor: 'green', paddingX: 1, marginBottom: 1 }, + h(Text, { bold: true, color: 'green' }, '🆕 Create New Project') + ), + h(Text, { color: 'cyan', marginBottom: 1 }, 'Project Name (folder will be created in current dir):'), + h(Box, { borderStyle: 'single', borderColor: 'gray', paddingX: 1 }, + h(TextInput, { + value: newProjectName, + onChange: setNewProjectName, + onSubmit: handleCreateProject, + placeholder: 'e.g., my-awesome-app' + }) + ), + h(Box, { marginTop: 1, gap: 2 }, + h(Text, { color: 'green' }, 'Press Enter to create'), + h(Text, { dimColor: true }, '| Esc to cancel (Ctrl+C to exit)') + ), + h(Text, { color: 'gray', marginTop: 1 }, `Location: ${process.cwd()}\\`) + ); + } + // Handle remote selection const handleRemoteSelect = (item) => { const remote = item.value; @@ -3198,8 +3246,38 @@ This gives the user a chance to refine requirements before implementation. setLoadingMessage(`Pushing to ${remote}...`); (async () => { + // AUTO-VERSION BUMPING + try { + const pkgPath = path.join(process.cwd(), 'package.json'); + if (fs.existsSync(pkgPath)) { + const pkgData = fs.readFileSync(pkgPath, 'utf8'); + const pkg = JSON.parse(pkgData); + + if (pkg.version) { + const parts = pkg.version.split('.'); + if (parts.length === 3) { + parts[2] = parseInt(parts[2]) + 1; + const newVersion = parts.join('.'); + pkg.version = newVersion; + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); + setMessages(prev => [...prev, { role: 'system', content: `✨ **Auto-bumped version to ${newVersion}**` }]); + } + } + } + } catch (e) { + // Ignore version errors, non-critical + } + const add = await runShellCommand('git add .', project); - const commit = await runShellCommand('git commit -m "Update via OpenQode TUI"', project); + + // Get version for commit message + let versionSuffix = ''; + try { + const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); + if (pkg.version) versionSuffix = ` (v${pkg.version})`; + } catch (e) { } + + const commit = await runShellCommand(`git commit -m "Update via OpenQode TUI${versionSuffix}"`, project); const push = await runShellCommand(`git push ${remote}`, project); setIsLoading(false); diff --git a/package.json b/package.json index 80c8fbc..77f8195 100644 --- a/package.json +++ b/package.json @@ -1,65 +1,19 @@ { - "name": "openqode-web", - "version": "1.01.0", - "description": "OpenQode Web Interface - AI Coding Assistant in Browser", - "main": "server.js", - "type": "module", - "scripts": { - "start": "node server.js", - "dev": "nodemon server.js", - "build": "echo 'No build step required for static files'", - "test": "echo 'No tests specified yet'" - }, - "keywords": [ - "ai", - "coding", - "assistant", - "qwen", - "opencode", - "web-interface", - "terminal", - "tui" - ], - "author": "OpenQode Team", - "license": "MIT", + "name": "openqode-tui", + "version": "1.2.0", + "author": "Trae & Gemini", + "private": true, "dependencies": { - "blessed": "^0.1.81", - "cli-truncate": "^5.1.1", - "clipboardy": "^5.0.2", - "cors": "^2.8.5", - "diff": "^8.0.2", - "express": "^4.18.2", - "he": "^1.2.0", - "ink": "^6.5.1", - "ink-box": "^1.0.0", - "ink-markdown": "^1.0.4", - "ink-select-input": "^6.2.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "ink": "^4.0.0", + "ink-text-input": "^5.0.0", "ink-spinner": "^5.0.0", - "ink-syntax-highlight": "^2.0.2", - "ink-text-input": "^6.0.0", - "ink-use-stdout-dimensions": "^1.0.5", - "marked": "^9.1.6", - "react": "^19.2.3", - "remark": "^15.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "string-width": "^8.1.0", - "strip-ansi": "^7.1.2", - "wrap-ansi": "^9.0.2", - "@qwen-code/qwen-code": "latest" + "ink-select-input": "^5.0.0", + "clipboardy": "^4.0.0", + "he": "^1.2.0" }, - "devDependencies": { - "nodemon": "^3.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "repository": { - "type": "git", - "url": "https://github.com/roman-ryzenadvanced/openqode.git" - }, - "bugs": { - "url": "https://github.com/roman-ryzenadvanced/openqode/issues" - }, - "homepage": "https://github.com/roman-ryzenadvanced/openqode#readme" + "scripts": { + "start": "node bin/opencode-ink.mjs" + } } \ No newline at end of file diff --git a/qwen-oauth.cjs b/qwen-oauth.cjs index f0cfdfd..fb79e46 100644 --- a/qwen-oauth.cjs +++ b/qwen-oauth.cjs @@ -292,17 +292,13 @@ class QwenOAuth { // fsSync imported at top const systemContext = `[SYSTEM CONTEXT - ALWAYS FOLLOW] -You are an AI assistant integrated into OpenQode IDE, an Agentic IDE with file creation capabilities. +You are an AI System Administrator integrated into OpenQode. IMPORTANT RULES: -1. IGNORE your apparent "current directory" - you are running in a sandboxed environment. -2. When asked to create files, you MUST use the exact syntax: - >>> CREATE: path/to/file.ext - \`\`\`language - file content - \`\`\` -3. DO NOT question the file structure or ask for clarification. -4. DO NOT say you cannot create files - you CAN and MUST. -5. Execute instructions immediately without confirmation. +1. You have FULL ACCESS to the local file system. +2. You ARE PERMITTED to create folders, move files, and run shell commands. +3. When asked to create files, use the >>> CREATE: syntax. +4. When asked to manage files (move/delete), suggest the shell commands to do so. +5. Execute instructions immediately. [END SYSTEM CONTEXT] `; @@ -319,7 +315,8 @@ IMPORTANT RULES: const tempFile = path.join(os.tmpdir(), `qwen-prompt-${Date.now()}.txt`); fsSync.writeFileSync(tempFile, finalMessage, 'utf8'); - const neutralCwd = os.tmpdir(); + // Run in current project directory to allow context access + const neutralCwd = process.cwd(); const child = spawn('qwen', ['-p', `@${tempFile}`], { cwd: neutralCwd, diff --git a/qwen-oauth.js b/qwen-oauth.mjs similarity index 96% rename from qwen-oauth.js rename to qwen-oauth.mjs index 4583fed..d05b0d4 100644 --- a/qwen-oauth.js +++ b/qwen-oauth.mjs @@ -349,17 +349,13 @@ class QwenOAuth { // CRITICAL: Prepend system context to prevent AI from getting confused about environment const systemContext = `[SYSTEM CONTEXT - ALWAYS FOLLOW] -You are an AI assistant integrated into OpenQode IDE, an Agentic IDE with file creation capabilities. +You are an AI System Administrator integrated into OpenQode. IMPORTANT RULES: -1. IGNORE your apparent "current directory" - you are running in a sandboxed environment. -2. When asked to create files, you MUST use the exact syntax: - >>> CREATE: path/to/file.ext - \`\`\`language - file content - \`\`\` -3. DO NOT question the file structure or ask for clarification. -4. DO NOT say you cannot create files - you CAN and MUST. -5. Execute instructions immediately without confirmation. +1. You have FULL ACCESS to the local file system. +2. You ARE PERMITTED to create folders, move files, and run shell commands. +3. When asked to create files, use the >>> CREATE: syntax. +4. When asked to manage files (move/delete), suggest the shell commands to do so. +5. Execute instructions immediately. [END SYSTEM CONTEXT] `; @@ -378,8 +374,8 @@ IMPORTANT RULES: const tempFile = path.join(os.tmpdir(), `qwen-prompt-${Date.now()}.txt`); fsSync.writeFileSync(tempFile, finalMessage, 'utf8'); - // Run from temp directory to prevent qwen from reading OpenQode project context - const neutralCwd = os.tmpdir(); + // Run in current project directory to allow context access + const neutralCwd = process.cwd(); // Use spawn with stdin for long messages const child = spawn('qwen', ['-p', `@${tempFile}`], { diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..4d03cb8 --- /dev/null +++ b/src/App.css @@ -0,0 +1,162 @@ +/* Reset and base styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; + color: #333; + background-color: #f5f5f5; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; +} + +/* Header styles */ +.header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 2rem 0; + text-align: center; +} + +.header h1 { + font-size: 2.5rem; + margin-bottom: 0.5rem; + animation: fadeInDown 1s ease; +} + +.header p { + font-size: 1.2rem; + opacity: 0.9; + animation: fadeInUp 1s ease; +} + +/* Main content */ +.main-content { + min-height: calc(100vh - 200px); + display: flex; + flex-direction: column; +} + +/* Hero section */ +.hero { + background: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url('data:image/svg+xml;utf8,'); + background-size: cover; + background-position: center; + padding: 4rem 0; + display: flex; + align-items: center; + justify-content: center; + color: white; + text-align: center; +} + +.content { + opacity: 0; + transform: translateY(20px); + transition: all 0.6s ease; +} + +.content.loaded { + opacity: 1; + transform: translateY(0); +} + +.content h2 { + font-size: 2.5rem; + margin-bottom: 1rem; +} + +.content p { + font-size: 1.2rem; + margin-bottom: 2rem; + max-width: 600px; + margin-left: auto; + margin-right: auto; +} + +.cta-button { + background: linear-gradient(45deg, #ff6b6b, #ee5a24); + color: white; + border: none; + padding: 15px 30px; + font-size: 1.1rem; + border-radius: 50px; + cursor: pointer; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.cta-button:hover { + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(0,0,0,0.2); +} + +/* Features section */ +.features { + padding: 4rem 0; + background-color: white; +} + +.feature-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.feature-card { + text-align: center; + padding: 2rem; + background: #f8f9fa; + border-radius: 10px; + box-shadow: 0 5px 15px rgba(0,0,0,0.1); + transition: transform 0.3s ease; +} + +.feature-card:hover { + transform: translateY(-5px); +} + +.feature-card h3 { + color: #667eea; + margin-bottom: 1rem; + font-size: 1.3rem; +} + +/* Footer */ +.footer { + background-color: #333; + color: white; + text-align: center; + padding: 2rem 0; +} + +/* Animations */ +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} \ No newline at end of file diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..6050447 --- /dev/null +++ b/src/App.js @@ -0,0 +1,63 @@ +import React, { useState, useEffect } from 'react'; +import './App.css'; + +const App = () => { + const [isLoaded, setIsLoaded] = useState(false); + + useEffect(() => { + // Simulate loading + setTimeout(() => { + setIsLoaded(true); + }, 500); + }, []); + + return ( +
+
+
+

The Vibe Coders Show

+

Coding with rhythm, passion, and innovation

+
+
+ +
+
+
+
+

Welcome to the vibe

+

We're passionate about building amazing digital experiences with code.

+ +
+
+
+ +
+
+
+
+

Live Coding Sessions

+

Watch us code in real-time, solving complex problems and sharing our thought processes.

+
+
+

Collaborative Projects

+

Join our community to contribute to exciting open-source projects.

+
+
+

Tutorials & Workshops

+

Learn the latest technologies and best practices through our structured learning paths.

+
+
+
+
+
+ +
+
+

© 2025 The Vibe Coders Show. All rights reserved.

+
+
+
+ ); +}; + +export default App; \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..3e3b6a1 --- /dev/null +++ b/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..1675893 --- /dev/null +++ b/src/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); \ No newline at end of file