mattermost-claude-code 0.5.2 → 0.5.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
@@ -146,6 +146,16 @@ React 👍 to allow this message, ✅ to invite them to the session, 👎 to den
146
146
 
147
147
  Messages starting with `@someone-else` are ignored by the bot, allowing side conversations in the thread without triggering Claude.
148
148
 
149
+ ### Downgrade Permissions
150
+
151
+ If the bot is running with `--skip-permissions` (auto mode), you can enable interactive permissions for a specific session:
152
+
153
+ ```
154
+ /permissions interactive
155
+ ```
156
+
157
+ This allows collaboration by requiring approval for Claude's actions. Note: you can only downgrade (auto → interactive), not upgrade - this ensures security.
158
+
149
159
  ## Interactive Features
150
160
 
151
161
  ### Permission Approval
@@ -45,6 +45,7 @@ interface Session {
45
45
  pendingMessageApproval: PendingMessageApproval | null;
46
46
  planApproved: boolean;
47
47
  sessionAllowedUsers: Set<string>;
48
+ forceInteractivePermissions: boolean;
48
49
  sessionStartPostId: string | null;
49
50
  tasksPostId: string | null;
50
51
  activeSubagents: Map<string, string>;
@@ -111,6 +112,13 @@ export declare class SessionManager {
111
112
  inviteUser(threadId: string, invitedUser: string, invitedBy: string): Promise<void>;
112
113
  /** Kick a user from a specific session */
113
114
  kickUser(threadId: string, kickedUser: string, kickedBy: string): Promise<void>;
115
+ /**
116
+ * Enable interactive permissions for a session.
117
+ * Can only downgrade (skip → interactive), not upgrade.
118
+ */
119
+ enableInteractivePermissions(threadId: string, username: string): Promise<void>;
120
+ /** Check if a session should use interactive permissions */
121
+ isSessionInteractive(threadId: string): boolean;
114
122
  /** Update the session header post with current participants */
115
123
  private updateSessionHeader;
116
124
  /** Request approval for a message from an unauthorized user */
@@ -121,6 +121,7 @@ export class SessionManager {
121
121
  pendingMessageApproval: null,
122
122
  planApproved: false,
123
123
  sessionAllowedUsers: new Set([username]), // Owner is always allowed
124
+ forceInteractivePermissions: false, // Can be enabled via /permissions interactive
124
125
  sessionStartPostId: post.id, // Track for updating participants
125
126
  tasksPostId: null,
126
127
  activeSubagents: new Map(),
@@ -813,12 +814,53 @@ export class SessionManager {
813
814
  await this.mattermost.createPost(`âš ī¸ @${kickedUser} was not in this session`, threadId);
814
815
  }
815
816
  }
817
+ /**
818
+ * Enable interactive permissions for a session.
819
+ * Can only downgrade (skip → interactive), not upgrade.
820
+ */
821
+ async enableInteractivePermissions(threadId, username) {
822
+ const session = this.sessions.get(threadId);
823
+ if (!session)
824
+ return;
825
+ // Only session owner or globally allowed users can change permissions
826
+ if (session.startedBy !== username && !this.mattermost.isUserAllowed(username)) {
827
+ await this.mattermost.createPost(`âš ī¸ Only @${session.startedBy} or allowed users can change permissions`, threadId);
828
+ return;
829
+ }
830
+ // Can only downgrade, not upgrade
831
+ if (!this.skipPermissions) {
832
+ await this.mattermost.createPost(`â„šī¸ Permissions are already interactive for this session`, threadId);
833
+ return;
834
+ }
835
+ // Already enabled for this session
836
+ if (session.forceInteractivePermissions) {
837
+ await this.mattermost.createPost(`â„šī¸ Interactive permissions already enabled for this session`, threadId);
838
+ return;
839
+ }
840
+ session.forceInteractivePermissions = true;
841
+ await this.mattermost.createPost(`🔐 Interactive permissions enabled for this session by @${username}`, threadId);
842
+ console.log(` 🔐 Interactive permissions enabled for session by @${username}`);
843
+ await this.updateSessionHeader(session);
844
+ }
845
+ /** Check if a session should use interactive permissions */
846
+ isSessionInteractive(threadId) {
847
+ const session = this.sessions.get(threadId);
848
+ if (!session)
849
+ return !this.skipPermissions;
850
+ // If global is interactive, always interactive
851
+ if (!this.skipPermissions)
852
+ return true;
853
+ // If session has forced interactive, use that
854
+ return session.forceInteractivePermissions;
855
+ }
816
856
  /** Update the session header post with current participants */
817
857
  async updateSessionHeader(session) {
818
858
  if (!session.sessionStartPostId)
819
859
  return;
820
860
  const shortDir = this.workingDir.replace(process.env.HOME || '', '~');
821
- const permMode = this.skipPermissions ? '⚡ Auto' : '🔐 Interactive';
861
+ // Check session-level permission override
862
+ const isInteractive = !this.skipPermissions || session.forceInteractivePermissions;
863
+ const permMode = isInteractive ? '🔐 Interactive' : '⚡ Auto';
822
864
  // Build participants list (excluding owner who is shown in "Started by")
823
865
  const otherParticipants = [...session.sessionAllowedUsers]
824
866
  .filter(u => u !== session.startedBy)
package/dist/index.js CHANGED
@@ -102,6 +102,19 @@ async function main() {
102
102
  await session.kickUser(threadRoot, kickMatch[1], username);
103
103
  return;
104
104
  }
105
+ // Check for /permissions command
106
+ const permMatch = content.match(/^\/permissions?\s+(interactive|auto)/i);
107
+ if (permMatch) {
108
+ const mode = permMatch[1].toLowerCase();
109
+ if (mode === 'interactive') {
110
+ await session.enableInteractivePermissions(threadRoot, username);
111
+ }
112
+ else {
113
+ // Can't upgrade to auto - that would be less secure
114
+ await mattermost.createPost(`âš ī¸ Cannot upgrade to auto permissions - can only downgrade to interactive`, threadRoot);
115
+ }
116
+ return;
117
+ }
105
118
  // Check if user is allowed in this session
106
119
  if (!session.isUserAllowedInSession(threadRoot, username)) {
107
120
  // Request approval for their message
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mattermost-claude-code",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
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",