jupyterlab_claude_code_extension 1.0.21 → 1.0.26

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/lib/icons.d.ts CHANGED
@@ -3,3 +3,4 @@ export declare const claudeIcon: LabIcon;
3
3
  export declare const starFilledIcon: LabIcon;
4
4
  export declare const refreshIcon: LabIcon;
5
5
  export declare const removeIcon: LabIcon;
6
+ export declare const shieldIcon: LabIcon;
package/lib/icons.js CHANGED
@@ -42,3 +42,10 @@ export const removeIcon = new LabIcon({
42
42
  name: 'jupyterlab_claude_code_extension:remove',
43
43
  svgstr: removeSvgStr
44
44
  });
45
+ const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
46
+ <path class="jp-icon3" fill="#616161" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/>
47
+ </svg>`;
48
+ export const shieldIcon = new LabIcon({
49
+ name: 'jupyterlab_claude_code_extension:shield',
50
+ svgstr: shieldSvgStr
51
+ });
package/lib/index.js CHANGED
@@ -38,6 +38,9 @@ const plugin = {
38
38
  if (typeof limit === 'number') {
39
39
  widget.setRecentLimit(limit);
40
40
  }
41
+ const dangerous = settings.get('dangerouslySkipPermissions')
42
+ .composite;
43
+ widget.setDangerouslySkipPermissions(!!dangerous);
41
44
  };
42
45
  apply();
43
46
  settings.changed.connect(apply);
package/lib/types.d.ts CHANGED
@@ -37,6 +37,7 @@ export interface IRemoveResponse {
37
37
  export interface ILaunchTerminalRequest {
38
38
  project_path: string;
39
39
  session_id: string;
40
+ dangerously_skip_permissions?: boolean;
40
41
  }
41
42
  export interface ILaunchTerminalResponse {
42
43
  terminal_name: string;
package/lib/widget.d.ts CHANGED
@@ -9,6 +9,8 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
9
9
  setResolveSessionNames(on: boolean): void;
10
10
  /** Set how many rows the Recent section displays. */
11
11
  setRecentLimit(n: number): void;
12
+ /** Toggle the --dangerously-skip-permissions flag on launched sessions. */
13
+ setDangerouslySkipPermissions(on: boolean): void;
12
14
  protected onAfterShow(_msg: Message): void;
13
15
  protected onBeforeHide(_msg: Message): void;
14
16
  protected onCloseRequest(msg: Message): void;
@@ -19,11 +21,11 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
19
21
  * compare equal as "foobar".
20
22
  */
21
23
  private _normalize;
22
- /** Strict fuzzy match: substring on normalised strings, with up to 2%
23
- * Levenshtein tolerance for queries of 6+ chars (so the 98% threshold
24
- * only relaxes substring strictness when the query is long enough that
25
- * 2% rounds to a nonzero edit budget - effectively substring-only for
26
- * short queries).
24
+ /** Fuzzy match at a 95% threshold: substring on normalised strings,
25
+ * with up to 5% Levenshtein tolerance. For short queries the budget
26
+ * still rounds to zero so behaviour is substring-only there - the
27
+ * relaxation only kicks in for queries long enough that 5% reaches a
28
+ * full edit (10+ chars).
27
29
  */
28
30
  private _fuzzyMatch;
29
31
  private _levenshtein;
@@ -73,6 +75,7 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
73
75
  private readonly _rootDir;
74
76
  private _resolveNames;
75
77
  private _recentLimit;
78
+ private _dangerouslySkip;
76
79
  private _displayNames;
77
80
  private _filter;
78
81
  }
package/lib/widget.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CommandRegistry } from '@lumino/commands';
2
2
  import { Menu, Widget } from '@lumino/widgets';
3
3
  import { requestAPI } from './request';
4
- import { claudeIcon, refreshIcon, removeIcon, starFilledIcon } from './icons';
4
+ import { claudeIcon, refreshIcon, removeIcon, shieldIcon, starFilledIcon } from './icons';
5
5
  const POLL_INTERVAL_MS = 10000;
6
6
  const DEFAULT_RECENT_LIMIT = 10;
7
7
  const EXPANDED_STORAGE_KEY = 'jupyterlab_claude_code_extension:expanded';
@@ -66,6 +66,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
66
66
  this._pendingByPath = new Map();
67
67
  this._resolveNames = true;
68
68
  this._recentLimit = DEFAULT_RECENT_LIMIT;
69
+ this._dangerouslySkip = false;
69
70
  this._displayNames = new Map();
70
71
  this._filter = '';
71
72
  this._app = app;
@@ -103,6 +104,10 @@ export class ClaudeCodeSessionsWidget extends Widget {
103
104
  this._recentLimit = clamped;
104
105
  this._render();
105
106
  }
107
+ /** Toggle the --dangerously-skip-permissions flag on launched sessions. */
108
+ setDangerouslySkipPermissions(on) {
109
+ this._dangerouslySkip = !!on;
110
+ }
106
111
  onAfterShow(_msg) {
107
112
  this.refresh();
108
113
  this._startPolling();
@@ -163,11 +168,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
163
168
  .toLowerCase()
164
169
  .replace(/[\s\-_./]+/g, '');
165
170
  }
166
- /** Strict fuzzy match: substring on normalised strings, with up to 2%
167
- * Levenshtein tolerance for queries of 6+ chars (so the 98% threshold
168
- * only relaxes substring strictness when the query is long enough that
169
- * 2% rounds to a nonzero edit budget - effectively substring-only for
170
- * short queries).
171
+ /** Fuzzy match at a 95% threshold: substring on normalised strings,
172
+ * with up to 5% Levenshtein tolerance. For short queries the budget
173
+ * still rounds to zero so behaviour is substring-only there - the
174
+ * relaxation only kicks in for queries long enough that 5% reaches a
175
+ * full edit (10+ chars).
171
176
  */
172
177
  _fuzzyMatch(haystack, needle) {
173
178
  if (!needle) {
@@ -181,7 +186,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
181
186
  if (h.includes(n)) {
182
187
  return true;
183
188
  }
184
- const tol = Math.floor(n.length * 0.02);
189
+ const tol = Math.round(n.length * 0.05);
185
190
  if (tol === 0) {
186
191
  return false;
187
192
  }
@@ -288,20 +293,20 @@ export class ClaudeCodeSessionsWidget extends Widget {
288
293
  }
289
294
  }
290
295
  // -------------------------------------------------------------- terminal
291
- async _resumeInTerminal(session) {
296
+ async _resumeInTerminal(session, forceDangerous = false) {
292
297
  // Coalesce concurrent clicks on the same row - subsequent clicks attach
293
298
  // to the in-flight promise instead of creating their own terminal.
294
299
  const inFlight = this._pendingByPath.get(session.project_path);
295
300
  if (inFlight) {
296
301
  return inFlight;
297
302
  }
298
- const promise = this._doResumeInTerminal(session).finally(() => {
303
+ const promise = this._doResumeInTerminal(session, forceDangerous).finally(() => {
299
304
  this._pendingByPath.delete(session.project_path);
300
305
  });
301
306
  this._pendingByPath.set(session.project_path, promise);
302
307
  return promise;
303
308
  }
304
- async _doResumeInTerminal(session) {
309
+ async _doResumeInTerminal(session, forceDangerous) {
305
310
  try {
306
311
  // 1. In-memory microcache - covers rapid-click and post-creation reuse
307
312
  // before the new widget propagates fully through the tracker. Cleared
@@ -330,7 +335,8 @@ export class ClaudeCodeSessionsWidget extends Widget {
330
335
  method: 'POST',
331
336
  body: JSON.stringify({
332
337
  project_path: session.project_path,
333
- session_id: session.session_id
338
+ session_id: session.session_id,
339
+ dangerously_skip_permissions: forceDangerous || this._dangerouslySkip
334
340
  })
335
341
  });
336
342
  const widget = await this._app.commands.execute('terminal:open', {
@@ -679,6 +685,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
679
685
  }
680
686
  }
681
687
  });
688
+ this._commands.addCommand('claude-code-sessions:resume-dangerous', {
689
+ label: 'Resume (Skip Permissions)',
690
+ icon: shieldIcon,
691
+ execute: () => {
692
+ if (this._activeSession) {
693
+ void this._resumeInTerminal(this._activeSession, true);
694
+ }
695
+ }
696
+ });
682
697
  this._commands.addCommand('claude-code-sessions:remove', {
683
698
  label: 'Remove from Claude',
684
699
  icon: removeIcon,
@@ -691,6 +706,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
691
706
  this._contextMenu = new Menu({ commands: this._commands });
692
707
  this._contextMenu.addClass('jp-ClaudeSessionsContextMenu');
693
708
  this._contextMenu.addItem({ command: 'claude-code-sessions:resume' });
709
+ this._contextMenu.addItem({
710
+ command: 'claude-code-sessions:resume-dangerous'
711
+ });
694
712
  this._contextMenu.addItem({
695
713
  command: 'claude-code-sessions:toggle-favourite'
696
714
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_claude_code_extension",
3
- "version": "1.0.21",
3
+ "version": "1.0.26",
4
4
  "description": "Browse, resume, and manage your Claude Code CLI sessions from a JupyterLab side panel. One click reactivates the right terminal - no duplicate tabs, live remote-control indicator, and favourites for the projects you keep coming back to.",
5
5
  "keywords": [
6
6
  "jupyter",
package/src/icons.ts CHANGED
@@ -50,3 +50,12 @@ export const removeIcon = new LabIcon({
50
50
  name: 'jupyterlab_claude_code_extension:remove',
51
51
  svgstr: removeSvgStr
52
52
  });
53
+
54
+ const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
55
+ <path class="jp-icon3" fill="#616161" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/>
56
+ </svg>`;
57
+
58
+ export const shieldIcon = new LabIcon({
59
+ name: 'jupyterlab_claude_code_extension:shield',
60
+ svgstr: shieldSvgStr
61
+ });
package/src/index.ts CHANGED
@@ -66,6 +66,9 @@ const plugin: JupyterFrontEndPlugin<void> = {
66
66
  if (typeof limit === 'number') {
67
67
  widget.setRecentLimit(limit);
68
68
  }
69
+ const dangerous = settings.get('dangerouslySkipPermissions')
70
+ .composite as boolean;
71
+ widget.setDangerouslySkipPermissions(!!dangerous);
69
72
  };
70
73
  apply();
71
74
  settings.changed.connect(apply);
package/src/types.ts CHANGED
@@ -44,6 +44,7 @@ export interface IRemoveResponse {
44
44
  export interface ILaunchTerminalRequest {
45
45
  project_path: string;
46
46
  session_id: string;
47
+ dangerously_skip_permissions?: boolean;
47
48
  }
48
49
 
49
50
  export interface ILaunchTerminalResponse {
package/src/widget.ts CHANGED
@@ -6,7 +6,13 @@ import { Menu, Widget } from '@lumino/widgets';
6
6
  import { Message } from '@lumino/messaging';
7
7
 
8
8
  import { requestAPI } from './request';
9
- import { claudeIcon, refreshIcon, removeIcon, starFilledIcon } from './icons';
9
+ import {
10
+ claudeIcon,
11
+ refreshIcon,
12
+ removeIcon,
13
+ shieldIcon,
14
+ starFilledIcon
15
+ } from './icons';
10
16
  import {
11
17
  IFavouriteResponse,
12
18
  ILaunchTerminalResponse,
@@ -125,6 +131,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
125
131
  this._render();
126
132
  }
127
133
 
134
+ /** Toggle the --dangerously-skip-permissions flag on launched sessions. */
135
+ setDangerouslySkipPermissions(on: boolean): void {
136
+ this._dangerouslySkip = !!on;
137
+ }
138
+
128
139
  protected onAfterShow(_msg: Message): void {
129
140
  this.refresh();
130
141
  this._startPolling();
@@ -199,11 +210,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
199
210
  .replace(/[\s\-_./]+/g, '');
200
211
  }
201
212
 
202
- /** Strict fuzzy match: substring on normalised strings, with up to 2%
203
- * Levenshtein tolerance for queries of 6+ chars (so the 98% threshold
204
- * only relaxes substring strictness when the query is long enough that
205
- * 2% rounds to a nonzero edit budget - effectively substring-only for
206
- * short queries).
213
+ /** Fuzzy match at a 95% threshold: substring on normalised strings,
214
+ * with up to 5% Levenshtein tolerance. For short queries the budget
215
+ * still rounds to zero so behaviour is substring-only there - the
216
+ * relaxation only kicks in for queries long enough that 5% reaches a
217
+ * full edit (10+ chars).
207
218
  */
208
219
  private _fuzzyMatch(haystack: string, needle: string): boolean {
209
220
  if (!needle) {
@@ -217,7 +228,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
217
228
  if (h.includes(n)) {
218
229
  return true;
219
230
  }
220
- const tol = Math.floor(n.length * 0.02);
231
+ const tol = Math.round(n.length * 0.05);
221
232
  if (tol === 0) {
222
233
  return false;
223
234
  }
@@ -344,21 +355,29 @@ export class ClaudeCodeSessionsWidget extends Widget {
344
355
 
345
356
  // -------------------------------------------------------------- terminal
346
357
 
347
- private async _resumeInTerminal(session: ISession): Promise<void> {
358
+ private async _resumeInTerminal(
359
+ session: ISession,
360
+ forceDangerous: boolean = false
361
+ ): Promise<void> {
348
362
  // Coalesce concurrent clicks on the same row - subsequent clicks attach
349
363
  // to the in-flight promise instead of creating their own terminal.
350
364
  const inFlight = this._pendingByPath.get(session.project_path);
351
365
  if (inFlight) {
352
366
  return inFlight;
353
367
  }
354
- const promise = this._doResumeInTerminal(session).finally(() => {
355
- this._pendingByPath.delete(session.project_path);
356
- });
368
+ const promise = this._doResumeInTerminal(session, forceDangerous).finally(
369
+ () => {
370
+ this._pendingByPath.delete(session.project_path);
371
+ }
372
+ );
357
373
  this._pendingByPath.set(session.project_path, promise);
358
374
  return promise;
359
375
  }
360
376
 
361
- private async _doResumeInTerminal(session: ISession): Promise<void> {
377
+ private async _doResumeInTerminal(
378
+ session: ISession,
379
+ forceDangerous: boolean
380
+ ): Promise<void> {
362
381
  try {
363
382
  // 1. In-memory microcache - covers rapid-click and post-creation reuse
364
383
  // before the new widget propagates fully through the tracker. Cleared
@@ -392,7 +411,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
392
411
  method: 'POST',
393
412
  body: JSON.stringify({
394
413
  project_path: session.project_path,
395
- session_id: session.session_id
414
+ session_id: session.session_id,
415
+ dangerously_skip_permissions:
416
+ forceDangerous || this._dangerouslySkip
396
417
  })
397
418
  }
398
419
  );
@@ -779,6 +800,16 @@ export class ClaudeCodeSessionsWidget extends Widget {
779
800
  }
780
801
  });
781
802
 
803
+ this._commands.addCommand('claude-code-sessions:resume-dangerous', {
804
+ label: 'Resume (Skip Permissions)',
805
+ icon: shieldIcon,
806
+ execute: () => {
807
+ if (this._activeSession) {
808
+ void this._resumeInTerminal(this._activeSession, true);
809
+ }
810
+ }
811
+ });
812
+
782
813
  this._commands.addCommand('claude-code-sessions:remove', {
783
814
  label: 'Remove from Claude',
784
815
  icon: removeIcon,
@@ -792,6 +823,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
792
823
  this._contextMenu = new Menu({ commands: this._commands });
793
824
  this._contextMenu.addClass('jp-ClaudeSessionsContextMenu');
794
825
  this._contextMenu.addItem({ command: 'claude-code-sessions:resume' });
826
+ this._contextMenu.addItem({
827
+ command: 'claude-code-sessions:resume-dangerous'
828
+ });
795
829
  this._contextMenu.addItem({
796
830
  command: 'claude-code-sessions:toggle-favourite'
797
831
  });
@@ -848,6 +882,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
848
882
  private readonly _rootDir: string;
849
883
  private _resolveNames: boolean = true;
850
884
  private _recentLimit: number = DEFAULT_RECENT_LIMIT;
885
+ private _dangerouslySkip: boolean = false;
851
886
  private _displayNames: Map<string, string> = new Map();
852
887
  private _filter: string = '';
853
888
  }