@stilero/bankan 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniele Eliasson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,363 @@
1
+ <p align="center">
2
+ <img src="docs/images/ban_kan_logo_readme.svg" alt="Ban Kan logo" width="520" />
3
+ </p>
4
+
5
+ # Ban Kan
6
+
7
+ <p align="center">
8
+ <strong>Run AI coding agents like a Kanban board.</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ Plan → Implement → Review → Pull Request
13
+ </p>
14
+
15
+ <p align="center">
16
+ The control center for managing many AI coding agents in one simple UI.
17
+ </p>
18
+
19
+ <p align="center">
20
+ Bring order to parallel AI development without leaving your local workflow.
21
+ </p>
22
+
23
+ <p align="center">
24
+ <img src="docs/images/bankan_screenshot.png" alt="Ban Kan dashboard showing backlog, planning, implementation, review, and done columns" width="1200" />
25
+ </p>
26
+
27
+ <p align="center">
28
+ <a href="https://github.com/stilero/bankan/actions/workflows/ci.yml">CI</a>
29
+ ·
30
+ <a href="https://github.com/stilero/bankan">GitHub</a>
31
+ ·
32
+ <a href="https://github.com/stilero/bankan/issues">Issues</a>
33
+ </p>
34
+
35
+ <p align="center">
36
+ ⭐ If Ban Kan helps you ship faster, please consider starring the repo.
37
+ </p>
38
+
39
+ ---
40
+
41
+ ## What Is Ban Kan
42
+
43
+ Ban Kan is a **local control center for AI coding agents** that work across real repositories.
44
+
45
+ Instead of one long AI chat trying to do everything, tasks move through a structured pipeline inspired by a Kanban board:
46
+
47
+ Backlog → Planning → Implementation → Review → Done
48
+
49
+ Each stage can use different agents, prompts, and concurrency settings. Developers keep full visibility and control over what is happening at every step.
50
+
51
+ Ban Kan combines:
52
+
53
+ - structured workflows
54
+ - parallel agent execution
55
+ - human approvals
56
+ - local repository access
57
+ - optional pull request automation
58
+
59
+ All in one dashboard.
60
+
61
+ ---
62
+
63
+ ## Why Ban Kan Exists
64
+
65
+ Most AI coding workflows eventually break down in the same way:
66
+
67
+ - one giant prompt tries to do planning, coding, and review
68
+ - context grows and token usage explodes
69
+ - agents overwrite each other’s work
70
+ - there is no clear review stage
71
+ - parallel development becomes chaos
72
+
73
+ Ban Kan fixes this with a model developers already understand:
74
+
75
+ **a Kanban board with specialized AI agents.**
76
+
77
+ Each stage has a clear responsibility, and tasks move forward only when the previous step succeeds.
78
+
79
+ <table>
80
+ <tr>
81
+ <td align="center" width="50%">
82
+ <img src="docs/images/before_claude_windows.png" alt="Four separate Claude Code terminal windows used to coordinate parallel agent work before Ban Kan" width="100%" />
83
+ <br />
84
+ <strong>Before Ban Kan</strong>
85
+ <br />
86
+ Managing multiple agents means juggling separate terminal windows and fragmented context.
87
+ </td>
88
+ <td align="center" width="50%">
89
+ <img src="docs/images/bankan_multi_tasks.png" alt="Ban Kan dashboard showing multiple tasks and agent output in one coordinated interface" width="100%" />
90
+ <br />
91
+ <strong>With Ban Kan</strong>
92
+ <br />
93
+ Tasks, agent stages, approvals, and live output stay visible in one shared dashboard.
94
+ </td>
95
+ </tr>
96
+ </table>
97
+
98
+ ---
99
+
100
+ ## Built for Agile Development
101
+
102
+ Ban Kan fits naturally into Agile workflows where work is organized as stories.
103
+
104
+ Each story moves through a structured lifecycle that mirrors how real development teams operate:
105
+
106
+ ```mermaid
107
+ flowchart LR
108
+ A[Story / Task Created] --> B[Planning Agent]
109
+ B --> C[Implementation Agent]
110
+ C --> D[Review Agent]
111
+ D --> E[Done / Pull Request]
112
+ ```
113
+
114
+ This structure makes Ban Kan especially useful when working with:
115
+
116
+ - Agile user stories
117
+ - sprint backlogs
118
+ - feature tasks
119
+ - incremental development
120
+
121
+ Instead of one AI trying to solve everything in a single prompt, each stage has a clear responsibility — just like in a real Agile team.
122
+
123
+ Developers plan the story, agents implement the work, reviewers validate the result, and the change moves forward when it meets quality gates.
124
+
125
+ ---
126
+
127
+ ## What It Looks Like In Practice
128
+
129
+ Example story: **Add Stripe payments**
130
+
131
+ Below is the same task moving through Ban Kan's workflow from creation to completion.
132
+
133
+ ### 1. Create the task
134
+
135
+ <p align="center">
136
+ <img src="docs/images/workflow/add_task.png" alt="Ban Kan add task modal used to create the Stripe payments task" />
137
+ </p>
138
+
139
+ The developer creates a task in the dashboard and defines the story to be planned and executed.
140
+
141
+ ### 2. Planning starts
142
+
143
+ <p align="center">
144
+ <img src="docs/images/workflow/planning_stage_started.png" alt="Ban Kan planning stage showing the Stripe payments task as planning starts" />
145
+ </p>
146
+
147
+ The planner agent picks up the task, analyzes the repository, and prepares an implementation plan.
148
+
149
+ ### 3. Review and approve the plan
150
+
151
+ <p align="center">
152
+ <img src="docs/images/workflow/planning_approve_plan.png" alt="Ban Kan planning stage showing an approval-ready plan for the Stripe payments task" />
153
+ </p>
154
+
155
+ The generated plan is shown in the dashboard so the developer can approve it before any code is written.
156
+
157
+ ### 4. Implementation runs
158
+
159
+ <p align="center">
160
+ <img src="docs/images/workflow/implementing_task.png" alt="Ban Kan implementation stage showing the Stripe payments task being actively worked on by an agent" />
161
+ </p>
162
+
163
+ After approval, the implementor agent creates its workspace, writes the code, and reports progress live in the UI.
164
+
165
+ ### 5. Review stage
166
+
167
+ <p align="center">
168
+ <img src="docs/images/workflow/review_stage.png" alt="Ban Kan review stage showing the Stripe payments task being validated by a reviewer agent" />
169
+ </p>
170
+
171
+ The reviewer agent validates the implementation, checks for issues, and decides whether the task is ready to move forward.
172
+
173
+ ### 6. Done / ready for PR
174
+
175
+ <p align="center">
176
+ <img src="docs/images/workflow/done_stage.png" alt="Ban Kan done stage showing the Stripe payments task completed and ready for pull request creation" />
177
+ </p>
178
+
179
+ Once review passes, the task moves to Done and can be used as the basis for a pull request.
180
+
181
+ Multiple tasks can move through these stages simultaneously with different agents assigned to each step.
182
+
183
+ ---
184
+
185
+ ## Installation
186
+
187
+ ### Run instantly
188
+
189
+ ```bash
190
+ npx bankan
191
+ ```
192
+
193
+ ### Install globally
194
+
195
+ ```bash
196
+ npm install -g bankan
197
+ bankan
198
+ ```
199
+
200
+ ### Run from source
201
+
202
+ ```bash
203
+ git clone https://github.com/stilero/bankan.git
204
+ cd bankan
205
+
206
+ npm run install:all
207
+ npm run setup
208
+ npm run dev
209
+ ```
210
+
211
+ Ban Kan starts a local server, opens your browser automatically, and serves the dashboard from the same process.
212
+
213
+ ---
214
+
215
+ ## Requirements
216
+
217
+ - Node.js >= 18
218
+ - git
219
+ - One AI CLI tool:
220
+ - claude
221
+ - codex
222
+ - Native build tools for node-pty
223
+
224
+ macOS: Xcode Command Line Tools
225
+ Linux: build-essential
226
+
227
+ ---
228
+
229
+ ## Quick Start
230
+
231
+ 1. Launch Ban Kan
232
+
233
+ ```bash
234
+ bankan
235
+ ```
236
+
237
+ 2. Complete the setup wizard
238
+
239
+ 3. Add one or more local repositories
240
+
241
+ 4. Create a task in the dashboard
242
+
243
+ 5. Approve the generated plan
244
+
245
+ 6. Watch agents implement and review the change
246
+
247
+ 7. Optionally create a pull request
248
+
249
+ ---
250
+
251
+ ## How It Works
252
+
253
+ ```mermaid
254
+ flowchart TD
255
+ A[Developer creates task] --> B[Planner agent analyzes repository]
256
+ B --> C[Plan generated]
257
+ C --> D{Approve plan?}
258
+ D -->|Yes| E[Implementor writes code]
259
+ D -->|No| F[Revise plan]
260
+ E --> G[Reviewer validates changes]
261
+ G --> H{Review passed?}
262
+ H -->|Yes| I[Done / PR created]
263
+ H -->|No| E
264
+ ```
265
+
266
+ Multiple tasks can run in parallel across different agents.
267
+
268
+ ---
269
+
270
+ ## Key Features
271
+
272
+ ### Parallel AI agents
273
+ Run multiple planning, implementation, and review agents simultaneously.
274
+
275
+ ### Local-first workflow
276
+ Repositories stay on your machine. Agents operate directly on local clones and workspaces.
277
+
278
+ ### Human approval gates
279
+ Developers approve plans before implementation begins.
280
+
281
+ ### Live agent terminals
282
+ Open the terminal of any running agent and take control when needed.
283
+
284
+ ### VS Code workspace support
285
+ Open a task workspace directly from the dashboard.
286
+
287
+ ### PR automation
288
+ Configure GitHub settings to automatically create pull requests.
289
+
290
+ ### Real-time dashboard
291
+ Track:
292
+
293
+ - active tasks
294
+ - blocked tasks
295
+ - agent activity
296
+ - context usage
297
+
298
+ ---
299
+
300
+ ## CLI
301
+
302
+ Ban Kan keeps the CLI intentionally simple.
303
+
304
+ ```bash
305
+ bankan --port 3005
306
+ bankan --no-open
307
+ bankan --help
308
+ ```
309
+
310
+ Options:
311
+
312
+ - `--port` bind to a specific port
313
+ - `--no-open` start without opening a browser
314
+
315
+ Most workflows happen inside the dashboard after launch.
316
+
317
+ ---
318
+
319
+ ## Architecture
320
+
321
+ Ban Kan includes:
322
+
323
+ - Node / Express backend orchestration
324
+ - WebSocket communication for live updates
325
+ - React dashboard built with Vite
326
+ - CLI launcher that starts the local app
327
+ - Configurable planner, implementor, and reviewer agent pools
328
+
329
+ ---
330
+
331
+ ## Development
332
+
333
+ ```bash
334
+ npm run setup
335
+ npm run dev
336
+ ```
337
+
338
+ Useful scripts:
339
+
340
+ - `npm run build` – build client bundle
341
+ - `npm run dev` – run server + Vite client
342
+ - `npm run setup` – interactive setup wizard
343
+ - `npm run install:all` – install all dependencies
344
+
345
+ ---
346
+
347
+ ## Contributing
348
+
349
+ Contributions are welcome.
350
+
351
+ 1. Fork the repository
352
+ 2. Open an issue before starting work
353
+ 3. Create a focused branch
354
+ 4. Make your changes
355
+ 5. Submit a pull request
356
+
357
+ Screenshots are appreciated for UI updates.
358
+
359
+ ---
360
+
361
+ ## License
362
+
363
+ MIT
package/bin/bankan.js ADDED
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync } from 'node:fs';
4
+ import net from 'node:net';
5
+ import { spawn, execFileSync } from 'node:child_process';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const ROOT_DIR = join(__dirname, '..');
11
+
12
+ function parseArgs(argv) {
13
+ const args = { noOpen: false, port: null };
14
+
15
+ for (let i = 0; i < argv.length; i += 1) {
16
+ const arg = argv[i];
17
+
18
+ if (arg === '--no-open') {
19
+ args.noOpen = true;
20
+ continue;
21
+ }
22
+
23
+ if (arg === '--help' || arg === '-h') {
24
+ args.help = true;
25
+ continue;
26
+ }
27
+
28
+ if (arg === '--port' || arg === '-p') {
29
+ const value = argv[i + 1];
30
+ if (!value || !/^\d+$/.test(value)) {
31
+ throw new Error('`--port` expects a numeric value.');
32
+ }
33
+ args.port = parseInt(value, 10);
34
+ i += 1;
35
+ continue;
36
+ }
37
+
38
+ throw new Error(`Unknown argument: ${arg}`);
39
+ }
40
+
41
+ return args;
42
+ }
43
+
44
+ function printHelp() {
45
+ process.stdout.write(`Ban Kan\n\nUsage:\n bankan [--port <number>] [--no-open]\n`);
46
+ }
47
+
48
+ function isPortAvailable(port, host) {
49
+ return new Promise((resolve) => {
50
+ const tester = net.createServer();
51
+ tester.once('error', () => resolve(false));
52
+ tester.once('listening', () => {
53
+ tester.close(() => resolve(true));
54
+ });
55
+ tester.listen(port, host);
56
+ });
57
+ }
58
+
59
+ async function findAvailablePort(preferredPort, host, { exact = false } = {}) {
60
+ if (exact) {
61
+ const available = await isPortAvailable(preferredPort, host);
62
+ if (!available) {
63
+ throw new Error(`Port ${preferredPort} is already in use.`);
64
+ }
65
+ return preferredPort;
66
+ }
67
+
68
+ for (let port = preferredPort; port < preferredPort + 20; port += 1) {
69
+ if (await isPortAvailable(port, host)) {
70
+ return port;
71
+ }
72
+ }
73
+
74
+ throw new Error(`Unable to find an open port near ${preferredPort}.`);
75
+ }
76
+
77
+ function openBrowser(url) {
78
+ if (process.platform === 'darwin') {
79
+ spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();
80
+ return;
81
+ }
82
+
83
+ if (process.platform === 'win32') {
84
+ spawn('cmd', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' }).unref();
85
+ return;
86
+ }
87
+
88
+ spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref();
89
+ }
90
+
91
+ function runSetup() {
92
+ execFileSync(process.execPath, [join(ROOT_DIR, 'scripts', 'setup.js')], {
93
+ env: process.env,
94
+ stdio: 'inherit',
95
+ });
96
+ }
97
+
98
+ async function main() {
99
+ const args = parseArgs(process.argv.slice(2));
100
+ if (args.help) {
101
+ printHelp();
102
+ return;
103
+ }
104
+
105
+ process.env.BANKAN_RUNTIME_MODE = 'packaged';
106
+
107
+ const { getRuntimePaths } = await import('../server/src/paths.js');
108
+ const runtimePaths = getRuntimePaths();
109
+
110
+ if (!existsSync(runtimePaths.clientDistDir)) {
111
+ throw new Error('Built client assets are missing. Rebuild the package before publishing.');
112
+ }
113
+
114
+ if (!existsSync(runtimePaths.envFile) && !existsSync(runtimePaths.settingsFile)) {
115
+ runSetup();
116
+ }
117
+
118
+ const { default: config } = await import('../server/src/config.js');
119
+ const { startServer } = await import('../server/src/index.js');
120
+
121
+ const host = '127.0.0.1';
122
+ const preferredPort = args.port ?? config.PORT;
123
+ const port = await findAvailablePort(preferredPort, host, { exact: args.port !== null });
124
+ const { server, port: resolvedPort } = await startServer({ port, host });
125
+ const url = `http://${host}:${resolvedPort}`;
126
+
127
+ process.stdout.write(`Ban Kan available at ${url}\n`);
128
+
129
+ if (!args.noOpen) {
130
+ try {
131
+ openBrowser(url);
132
+ } catch (err) {
133
+ process.stderr.write(`Failed to open browser automatically: ${err.message}\n`);
134
+ }
135
+ }
136
+
137
+ const shutdown = () => {
138
+ server.close(() => process.exit(0));
139
+ };
140
+
141
+ process.on('SIGINT', shutdown);
142
+ process.on('SIGTERM', shutdown);
143
+ }
144
+
145
+ main().catch((err) => {
146
+ process.stderr.write(`${err.message}\n`);
147
+ process.exit(1);
148
+ });
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
+ * https://github.com/chjj/term.js
5
+ * @license MIT
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ *
25
+ * Originally forked from (with the author's permission):
26
+ * Fabrice Bellard's javascript vt100 for jslinux:
27
+ * http://bellard.org/jslinux/
28
+ * Copyright (c) 2011 Fabrice Bellard
29
+ * The original design remains. The terminal itself
30
+ * has been extended to include xterm CSI codes, among
31
+ * other features.
32
+ */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}:root{--bg: #0C0C0E;--bg1: #111114;--bg2: #16161A;--bg3: #1C1C22;--border: #252530;--border2: #2E2E3C;--amber: #F5A623;--steel2: #6AABDB;--green: #3DDC84;--red: #FF4D4D;--yellow: #FFD166;--purple: #A78BFA;--blue: #60A5FA;--text: #E8E8F0;--text2: #9090A8;--text3: #50505E;--font-mono: "DM Mono", monospace;--font-head: "Syne", sans-serif}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%;overflow:hidden}body{font-family:var(--font-mono);font-size:13px;color:var(--text);background:var(--bg);-webkit-font-smoothing:antialiased}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border2);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text3)}@keyframes card-enter{0%{opacity:0;transform:translate(-12px) scale(.97)}to{opacity:1;transform:translate(0) scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.4}}@keyframes fade-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes toast-in{0%{opacity:0;transform:translate(100px)}to{opacity:1;transform:translate(0)}}button{font-family:var(--font-mono);cursor:pointer;border:none;background:none;color:var(--text);font-size:12px}input,textarea,select{font-family:var(--font-mono);font-size:13px;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:8px 12px;outline:none}input:focus,textarea:focus,select:focus{border-color:var(--amber)}select option{color:var(--text);background:var(--bg1)}