claude-home 1.1.1 → 1.2.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/README.md CHANGED
@@ -62,13 +62,16 @@ Opens `http://localhost:3141` in your browser automatically.
62
62
 
63
63
  ### Auto-launch on every Claude session
64
64
 
65
- Run this once to register a hook that starts claude-home in the background whenever you open Claude Code:
65
+ On first run, claude-home will ask if you want to register a hook that starts the dashboard automatically whenever Claude Code opens. You can also manage this anytime:
66
66
 
67
67
  ```bash
68
- claude-home setup-hook
68
+ claude-home setup-hook # enable auto-start
69
+ claude-home remove-hook # disable auto-start
69
70
  ```
70
71
 
71
- This also creates a `/claude-home` slash command so you can trigger it from any Claude session.
72
+ Or toggle it directly from the **Hooks** section in the UI.
73
+
74
+ `setup-hook` also creates a `/claude-home` slash command so you can trigger it from any Claude session.
72
75
 
73
76
  ### CLI flags
74
77
 
@@ -82,6 +85,7 @@ claude-home [options]
82
85
 
83
86
  Commands:
84
87
  setup-hook Add SessionStart hook + /claude-home slash command
88
+ remove-hook Remove SessionStart hook + /claude-home slash command
85
89
  ```
86
90
 
87
91
  ---
package/bin/cli.js CHANGED
@@ -44,29 +44,36 @@ if (args.includes('--help') || args.includes('-h')) {
44
44
 
45
45
  Commands:
46
46
  setup-hook Add SessionStart hook and /claude-home slash command
47
+ remove-hook Remove SessionStart hook and /claude-home slash command
47
48
  `);
48
49
  process.exit(0);
49
50
  }
50
51
 
51
- // ─── Setup-hook subcommand ────────────────────────────────────────────────────
52
+ // ─── Setup-hook / remove-hook subcommands ────────────────────────────────────
52
53
 
53
54
  if (subcommand === 'setup-hook') {
54
55
  setupHook();
55
56
  process.exit(0);
56
57
  }
57
58
 
59
+ if (subcommand === 'remove-hook') {
60
+ removeHook();
61
+ process.exit(0);
62
+ }
63
+
58
64
  // ─── Main: start server or reuse existing ────────────────────────────────────
59
65
 
60
66
  checkForUpdates(); // async, non-blocking
61
-
62
- isPortFree(port).then(free => {
63
- if (free) {
64
- const { startServer } = require('../server.js');
65
- startServer(port);
66
- } else {
67
- console.log(`claude-home already running at http://localhost:${port}`);
68
- }
69
- if (!noOpen) openBrowser(`http://localhost:${port}`);
67
+ promptSetupHookIfNeeded().then(() => {
68
+ isPortFree(port).then(free => {
69
+ if (free) {
70
+ const { startServer } = require('../server.js');
71
+ startServer(port);
72
+ } else {
73
+ console.log(`claude-home already running at http://localhost:${port}`);
74
+ }
75
+ if (!noOpen) openBrowser(`http://localhost:${port}`);
76
+ });
70
77
  });
71
78
 
72
79
  // ─── Update check ────────────────────────────────────────────────────────────
@@ -118,6 +125,34 @@ function isPortFree(p) {
118
125
  });
119
126
  }
120
127
 
128
+ function promptSetupHookIfNeeded() {
129
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
130
+ const sentinelPath = path.join(os.homedir(), '.claude', 'claude-home', '.hook-prompted');
131
+
132
+ // Already prompted before, or hook already set up
133
+ if (fs.existsSync(sentinelPath)) return Promise.resolve();
134
+ try {
135
+ const s = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
136
+ const already = s.hooks?.SessionStart?.some(e => e.hooks?.some(h => h.command?.includes('claude-home')));
137
+ if (already) { fs.writeFileSync(sentinelPath, '1'); return Promise.resolve(); }
138
+ } catch { /* settings.json may not exist */ }
139
+
140
+ return new Promise(resolve => {
141
+ process.stdout.write('\n Auto-start claude-home when Claude Code opens? (y/n) ');
142
+ process.stdin.setEncoding('utf8');
143
+ process.stdin.once('data', d => {
144
+ process.stdin.pause();
145
+ if (d.trim().toLowerCase() === 'y') {
146
+ setupHook();
147
+ } else {
148
+ console.log(' Skipped. Run `claude-home setup-hook` anytime to enable it.\n');
149
+ }
150
+ fs.writeFileSync(sentinelPath, '1');
151
+ resolve();
152
+ });
153
+ });
154
+ }
155
+
121
156
  function openBrowser(url) {
122
157
  const cmd = process.platform === 'darwin' ? `open "${url}"`
123
158
  : process.platform === 'win32' ? `start "" "${url}"`
@@ -175,3 +210,38 @@ function setupHook() {
175
210
 
176
211
  console.log('\nDone! Restart Claude Code for the hook to take effect.');
177
212
  }
213
+
214
+ function removeHook() {
215
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
216
+ const commandPath = path.join(os.homedir(), '.claude', 'commands', 'claude-home.md');
217
+ const sentinelPath = path.join(os.homedir(), '.claude', 'claude-home', '.hook-prompted');
218
+
219
+ // Remove SessionStart hook
220
+ let removed = false;
221
+ try {
222
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
223
+ if (settings.hooks?.SessionStart) {
224
+ const before = settings.hooks.SessionStart.length;
225
+ settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
226
+ e => !e.hooks?.some(h => h.command?.includes('claude-home'))
227
+ );
228
+ if (settings.hooks.SessionStart.length < before) {
229
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
230
+ console.log(`✓ SessionStart hook removed from ${settingsPath}`);
231
+ removed = true;
232
+ }
233
+ }
234
+ if (!removed) console.log(' No claude-home hook found — skipped');
235
+ } catch { console.log(' Could not read settings.json — skipped'); }
236
+
237
+ // Remove slash command
238
+ if (fs.existsSync(commandPath)) {
239
+ fs.unlinkSync(commandPath);
240
+ console.log(`✓ Slash command removed from ${commandPath}`);
241
+ }
242
+
243
+ // Reset sentinel so setup prompt can appear again
244
+ try { fs.unlinkSync(sentinelPath); } catch { /* ignore */ }
245
+
246
+ console.log('\nDone! Restart Claude Code for the change to take effect.');
247
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-home",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Web dashboard for Claude Code — browse sessions, manage skills, hooks, commands, and agents",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -3121,6 +3121,18 @@
3121
3121
  <div class="perm-pipeline-note">Hooks reciben el evento como JSON por stdin. Usar matcher para filtrar por tool (Bash, Edit…)</div>
3122
3122
  </div>
3123
3123
 
3124
+ <!-- Auto-start toggle -->
3125
+ <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border:1px solid var(--rule-2);border-radius:6px;margin-bottom:16px;background:var(--canvas)">
3126
+ <div>
3127
+ <div style="font-size:12px;font-weight:600;color:var(--ink)">Auto-start claude-home</div>
3128
+ <div style="font-size:11px;color:var(--ink-3);margin-top:2px">Start the dashboard automatically when Claude Code opens</div>
3129
+ </div>
3130
+ <label style="display:flex;align-items:center;gap:8px;cursor:pointer">
3131
+ <span style="font-size:11px;color:var(--ink-3)" x-text="isAutoStartEnabled() ? 'On' : 'Off'"></span>
3132
+ <input type="checkbox" :checked="isAutoStartEnabled()" @change="toggleAutoStart($event.target.checked)" style="cursor:pointer;width:16px;height:16px;accent-color:var(--accent,#4a9)">
3133
+ </label>
3134
+ </div>
3135
+
3124
3136
  <!-- Hooks section -->
3125
3137
  <div class="config-section" style="margin-bottom:16px">
3126
3138
  <div class="config-section-title" style="display:flex;align-items:center;justify-content:space-between">
@@ -5191,6 +5203,31 @@
5191
5203
  return ordered;
5192
5204
  },
5193
5205
 
5206
+ isAutoStartEnabled() {
5207
+ const hooks = this.configHooks();
5208
+ return hooks.some(h => h.event === 'SessionStart' && h.command?.includes('claude-home'));
5209
+ },
5210
+
5211
+ async toggleAutoStart(enable) {
5212
+ if (enable) {
5213
+ await fetch('/api/config/hooks', {
5214
+ method: 'POST',
5215
+ headers: { 'Content-Type': 'application/json' },
5216
+ body: JSON.stringify({
5217
+ scope: 'user',
5218
+ event: 'SessionStart',
5219
+ matcher: '',
5220
+ type: 'command',
5221
+ command: `lsof -ti:3141 >/dev/null 2>&1 || (claude-home --no-open &>/dev/null &)`,
5222
+ }),
5223
+ });
5224
+ } else {
5225
+ const hook = this.configHooks().find(h => h.event === 'SessionStart' && h.command?.includes('claude-home'));
5226
+ if (hook) await this.deleteHook(hook);
5227
+ }
5228
+ await this.loadConfig();
5229
+ },
5230
+
5194
5231
  configHooks() {
5195
5232
  if (!this.config?.settings) return [];
5196
5233
  const result = [];