claude-threads 0.12.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.13.0] - 2025-12-29
11
+
12
+ ### Added
13
+ - **`--setup` flag** - Re-run interactive setup wizard to reconfigure settings
14
+ - Existing .env values are used as defaults (press Enter to keep)
15
+ - Token field allows keeping existing token without re-entering
16
+ - New settings added since initial setup are presented with built-in defaults
17
+ - Config saved back to original location
18
+ - **Chrome and worktree settings in onboarding** - New setup prompts for:
19
+ - Chrome integration (yes/no)
20
+ - Git worktree mode (prompt/off/require)
21
+
22
+ ### Changed
23
+ - **Improved README** - New tagline and improved intro section
24
+ - **Worktree documentation** - Added comprehensive Git Worktrees section to README
25
+ - **Updated CLI options** - Added `--chrome`, `--no-chrome`, `--worktree-mode`, `--setup` to README
26
+
27
+ ### Fixed
28
+ - **Warning icon alignment** - Fixed spacing of ⚠️ icon in CLI startup output
29
+ - **WORKTREE_MODE documentation** - Fixed incorrect values in README (was `always`/`never`, now correctly `off`/`prompt`/`require`)
30
+
31
+ ## [0.12.1] - 2025-12-29
32
+
33
+ ### Fixed
34
+ - **Fix logo star positioning** - Right bottom star shifted left as intended
35
+ - **Update README** - Title changed to "Claude Threads" and logo added
36
+
10
37
  ## [0.12.0] - 2025-12-29
11
38
 
12
39
  ### Changed
package/README.md CHANGED
@@ -1,9 +1,17 @@
1
- # Mattermost Claude Code Bridge
1
+ # Claude Threads
2
+
3
+ ```
4
+ ✴ ▄█▀ ███ ✴ claude-threads
5
+ ✴ █▀ █ ✴ Mattermost × Claude Code
6
+ ✴ ▀█▄ █ ✴
7
+ ```
2
8
 
3
9
  [![npm version](https://img.shields.io/npm/v/claude-threads.svg)](https://www.npmjs.com/package/claude-threads)
4
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
11
 
6
- Share Claude Code sessions live in a Mattermost channel. Your colleagues can watch you work with Claude in real-time, collaborate on sessions, and even trigger their own sessions from Mattermost.
12
+ **Bring Claude Code to your team.** Run Claude Code on your machine, share it live in Mattermost. Colleagues can watch, collaborate, and run their own sessions—all from chat.
13
+
14
+ > 💡 *Think of it as screen-sharing for AI pair programming, but everyone can type.*
7
15
 
8
16
  ## Features
9
17
 
@@ -95,16 +103,20 @@ In Mattermost, mention the bot:
95
103
  claude-threads [options]
96
104
 
97
105
  Options:
98
- --url <url> Mattermost server URL
99
- --token <token> Bot token
100
- --channel <id> Channel ID
101
- --bot-name <name> Bot mention name (default: claude-code)
102
- --allowed-users <list> Comma-separated allowed usernames
103
- --skip-permissions Skip permission prompts (auto-approve)
104
- --no-skip-permissions Enable permission prompts (override env)
105
- --debug Enable debug logging
106
- --version Show version
107
- --help Show help
106
+ --url <url> Mattermost server URL
107
+ --token <token> Bot token
108
+ --channel <id> Channel ID
109
+ --bot-name <name> Bot mention name (default: claude-code)
110
+ --allowed-users <list> Comma-separated allowed usernames
111
+ --skip-permissions Skip permission prompts (auto-approve)
112
+ --no-skip-permissions Enable permission prompts (override env)
113
+ --chrome Enable Chrome integration
114
+ --no-chrome Disable Chrome integration
115
+ --worktree-mode <mode> Git worktree mode: off, prompt, require
116
+ --setup Re-run setup wizard (reconfigure settings)
117
+ --debug Enable debug logging
118
+ --version Show version
119
+ --help Show help
108
120
  ```
109
121
 
110
122
  CLI options override environment variables.
@@ -117,11 +129,17 @@ Type `!help` in any session thread to see available commands:
117
129
  |:--------|:------------|
118
130
  | `!help` | Show available commands |
119
131
  | `!release-notes` | Show release notes for current version |
132
+ | `!context` | Show context usage (tokens used/remaining) |
133
+ | `!cost` | Show token usage and cost for this session |
134
+ | `!compact` | Compress context to free up space |
120
135
  | `!cd <path>` | Change working directory (restarts Claude) |
136
+ | `!worktree <branch>` | Create and switch to a git worktree |
121
137
  | `!invite @user` | Invite a user to this session |
122
138
  | `!kick @user` | Remove an invited user |
123
139
  | `!permissions interactive` | Enable interactive permissions |
140
+ | `!escape` | Interrupt current task (keeps session active) |
124
141
  | `!stop` | Stop this session |
142
+ | `!kill` | Emergency shutdown (kills ALL sessions, exits bot) |
125
143
 
126
144
  > **Note:** Commands use `!` prefix instead of `/` to avoid conflicts with Mattermost's slash commands.
127
145
 
@@ -170,6 +188,47 @@ If the bot is running with `--skip-permissions` (auto mode), you can enable inte
170
188
 
171
189
  This allows collaboration by requiring approval for Claude's actions. Note: you can only downgrade (auto → interactive), not upgrade - this ensures security.
172
190
 
191
+ ## Git Worktrees
192
+
193
+ When working on a task that requires code changes, Claude can work in an isolated git worktree. This keeps your main branch clean while Claude works on a feature branch in a separate directory.
194
+
195
+ ### Starting a Session with a Worktree
196
+
197
+ Specify a branch when starting:
198
+
199
+ ```
200
+ @claude-code on branch feature/add-auth implement user authentication
201
+ ```
202
+
203
+ Or use the worktree command:
204
+
205
+ ```
206
+ @claude-code !worktree feature/add-auth implement user authentication
207
+ ```
208
+
209
+ ### Worktree Commands
210
+
211
+ | Command | Description |
212
+ |:--------|:------------|
213
+ | `!worktree <branch>` | Create worktree and switch to it |
214
+ | `!worktree list` | List all worktrees for this repo |
215
+ | `!worktree switch <branch>` | Switch to an existing worktree |
216
+ | `!worktree remove <branch>` | Remove a worktree |
217
+ | `!worktree off` | Disable worktree prompts for this session |
218
+
219
+ ### How It Works
220
+
221
+ 1. Creates a new worktree at `../<repo>-worktrees/<branch>/`
222
+ 2. Creates or checks out the specified branch
223
+ 3. Claude works in the worktree directory
224
+ 4. Your main working directory stays untouched
225
+
226
+ ### Environment Variable
227
+
228
+ | Variable | Description |
229
+ |----------|-------------|
230
+ | `WORKTREE_MODE` | `prompt` (ask on new sessions), `require` (always require branch), `off` (disable) |
231
+
173
232
  ## Interactive Features
174
233
 
175
234
  ### Permission Approval
@@ -252,6 +311,8 @@ ALLOWED_USERS=alice,bob,carol
252
311
  | `MATTERMOST_BOT_NAME` | Mention name (default: `claude-code`) |
253
312
  | `ALLOWED_USERS` | Comma-separated usernames |
254
313
  | `SKIP_PERMISSIONS` | `true` to auto-approve actions |
314
+ | `CLAUDE_CHROME` | `true` to enable Chrome integration |
315
+ | `WORKTREE_MODE` | `off`, `prompt`, or `require` (default: `prompt`) |
255
316
  | `MAX_SESSIONS` | Max concurrent sessions (default: `5`) |
256
317
  | `SESSION_TIMEOUT_MS` | Idle timeout in ms (default: `1800000` = 30 min) |
257
318
  | `NO_UPDATE_NOTIFIER` | Set to `1` to disable update checks |
package/dist/index.js CHANGED
@@ -29,6 +29,8 @@ program
29
29
  .option('--no-skip-permissions', 'Enable interactive permission prompts (override env)')
30
30
  .option('--chrome', 'Enable Claude in Chrome integration')
31
31
  .option('--no-chrome', 'Disable Claude in Chrome integration')
32
+ .option('--worktree-mode <mode>', 'Git worktree mode: off, prompt, require (default: prompt)')
33
+ .option('--setup', 'Run interactive setup wizard (reconfigure existing settings)')
32
34
  .option('--debug', 'Enable debug logging')
33
35
  .parse();
34
36
  const opts = program.opts();
@@ -52,10 +54,14 @@ async function main() {
52
54
  allowedUsers: opts.allowedUsers,
53
55
  skipPermissions: opts.skipPermissions,
54
56
  chrome: opts.chrome,
57
+ worktreeMode: opts.worktreeMode,
55
58
  };
56
59
  // Check if we need onboarding
57
- if (!configExists() && !hasRequiredCliArgs(opts)) {
58
- await runOnboarding();
60
+ if (opts.setup) {
61
+ await runOnboarding(true); // reconfigure mode
62
+ }
63
+ else if (!configExists() && !hasRequiredCliArgs(opts)) {
64
+ await runOnboarding(false); // first-time mode
59
65
  }
60
66
  const workingDir = process.cwd();
61
67
  const config = loadConfig(cliArgs);
@@ -68,7 +74,7 @@ async function main() {
68
74
  console.log(` 💬 ${cyan('@' + config.mattermost.botName)}`);
69
75
  console.log(` 🌐 ${dim(config.mattermost.url)}`);
70
76
  if (config.skipPermissions) {
71
- console.log(` ⚠️ ${dim('Permissions disabled')}`);
77
+ console.log(` ⚠️ ${dim('Permissions disabled')}`);
72
78
  }
73
79
  else {
74
80
  console.log(` 🔐 ${dim('Interactive permissions')}`);
package/dist/logo.d.ts CHANGED
@@ -12,7 +12,7 @@ export declare const CLI_LOGO: string;
12
12
  * ASCII logo for Mattermost (plain text, no ANSI codes)
13
13
  * Use getMattermostLogo(version) instead to include version
14
14
  */
15
- export declare const MATTERMOST_LOGO = "```\n \u2734 \u2584\u2588\u2580 \u2588\u2588\u2588 \u2734 claude-threads\n\u2734 \u2588\u2580 \u2588 \u2734 Mattermost \u00D7 Claude Code\n\u2734 \u2580\u2588\u2584 \u2588 \u2734\n```";
15
+ export declare const MATTERMOST_LOGO = "```\n \u2734 \u2584\u2588\u2580 \u2588\u2588\u2588 \u2734 claude-threads\n\u2734 \u2588\u2580 \u2588 \u2734 Mattermost \u00D7 Claude Code\n \u2734 \u2580\u2588\u2584 \u2588 \u2734\n```";
16
16
  /**
17
17
  * Get ASCII logo for Mattermost with version included
18
18
  */
package/dist/logo.js CHANGED
@@ -20,7 +20,7 @@ const colors = {
20
20
  export const CLI_LOGO = `
21
21
  ${colors.orange} ✴${colors.reset} ${colors.blue}▄█▀ ███${colors.reset} ${colors.orange}✴${colors.reset} ${colors.bold}claude-threads${colors.reset}
22
22
  ${colors.orange}✴${colors.reset} ${colors.blue}█▀ █${colors.reset} ${colors.orange}✴${colors.reset} ${colors.dim}Mattermost × Claude Code${colors.reset}
23
- ${colors.orange}✴${colors.reset} ${colors.blue}▀█▄ █${colors.reset} ${colors.orange}✴${colors.reset}
23
+ ${colors.orange}✴${colors.reset} ${colors.blue}▀█▄ █${colors.reset} ${colors.orange}✴${colors.reset}
24
24
  `;
25
25
  /**
26
26
  * ASCII logo for Mattermost (plain text, no ANSI codes)
@@ -29,7 +29,7 @@ ${colors.orange}✴${colors.reset} ${colors.blue}▀█▄ █${colors.reset}
29
29
  export const MATTERMOST_LOGO = `\`\`\`
30
30
  ✴ ▄█▀ ███ ✴ claude-threads
31
31
  ✴ █▀ █ ✴ Mattermost × Claude Code
32
- ▀█▄ █
32
+ ▀█▄ █
33
33
  \`\`\``;
34
34
  /**
35
35
  * Get ASCII logo for Mattermost with version included
@@ -38,7 +38,7 @@ export function getMattermostLogo(version) {
38
38
  return `\`\`\`
39
39
  ✴ ▄█▀ ███ ✴ claude-threads v${version}
40
40
  ✴ █▀ █ ✴ Mattermost × Claude Code
41
- ▀█▄ █
41
+ ▀█▄ █
42
42
  \`\`\``;
43
43
  }
44
44
  /**
@@ -1 +1 @@
1
- export declare function runOnboarding(): Promise<void>;
1
+ export declare function runOnboarding(reconfigure?: boolean): Promise<void>;
@@ -1,16 +1,47 @@
1
1
  import prompts from 'prompts';
2
- import { writeFileSync, mkdirSync } from 'fs';
2
+ import { writeFileSync, mkdirSync, readFileSync, existsSync } from 'fs';
3
3
  import { homedir } from 'os';
4
- import { resolve } from 'path';
4
+ import { resolve, dirname } from 'path';
5
+ import { parse } from 'dotenv';
5
6
  const bold = (s) => `\x1b[1m${s}\x1b[0m`;
6
7
  const dim = (s) => `\x1b[2m${s}\x1b[0m`;
7
8
  const green = (s) => `\x1b[32m${s}\x1b[0m`;
8
- export async function runOnboarding() {
9
- console.log('');
10
- console.log(bold(' Welcome to claude-threads!'));
11
- console.log(dim(' ─────────────────────────────────'));
9
+ // Paths to search for .env files (in order of priority)
10
+ const ENV_PATHS = [
11
+ resolve(process.cwd(), '.env'),
12
+ resolve(homedir(), '.config', 'claude-threads', '.env'),
13
+ resolve(homedir(), '.claude-threads.env'),
14
+ ];
15
+ function loadExistingConfig() {
16
+ for (const envPath of ENV_PATHS) {
17
+ if (existsSync(envPath)) {
18
+ try {
19
+ const content = readFileSync(envPath, 'utf-8');
20
+ return { path: envPath, values: parse(content) };
21
+ }
22
+ catch {
23
+ return { path: null, values: {} };
24
+ }
25
+ }
26
+ }
27
+ return { path: null, values: {} };
28
+ }
29
+ export async function runOnboarding(reconfigure = false) {
30
+ const { path: existingPath, values: existing } = reconfigure ? loadExistingConfig() : { path: null, values: {} };
31
+ const hasExisting = Object.keys(existing).length > 0;
12
32
  console.log('');
13
- console.log(' No configuration found. Let\'s set things up.');
33
+ if (reconfigure && hasExisting) {
34
+ console.log(bold(' Reconfiguring claude-threads'));
35
+ console.log(dim(' ─────────────────────────────────'));
36
+ console.log('');
37
+ console.log(dim(' Press Enter to keep existing values.'));
38
+ }
39
+ else {
40
+ console.log(bold(' Welcome to claude-threads!'));
41
+ console.log(dim(' ─────────────────────────────────'));
42
+ console.log('');
43
+ console.log(' No configuration found. Let\'s set things up.');
44
+ }
14
45
  console.log('');
15
46
  console.log(dim(' You\'ll need:'));
16
47
  console.log(dim(' • A Mattermost bot account with a token'));
@@ -23,25 +54,41 @@ export async function runOnboarding() {
23
54
  console.log(dim(' Setup cancelled.'));
24
55
  process.exit(0);
25
56
  };
57
+ // Helper to get worktree mode index
58
+ const worktreeModeIndex = (mode) => {
59
+ if (mode === 'off')
60
+ return 1;
61
+ if (mode === 'require')
62
+ return 2;
63
+ return 0; // default to 'prompt'
64
+ };
26
65
  const response = await prompts([
27
66
  {
28
67
  type: 'text',
29
68
  name: 'url',
30
69
  message: 'Mattermost URL',
31
- initial: 'https://your-mattermost-server.com',
70
+ initial: existing.MATTERMOST_URL || 'https://your-mattermost-server.com',
32
71
  validate: (v) => v.startsWith('http') ? true : 'URL must start with http:// or https://',
33
72
  },
34
73
  {
35
74
  type: 'password',
36
75
  name: 'token',
37
76
  message: 'Bot token',
38
- hint: 'Create at: Integrations > Bot Accounts > Add Bot Account',
39
- validate: (v) => v.length > 0 ? true : 'Token is required',
77
+ hint: existing.MATTERMOST_TOKEN
78
+ ? 'Enter to keep existing, or type new token'
79
+ : 'Create at: Integrations > Bot Accounts > Add Bot Account',
80
+ validate: (v) => {
81
+ // Allow empty if we have an existing token (user wants to keep it)
82
+ if (!v && existing.MATTERMOST_TOKEN)
83
+ return true;
84
+ return v.length > 0 ? true : 'Token is required';
85
+ },
40
86
  },
41
87
  {
42
88
  type: 'text',
43
89
  name: 'channelId',
44
90
  message: 'Channel ID',
91
+ initial: existing.MATTERMOST_CHANNEL_ID || '',
45
92
  hint: 'Click channel name > View Info > copy ID from URL',
46
93
  validate: (v) => v.length > 0 ? true : 'Channel ID is required',
47
94
  },
@@ -49,26 +96,48 @@ export async function runOnboarding() {
49
96
  type: 'text',
50
97
  name: 'botName',
51
98
  message: 'Bot mention name',
52
- initial: 'claude-code',
99
+ initial: existing.MATTERMOST_BOT_NAME || 'claude-code',
53
100
  hint: 'Users will @mention this name',
54
101
  },
55
102
  {
56
103
  type: 'text',
57
104
  name: 'allowedUsers',
58
105
  message: 'Allowed usernames',
59
- initial: '',
106
+ initial: existing.ALLOWED_USERS || '',
60
107
  hint: 'Comma-separated, or empty for all users',
61
108
  },
62
109
  {
63
110
  type: 'confirm',
64
111
  name: 'skipPermissions',
65
112
  message: 'Skip permission prompts?',
66
- initial: true,
113
+ initial: existing.SKIP_PERMISSIONS !== undefined
114
+ ? existing.SKIP_PERMISSIONS === 'true'
115
+ : true,
67
116
  hint: 'If no, you\'ll approve each action via emoji reactions',
68
117
  },
118
+ {
119
+ type: 'confirm',
120
+ name: 'chrome',
121
+ message: 'Enable Chrome integration?',
122
+ initial: existing.CLAUDE_CHROME === 'true',
123
+ hint: 'Requires Claude in Chrome extension',
124
+ },
125
+ {
126
+ type: 'select',
127
+ name: 'worktreeMode',
128
+ message: 'Git worktree mode',
129
+ hint: 'Isolate changes in separate worktrees',
130
+ choices: [
131
+ { title: 'Prompt', value: 'prompt', description: 'Ask when starting new sessions' },
132
+ { title: 'Off', value: 'off', description: 'Never use worktrees' },
133
+ { title: 'Require', value: 'require', description: 'Always require a branch name' },
134
+ ],
135
+ initial: worktreeModeIndex(existing.WORKTREE_MODE),
136
+ },
69
137
  ], { onCancel });
70
- // Check if user cancelled
71
- if (!response.url || !response.token || !response.channelId) {
138
+ // Check if user cancelled - token can be empty if keeping existing
139
+ const finalToken = response.token || existing.MATTERMOST_TOKEN;
140
+ if (!response.url || !finalToken || !response.channelId) {
72
141
  console.log('');
73
142
  console.log(dim(' Setup incomplete. Run claude-threads again to retry.'));
74
143
  process.exit(1);
@@ -81,7 +150,7 @@ export async function runOnboarding() {
81
150
  MATTERMOST_URL=${response.url}
82
151
 
83
152
  # Bot token (from Integrations > Bot Accounts)
84
- MATTERMOST_TOKEN=${response.token}
153
+ MATTERMOST_TOKEN=${finalToken}
85
154
 
86
155
  # Channel ID where the bot listens
87
156
  MATTERMOST_CHANNEL_ID=${response.channelId}
@@ -94,10 +163,18 @@ ALLOWED_USERS=${response.allowedUsers || ''}
94
163
 
95
164
  # Skip permission prompts (true = auto-approve, false = require emoji approval)
96
165
  SKIP_PERMISSIONS=${response.skipPermissions ? 'true' : 'false'}
166
+
167
+ # Chrome integration (requires Claude in Chrome extension)
168
+ CLAUDE_CHROME=${response.chrome ? 'true' : 'false'}
169
+
170
+ # Git worktree mode (off, prompt, require)
171
+ WORKTREE_MODE=${response.worktreeMode || 'prompt'}
97
172
  `;
98
- // Save to ~/.config/claude-threads/.env
99
- const configDir = resolve(homedir(), '.config', 'claude-threads');
100
- const envPath = resolve(configDir, '.env');
173
+ // Save to same location if reconfiguring, otherwise default location
174
+ const defaultConfigDir = resolve(homedir(), '.config', 'claude-threads');
175
+ const defaultEnvPath = resolve(defaultConfigDir, '.env');
176
+ const envPath = existingPath || defaultEnvPath;
177
+ const configDir = dirname(envPath);
101
178
  try {
102
179
  mkdirSync(configDir, { recursive: true });
103
180
  writeFileSync(envPath, envContent, { mode: 0o600 }); // Secure permissions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Share Claude Code sessions live in a Mattermost channel with interactive features",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",