claude-threads 0.15.0 → 0.16.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +5 -5
  3. package/dist/index.js +20410 -387
  4. package/dist/mcp/permission-server.js +34038 -139
  5. package/package.json +14 -18
  6. package/dist/changelog.d.ts +0 -20
  7. package/dist/changelog.js +0 -134
  8. package/dist/claude/cli.d.ts +0 -50
  9. package/dist/claude/cli.js +0 -181
  10. package/dist/config/migration.d.ts +0 -45
  11. package/dist/config/migration.js +0 -35
  12. package/dist/config.d.ts +0 -21
  13. package/dist/config.js +0 -7
  14. package/dist/git/worktree.d.ts +0 -46
  15. package/dist/git/worktree.js +0 -228
  16. package/dist/index.d.ts +0 -2
  17. package/dist/logo.d.ts +0 -14
  18. package/dist/logo.js +0 -41
  19. package/dist/mattermost/api.d.ts +0 -85
  20. package/dist/mattermost/api.js +0 -124
  21. package/dist/mattermost/api.test.d.ts +0 -1
  22. package/dist/mattermost/api.test.js +0 -319
  23. package/dist/mcp/permission-server.d.ts +0 -2
  24. package/dist/onboarding.d.ts +0 -1
  25. package/dist/onboarding.js +0 -318
  26. package/dist/persistence/session-store.d.ts +0 -71
  27. package/dist/persistence/session-store.js +0 -152
  28. package/dist/platform/client.d.ts +0 -140
  29. package/dist/platform/client.js +0 -1
  30. package/dist/platform/formatter.d.ts +0 -74
  31. package/dist/platform/formatter.js +0 -1
  32. package/dist/platform/index.d.ts +0 -11
  33. package/dist/platform/index.js +0 -8
  34. package/dist/platform/mattermost/client.d.ts +0 -70
  35. package/dist/platform/mattermost/client.js +0 -404
  36. package/dist/platform/mattermost/formatter.d.ts +0 -20
  37. package/dist/platform/mattermost/formatter.js +0 -46
  38. package/dist/platform/mattermost/permission-api.d.ts +0 -10
  39. package/dist/platform/mattermost/permission-api.js +0 -139
  40. package/dist/platform/mattermost/types.d.ts +0 -71
  41. package/dist/platform/mattermost/types.js +0 -1
  42. package/dist/platform/permission-api-factory.d.ts +0 -11
  43. package/dist/platform/permission-api-factory.js +0 -21
  44. package/dist/platform/permission-api.d.ts +0 -67
  45. package/dist/platform/permission-api.js +0 -8
  46. package/dist/platform/types.d.ts +0 -70
  47. package/dist/platform/types.js +0 -7
  48. package/dist/session/commands.d.ts +0 -52
  49. package/dist/session/commands.js +0 -323
  50. package/dist/session/events.d.ts +0 -25
  51. package/dist/session/events.js +0 -368
  52. package/dist/session/index.d.ts +0 -7
  53. package/dist/session/index.js +0 -6
  54. package/dist/session/lifecycle.d.ts +0 -70
  55. package/dist/session/lifecycle.js +0 -456
  56. package/dist/session/manager.d.ts +0 -96
  57. package/dist/session/manager.js +0 -537
  58. package/dist/session/reactions.d.ts +0 -25
  59. package/dist/session/reactions.js +0 -151
  60. package/dist/session/streaming.d.ts +0 -47
  61. package/dist/session/streaming.js +0 -152
  62. package/dist/session/types.d.ts +0 -78
  63. package/dist/session/types.js +0 -9
  64. package/dist/session/worktree.d.ts +0 -56
  65. package/dist/session/worktree.js +0 -339
  66. package/dist/update-notifier.d.ts +0 -3
  67. package/dist/update-notifier.js +0 -41
  68. package/dist/utils/emoji.d.ts +0 -43
  69. package/dist/utils/emoji.js +0 -65
  70. package/dist/utils/emoji.test.d.ts +0 -1
  71. package/dist/utils/emoji.test.js +0 -131
  72. package/dist/utils/logger.d.ts +0 -34
  73. package/dist/utils/logger.js +0 -42
  74. package/dist/utils/logger.test.d.ts +0 -1
  75. package/dist/utils/logger.test.js +0 -121
  76. package/dist/utils/tool-formatter.d.ts +0 -53
  77. package/dist/utils/tool-formatter.js +0 -252
  78. package/dist/utils/tool-formatter.test.d.ts +0 -1
  79. package/dist/utils/tool-formatter.test.js +0 -372
@@ -1,71 +0,0 @@
1
- /**
2
- * Worktree information for a session
3
- */
4
- export interface WorktreeInfo {
5
- repoRoot: string;
6
- worktreePath: string;
7
- branch: string;
8
- }
9
- /**
10
- * Persisted session state for resuming after bot restart
11
- */
12
- export interface PersistedSession {
13
- platformId: string;
14
- threadId: string;
15
- claudeSessionId: string;
16
- startedBy: string;
17
- startedAt: string;
18
- sessionNumber: number;
19
- workingDir: string;
20
- sessionAllowedUsers: string[];
21
- forceInteractivePermissions: boolean;
22
- sessionStartPostId: string | null;
23
- tasksPostId: string | null;
24
- lastActivityAt: string;
25
- planApproved: boolean;
26
- worktreeInfo?: WorktreeInfo;
27
- pendingWorktreePrompt?: boolean;
28
- worktreePromptDisabled?: boolean;
29
- queuedPrompt?: string;
30
- }
31
- /**
32
- * SessionStore - Persistence layer for session state
33
- * Stores session data as JSON file for resume after restart
34
- */
35
- export declare class SessionStore {
36
- private debug;
37
- constructor();
38
- /**
39
- * Load all persisted sessions
40
- * Returns Map with composite sessionId ("platformId:threadId") as key
41
- */
42
- load(): Map<string, PersistedSession>;
43
- /**
44
- * Save a session (creates or updates)
45
- * @param sessionId - Composite key "platformId:threadId"
46
- * @param session - Session data to persist
47
- */
48
- save(sessionId: string, session: PersistedSession): void;
49
- /**
50
- * Remove a session
51
- * @param sessionId - Composite key "platformId:threadId"
52
- */
53
- remove(sessionId: string): void;
54
- /**
55
- * Remove sessions older than maxAgeMs
56
- * @returns Array of sessionIds that were removed
57
- */
58
- cleanStale(maxAgeMs: number): string[];
59
- /**
60
- * Clear all sessions
61
- */
62
- clear(): void;
63
- /**
64
- * Load raw data from file
65
- */
66
- private loadRaw;
67
- /**
68
- * Write data atomically (write to temp file, then rename)
69
- */
70
- private writeAtomic;
71
- }
@@ -1,152 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'fs';
2
- import { homedir } from 'os';
3
- import { join } from 'path';
4
- const STORE_VERSION = 2; // v2: Added platformId for multi-platform support
5
- const CONFIG_DIR = join(homedir(), '.config', 'claude-threads');
6
- const SESSIONS_FILE = join(CONFIG_DIR, 'sessions.json');
7
- /**
8
- * SessionStore - Persistence layer for session state
9
- * Stores session data as JSON file for resume after restart
10
- */
11
- export class SessionStore {
12
- debug = process.env.DEBUG === '1' || process.argv.includes('--debug');
13
- constructor() {
14
- // Ensure config directory exists
15
- if (!existsSync(CONFIG_DIR)) {
16
- mkdirSync(CONFIG_DIR, { recursive: true });
17
- }
18
- }
19
- /**
20
- * Load all persisted sessions
21
- * Returns Map with composite sessionId ("platformId:threadId") as key
22
- */
23
- load() {
24
- const sessions = new Map();
25
- if (!existsSync(SESSIONS_FILE)) {
26
- if (this.debug)
27
- console.log(' [persist] No sessions file found');
28
- return sessions;
29
- }
30
- try {
31
- const data = JSON.parse(readFileSync(SESSIONS_FILE, 'utf-8'));
32
- // Migration: v1 → v2 (add platformId and convert keys to composite format)
33
- if (data.version === 1) {
34
- console.log(' [persist] Migrating sessions from v1 to v2 (adding platformId)');
35
- const newSessions = {};
36
- for (const [_oldKey, session] of Object.entries(data.sessions)) {
37
- const v1Session = session;
38
- if (!v1Session.platformId) {
39
- v1Session.platformId = 'default';
40
- }
41
- // Convert key from threadId to platformId:threadId
42
- const newKey = `${v1Session.platformId}:${v1Session.threadId}`;
43
- newSessions[newKey] = v1Session;
44
- }
45
- data.sessions = newSessions;
46
- data.version = 2;
47
- // Save migrated data
48
- this.writeAtomic(data);
49
- }
50
- else if (data.version !== STORE_VERSION) {
51
- console.warn(` [persist] Sessions file version ${data.version} not supported, starting fresh`);
52
- return sessions;
53
- }
54
- // Load sessions with composite sessionId as key
55
- for (const session of Object.values(data.sessions)) {
56
- const sessionId = `${session.platformId}:${session.threadId}`;
57
- sessions.set(sessionId, session);
58
- }
59
- if (this.debug) {
60
- console.log(` [persist] Loaded ${sessions.size} session(s)`);
61
- }
62
- }
63
- catch (err) {
64
- console.error(' [persist] Failed to load sessions:', err);
65
- }
66
- return sessions;
67
- }
68
- /**
69
- * Save a session (creates or updates)
70
- * @param sessionId - Composite key "platformId:threadId"
71
- * @param session - Session data to persist
72
- */
73
- save(sessionId, session) {
74
- const data = this.loadRaw();
75
- // Use sessionId as key (already composite)
76
- data.sessions[sessionId] = session;
77
- this.writeAtomic(data);
78
- if (this.debug) {
79
- const shortId = sessionId.substring(0, 20);
80
- console.log(` [persist] Saved session ${shortId}...`);
81
- }
82
- }
83
- /**
84
- * Remove a session
85
- * @param sessionId - Composite key "platformId:threadId"
86
- */
87
- remove(sessionId) {
88
- const data = this.loadRaw();
89
- if (data.sessions[sessionId]) {
90
- delete data.sessions[sessionId];
91
- this.writeAtomic(data);
92
- if (this.debug) {
93
- const shortId = sessionId.substring(0, 20);
94
- console.log(` [persist] Removed session ${shortId}...`);
95
- }
96
- }
97
- }
98
- /**
99
- * Remove sessions older than maxAgeMs
100
- * @returns Array of sessionIds that were removed
101
- */
102
- cleanStale(maxAgeMs) {
103
- const data = this.loadRaw();
104
- const now = Date.now();
105
- const staleIds = [];
106
- for (const [sessionId, session] of Object.entries(data.sessions)) {
107
- const lastActivity = new Date(session.lastActivityAt).getTime();
108
- if (now - lastActivity > maxAgeMs) {
109
- staleIds.push(sessionId);
110
- delete data.sessions[sessionId];
111
- }
112
- }
113
- if (staleIds.length > 0) {
114
- this.writeAtomic(data);
115
- if (this.debug) {
116
- console.log(` [persist] Cleaned ${staleIds.length} stale session(s)`);
117
- }
118
- }
119
- return staleIds;
120
- }
121
- /**
122
- * Clear all sessions
123
- */
124
- clear() {
125
- this.writeAtomic({ version: STORE_VERSION, sessions: {} });
126
- if (this.debug) {
127
- console.log(' [persist] Cleared all sessions');
128
- }
129
- }
130
- /**
131
- * Load raw data from file
132
- */
133
- loadRaw() {
134
- if (!existsSync(SESSIONS_FILE)) {
135
- return { version: STORE_VERSION, sessions: {} };
136
- }
137
- try {
138
- return JSON.parse(readFileSync(SESSIONS_FILE, 'utf-8'));
139
- }
140
- catch {
141
- return { version: STORE_VERSION, sessions: {} };
142
- }
143
- }
144
- /**
145
- * Write data atomically (write to temp file, then rename)
146
- */
147
- writeAtomic(data) {
148
- const tempFile = `${SESSIONS_FILE}.tmp`;
149
- writeFileSync(tempFile, JSON.stringify(data, null, 2), 'utf-8');
150
- renameSync(tempFile, SESSIONS_FILE);
151
- }
152
- }
@@ -1,140 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import type { PlatformUser, PlatformPost, PlatformReaction, PlatformFile } from './types.js';
3
- import type { PlatformFormatter } from './formatter.js';
4
- /**
5
- * Events emitted by PlatformClient
6
- */
7
- export interface PlatformClientEvents {
8
- connected: () => void;
9
- disconnected: () => void;
10
- error: (error: Error) => void;
11
- message: (post: PlatformPost, user: PlatformUser | null) => void;
12
- reaction: (reaction: PlatformReaction, user: PlatformUser | null) => void;
13
- }
14
- /**
15
- * Platform-agnostic client interface
16
- *
17
- * All platform implementations (Mattermost, Slack) must implement this interface.
18
- * This allows SessionManager and other code to work with any platform without
19
- * knowing the specific implementation details.
20
- */
21
- export interface PlatformClient extends EventEmitter {
22
- /**
23
- * Unique identifier for this platform instance
24
- * e.g., 'mattermost-internal', 'slack-eng'
25
- */
26
- readonly platformId: string;
27
- /**
28
- * Platform type
29
- * e.g., 'mattermost', 'slack'
30
- */
31
- readonly platformType: string;
32
- /**
33
- * Human-readable display name
34
- * e.g., 'Internal Team', 'Engineering Slack'
35
- */
36
- readonly displayName: string;
37
- /**
38
- * Connect to the platform (WebSocket, Socket Mode, etc.)
39
- */
40
- connect(): Promise<void>;
41
- /**
42
- * Disconnect from the platform
43
- */
44
- disconnect(): void;
45
- /**
46
- * Get the bot's own user info
47
- */
48
- getBotUser(): Promise<PlatformUser>;
49
- /**
50
- * Get a user by their ID
51
- */
52
- getUser(userId: string): Promise<PlatformUser | null>;
53
- /**
54
- * Check if a username is in the allowed users list
55
- */
56
- isUserAllowed(username: string): boolean;
57
- /**
58
- * Get the bot's mention name (e.g., 'claude-code')
59
- */
60
- getBotName(): string;
61
- /**
62
- * Get platform config for MCP permission server
63
- */
64
- getMcpConfig(): {
65
- type: string;
66
- url: string;
67
- token: string;
68
- channelId: string;
69
- allowedUsers: string[];
70
- };
71
- /**
72
- * Get the platform-specific markdown formatter
73
- * Use this to format bold, code, etc. in a platform-appropriate way.
74
- */
75
- getFormatter(): PlatformFormatter;
76
- /**
77
- * Create a new post/message
78
- * @param message - Message text
79
- * @param threadId - Optional thread parent ID
80
- * @returns The created post
81
- */
82
- createPost(message: string, threadId?: string): Promise<PlatformPost>;
83
- /**
84
- * Update an existing post/message
85
- * @param postId - Post ID to update
86
- * @param message - New message text
87
- * @returns The updated post
88
- */
89
- updatePost(postId: string, message: string): Promise<PlatformPost>;
90
- /**
91
- * Create a post with reaction options (for interactive prompts)
92
- * @param message - Message text
93
- * @param reactions - Array of emoji names to add as options
94
- * @param threadId - Optional thread parent ID
95
- * @returns The created post
96
- */
97
- createInteractivePost(message: string, reactions: string[], threadId?: string): Promise<PlatformPost>;
98
- /**
99
- * Get a post by ID
100
- * @param postId - Post ID
101
- * @returns The post, or null if not found/deleted
102
- */
103
- getPost(postId: string): Promise<PlatformPost | null>;
104
- /**
105
- * Add a reaction to a post
106
- * @param postId - Post ID
107
- * @param emojiName - Emoji name (e.g., '+1', 'white_check_mark')
108
- */
109
- addReaction(postId: string, emojiName: string): Promise<void>;
110
- /**
111
- * Check if a message mentions the bot
112
- * @param message - Message text
113
- */
114
- isBotMentioned(message: string): boolean;
115
- /**
116
- * Extract the prompt from a message (remove bot mention)
117
- * @param message - Message text
118
- * @returns The message with bot mention removed
119
- */
120
- extractPrompt(message: string): string;
121
- /**
122
- * Send typing indicator to show bot is "thinking"
123
- * @param threadId - Optional thread ID
124
- */
125
- sendTyping(threadId?: string): void;
126
- /**
127
- * Download a file attachment
128
- * @param fileId - File ID
129
- * @returns File contents as Buffer
130
- */
131
- downloadFile?(fileId: string): Promise<Buffer>;
132
- /**
133
- * Get file metadata
134
- * @param fileId - File ID
135
- * @returns File metadata
136
- */
137
- getFileInfo?(fileId: string): Promise<PlatformFile>;
138
- on<K extends keyof PlatformClientEvents>(event: K, listener: PlatformClientEvents[K]): this;
139
- emit<K extends keyof PlatformClientEvents>(event: K, ...args: Parameters<PlatformClientEvents[K]>): boolean;
140
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,74 +0,0 @@
1
- /**
2
- * Platform-agnostic markdown formatter interface
3
- *
4
- * Different platforms have slightly different markdown dialects:
5
- * - Mattermost: Standard markdown (**bold**, _italic_, @username)
6
- * - Slack: mrkdwn (*bold*, _italic_, <@U123> for mentions)
7
- *
8
- * This interface abstracts those differences.
9
- */
10
- export interface PlatformFormatter {
11
- /**
12
- * Format text as bold
13
- * Mattermost: **text**
14
- * Slack: *text*
15
- */
16
- formatBold(text: string): string;
17
- /**
18
- * Format text as italic
19
- * Mattermost: _text_ or *text*
20
- * Slack: _text_
21
- */
22
- formatItalic(text: string): string;
23
- /**
24
- * Format text as inline code
25
- * Both: `code`
26
- */
27
- formatCode(text: string): string;
28
- /**
29
- * Format text as code block with optional language
30
- * Both: ```lang\ncode\n```
31
- */
32
- formatCodeBlock(code: string, language?: string): string;
33
- /**
34
- * Format a user mention
35
- * Mattermost: @username
36
- * Slack: <@U123456> (requires user ID)
37
- */
38
- formatUserMention(username: string, userId?: string): string;
39
- /**
40
- * Format a hyperlink
41
- * Mattermost: [text](url)
42
- * Slack: <url|text>
43
- */
44
- formatLink(text: string, url: string): string;
45
- /**
46
- * Format a bulleted list item
47
- * Both: - item or * item
48
- */
49
- formatListItem(text: string): string;
50
- /**
51
- * Format a numbered list item
52
- * Both: 1. item
53
- */
54
- formatNumberedListItem(number: number, text: string): string;
55
- /**
56
- * Format a blockquote
57
- * Both: > text
58
- */
59
- formatBlockquote(text: string): string;
60
- /**
61
- * Format a horizontal rule
62
- * Both: ---
63
- */
64
- formatHorizontalRule(): string;
65
- /**
66
- * Format a heading
67
- * Both: # Heading (level 1), ## Heading (level 2), etc.
68
- */
69
- formatHeading(text: string, level: number): string;
70
- /**
71
- * Escape special characters in text to prevent formatting
72
- */
73
- escapeText(text: string): string;
74
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,11 +0,0 @@
1
- /**
2
- * Platform abstraction layer
3
- *
4
- * This module provides platform-agnostic interfaces and types that allow
5
- * claude-threads to work with multiple chat platforms (Mattermost, Slack, etc.)
6
- * without coupling the core logic to any specific platform.
7
- */
8
- export type { PlatformClient, PlatformClientEvents } from './client.js';
9
- export type { PlatformFormatter } from './formatter.js';
10
- export type { PermissionApi, PermissionApiConfig, ReactionEvent, PostedMessage, } from './permission-api.js';
11
- export type { PlatformUser, PlatformPost, PlatformReaction, PlatformFile, CreatePostRequest, UpdatePostRequest, AddReactionRequest, } from './types.js';
@@ -1,8 +0,0 @@
1
- /**
2
- * Platform abstraction layer
3
- *
4
- * This module provides platform-agnostic interfaces and types that allow
5
- * claude-threads to work with multiple chat platforms (Mattermost, Slack, etc.)
6
- * without coupling the core logic to any specific platform.
7
- */
8
- export {};
@@ -1,70 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import type { MattermostPlatformConfig } from '../../config/migration.js';
3
- import type { PlatformClient, PlatformUser, PlatformPost, PlatformFile } from '../index.js';
4
- import type { PlatformFormatter } from '../formatter.js';
5
- export declare class MattermostClient extends EventEmitter implements PlatformClient {
6
- readonly platformId: string;
7
- readonly platformType: "mattermost";
8
- readonly displayName: string;
9
- private ws;
10
- private url;
11
- private token;
12
- private channelId;
13
- private botName;
14
- private allowedUsers;
15
- private reconnectAttempts;
16
- private maxReconnectAttempts;
17
- private reconnectDelay;
18
- private userCache;
19
- private botUserId;
20
- private readonly formatter;
21
- private pingInterval;
22
- private lastMessageAt;
23
- private readonly PING_INTERVAL_MS;
24
- private readonly PING_TIMEOUT_MS;
25
- constructor(platformConfig: MattermostPlatformConfig);
26
- private normalizePlatformUser;
27
- private normalizePlatformPost;
28
- private normalizePlatformReaction;
29
- private normalizePlatformFile;
30
- private api;
31
- getBotUser(): Promise<PlatformUser>;
32
- getUser(userId: string): Promise<PlatformUser | null>;
33
- createPost(message: string, threadId?: string): Promise<PlatformPost>;
34
- updatePost(postId: string, message: string): Promise<PlatformPost>;
35
- addReaction(postId: string, emojiName: string): Promise<void>;
36
- /**
37
- * Create a post with reaction options for user interaction
38
- *
39
- * This is a common pattern for interactive posts that need user response
40
- * via reactions (e.g., approval prompts, questions, permission requests).
41
- *
42
- * @param message - Post message content
43
- * @param reactions - Array of emoji names to add as reaction options
44
- * @param threadId - Optional thread root ID
45
- * @returns The created post
46
- */
47
- createInteractivePost(message: string, reactions: string[], threadId?: string): Promise<PlatformPost>;
48
- downloadFile(fileId: string): Promise<Buffer>;
49
- getFileInfo(fileId: string): Promise<PlatformFile>;
50
- getPost(postId: string): Promise<PlatformPost | null>;
51
- connect(): Promise<void>;
52
- private handleEvent;
53
- private scheduleReconnect;
54
- private startHeartbeat;
55
- private stopHeartbeat;
56
- isUserAllowed(username: string): boolean;
57
- isBotMentioned(message: string): boolean;
58
- extractPrompt(message: string): string;
59
- getBotName(): string;
60
- getMcpConfig(): {
61
- type: string;
62
- url: string;
63
- token: string;
64
- channelId: string;
65
- allowedUsers: string[];
66
- };
67
- getFormatter(): PlatformFormatter;
68
- sendTyping(parentId?: string): void;
69
- disconnect(): void;
70
- }