claudeck 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -24,16 +24,19 @@
24
24
  # One-command launch (no install needed)
25
25
  npx claudeck
26
26
 
27
+ # Custom port
28
+ npx claudeck --port 3000
29
+
27
30
  # Or install globally
28
31
  npm install -g claudeck
29
32
  claudeck
30
33
  ```
31
34
 
32
- Open **http://localhost:9009** in your browser.
35
+ On first run, Claudeck will ask you to choose a port (default: `9009`), then open your browser to the URL shown in the terminal. The port is saved to `~/.claudeck/.env` for future runs.
33
36
 
34
37
  > Requires **Node.js 18+** and Claude Code CLI authentication (`claude auth login`).
35
38
 
36
- On first run, Claudeck creates `~/.claudeck/` with your config, database, and plugins directory — safe for NPX upgrades.
39
+ User data lives in `~/.claudeck/` (config, database, plugins) — safe for NPX upgrades.
37
40
 
38
41
  ---
39
42
 
package/cli.js CHANGED
@@ -1,2 +1,67 @@
1
1
  #!/usr/bin/env node
2
- import("./server.js");
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ import { createInterface } from "readline";
6
+
7
+ const DEFAULT_PORT = 9009;
8
+ const envDir = process.env.CLAUDECK_HOME || join(homedir(), ".claudeck");
9
+ const envPath = join(envDir, ".env");
10
+ mkdirSync(envDir, { recursive: true });
11
+
12
+ function readEnv() {
13
+ try { return readFileSync(envPath, "utf-8"); } catch { return ""; }
14
+ }
15
+
16
+ function savePort(port) {
17
+ let content = readEnv();
18
+ if (/^PORT=.*/m.test(content)) {
19
+ content = content.replace(/^PORT=.*/m, `PORT=${port}`);
20
+ } else {
21
+ content = content.trimEnd() + `\nPORT=${port}\n`;
22
+ }
23
+ writeFileSync(envPath, content);
24
+ }
25
+
26
+ function getSavedPort() {
27
+ const match = readEnv().match(/^PORT=(\d+)/m);
28
+ return match ? match[1] : null;
29
+ }
30
+
31
+ function ask(question) {
32
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
33
+ return new Promise(resolve => {
34
+ rl.question(question, answer => { rl.close(); resolve(answer.trim()); });
35
+ });
36
+ }
37
+
38
+ async function main() {
39
+ // --port flag takes priority
40
+ const portArg = process.argv.find(a => a.startsWith('--port'));
41
+ if (portArg) {
42
+ const port = portArg.includes('=') ? portArg.split('=')[1] : process.argv[process.argv.indexOf(portArg) + 1];
43
+ if (port) {
44
+ process.env.PORT = port;
45
+ savePort(port);
46
+ return import("./server.js");
47
+ }
48
+ }
49
+
50
+ // If port already saved, use it
51
+ const saved = getSavedPort();
52
+ if (saved) {
53
+ process.env.PORT = saved;
54
+ return import("./server.js");
55
+ }
56
+
57
+ // First run — ask user
58
+ console.log(`\n\x1b[36m Claudeck\x1b[0m — first-time setup\n`);
59
+ const answer = await ask(` Which port would you like to use? \x1b[2m(default: ${DEFAULT_PORT})\x1b[0m `);
60
+ const port = answer && /^\d+$/.test(answer) ? answer : String(DEFAULT_PORT);
61
+ process.env.PORT = port;
62
+ savePort(port);
63
+ console.log(`\x1b[2m Saved to ~/.claudeck/.env\x1b[0m\n`);
64
+ return import("./server.js");
65
+ }
66
+
67
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudeck",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "A browser-based UI for Claude Code — chat, run workflows, manage MCP servers, track costs, and orchestrate autonomous agents from a local web interface. Installable as a PWA.",
6
6
  "main": "server.js",
package/public/index.html CHANGED
@@ -268,6 +268,11 @@
268
268
  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>
269
269
  </svg>
270
270
  </button>
271
+ <button id="remove-project-btn" class="add-project-btn sidebar-tooltip" data-tooltip="Remove project">
272
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
273
+ <polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
274
+ </svg>
275
+ </button>
271
276
  <button id="add-project-btn" class="add-project-btn sidebar-tooltip" data-tooltip="Add project">+</button>
272
277
  <button id="system-prompt-edit-btn" class="system-prompt-edit-btn sidebar-tooltip" data-tooltip="System prompt">&#9881;</button>
273
278
  </div>
@@ -283,6 +283,19 @@ export async function addProject(name, path) {
283
283
  return res.json();
284
284
  }
285
285
 
286
+ export async function deleteProject(path) {
287
+ const res = await fetch("/api/projects", {
288
+ method: "DELETE",
289
+ headers: { "Content-Type": "application/json" },
290
+ body: JSON.stringify({ path }),
291
+ });
292
+ if (!res.ok) {
293
+ const err = await res.json();
294
+ throw new Error(err.error);
295
+ }
296
+ return res.json();
297
+ }
298
+
286
299
  export async function fetchProjectCommands(path) {
287
300
  const res = await fetch(`/api/projects/commands?path=${encodeURIComponent(path)}`);
288
301
  return res.json();
@@ -259,6 +259,7 @@ export const $ = {
259
259
 
260
260
  // Add project modal
261
261
  openVscodeBtn: document.getElementById("open-vscode-btn"),
262
+ removeProjectBtn: document.getElementById("remove-project-btn"),
262
263
  addProjectBtn: document.getElementById("add-project-btn"),
263
264
  addProjectModal: document.getElementById("add-project-modal"),
264
265
  addProjectClose: document.getElementById("add-project-close"),
@@ -278,6 +278,36 @@ $.openVscodeBtn.addEventListener("click", async () => {
278
278
  } catch { /* ignore */ }
279
279
  });
280
280
 
281
+ // Remove project
282
+ $.removeProjectBtn.addEventListener("click", async () => {
283
+ const path = $.projectSelect.value;
284
+ if (!path) return;
285
+ const name = $.projectSelect.options[$.projectSelect.selectedIndex].textContent;
286
+ if (!confirm(`Remove "${name}" from your projects?\n\nThis only removes it from Claudeck — your files won't be deleted.`)) return;
287
+ try {
288
+ await api.deleteProject(path);
289
+ // Remove from dropdown
290
+ const opt = [...$.projectSelect.options].find(o => o.value === path);
291
+ if (opt) opt.remove();
292
+ // Remove from state
293
+ const projects = getState("projectsData");
294
+ const idx = projects.findIndex(p => p.path === path);
295
+ if (idx !== -1) projects.splice(idx, 1);
296
+ // Reset selection
297
+ $.projectSelect.value = "";
298
+ localStorage.removeItem("claudeck-cwd");
299
+ updateSystemPromptIndicator();
300
+ updateHeaderProjectName();
301
+ updateSessionControls();
302
+ loadProjectCommands();
303
+ $.messagesDiv.innerHTML = "";
304
+ showWhalyPlaceholder();
305
+ loadSessions();
306
+ } catch (err) {
307
+ alert("Failed to remove project: " + err.message);
308
+ }
309
+ });
310
+
281
311
  // Add project button & modal event listeners
282
312
  $.addProjectBtn.addEventListener("click", openAddProjectModal);
283
313
  $.addProjectClose.addEventListener("click", closeAddProjectModal);
package/server.js CHANGED
@@ -164,7 +164,21 @@ const PORT = process.env.PORT || 9009;
164
164
  // Mount full-stack plugin routes, then start server
165
165
  mountPluginRoutes(app, fullStackPluginsDir).then(() => {
166
166
  server.listen(PORT, () => {
167
- console.log(`Claudeck running at http://localhost:${PORT}`);
167
+ const url = `http://localhost:${PORT}`;
168
+ console.log(`
169
+ \x1b[36m _____ _ _ _
170
+ / ____| | | | | |
171
+ | | | | __ _ _ _ __| | ___ ___| | __
172
+ | | | |/ _\` | | | |/ _\` |/ _ \\/ __| |/ /
173
+ | |____| | (_| | |_| | (_| | __/ (__| <
174
+ \\_____|_|\\__,_|\\__,_|\\__,_|\\___|\\___|_|\\_\\\x1b[0m
175
+
176
+ \x1b[2m Browser UI for Claude Code\x1b[0m
177
+
178
+ \x1b[1m\x1b[32m➜\x1b[0m \x1b[1mReady:\x1b[0m ${url}
179
+ \x1b[2m➜ Port:\x1b[0m ${PORT}
180
+ \x1b[2m➜ Data:\x1b[0m ~/.claudeck/
181
+ `);
168
182
  });
169
183
  });
170
184