jupyterlab_claude_code_extension 1.0.21 → 1.0.29
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 +1 -0
- package/lib/icons.js +7 -0
- package/lib/index.js +3 -0
- package/lib/types.d.ts +1 -0
- package/lib/widget.d.ts +9 -5
- package/lib/widget.js +56 -17
- package/package.json +1 -1
- package/src/icons.ts +9 -0
- package/src/index.ts +3 -0
- package/src/types.ts +1 -0
- package/src/widget.ts +78 -19
package/lib/icons.d.ts
CHANGED
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="13" height="13">
|
|
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
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
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
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;
|
|
@@ -36,6 +38,7 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
36
38
|
private _resumeInTerminal;
|
|
37
39
|
private _doResumeInTerminal;
|
|
38
40
|
private _findTerminalForCwd;
|
|
41
|
+
private _showCloseExistingDialog;
|
|
39
42
|
private _wireTerminalDisposal;
|
|
40
43
|
/** Apply the resolve-names setting + path-segment disambiguation. */
|
|
41
44
|
private _displayName;
|
|
@@ -73,6 +76,7 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
73
76
|
private readonly _rootDir;
|
|
74
77
|
private _resolveNames;
|
|
75
78
|
private _recentLimit;
|
|
79
|
+
private _dangerouslySkip;
|
|
76
80
|
private _displayNames;
|
|
77
81
|
private _filter;
|
|
78
82
|
}
|
package/lib/widget.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { Dialog, showDialog } from '@jupyterlab/apputils';
|
|
1
2
|
import { CommandRegistry } from '@lumino/commands';
|
|
2
3
|
import { Menu, Widget } from '@lumino/widgets';
|
|
3
4
|
import { requestAPI } from './request';
|
|
4
|
-
import { claudeIcon, refreshIcon, removeIcon, starFilledIcon } from './icons';
|
|
5
|
+
import { claudeIcon, refreshIcon, removeIcon, shieldIcon, starFilledIcon } from './icons';
|
|
5
6
|
const POLL_INTERVAL_MS = 10000;
|
|
6
7
|
const DEFAULT_RECENT_LIMIT = 10;
|
|
7
8
|
const EXPANDED_STORAGE_KEY = 'jupyterlab_claude_code_extension:expanded';
|
|
@@ -66,6 +67,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
66
67
|
this._pendingByPath = new Map();
|
|
67
68
|
this._resolveNames = true;
|
|
68
69
|
this._recentLimit = DEFAULT_RECENT_LIMIT;
|
|
70
|
+
this._dangerouslySkip = false;
|
|
69
71
|
this._displayNames = new Map();
|
|
70
72
|
this._filter = '';
|
|
71
73
|
this._app = app;
|
|
@@ -103,6 +105,10 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
103
105
|
this._recentLimit = clamped;
|
|
104
106
|
this._render();
|
|
105
107
|
}
|
|
108
|
+
/** Toggle the --dangerously-skip-permissions flag on launched sessions. */
|
|
109
|
+
setDangerouslySkipPermissions(on) {
|
|
110
|
+
this._dangerouslySkip = !!on;
|
|
111
|
+
}
|
|
106
112
|
onAfterShow(_msg) {
|
|
107
113
|
this.refresh();
|
|
108
114
|
this._startPolling();
|
|
@@ -163,11 +169,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
163
169
|
.toLowerCase()
|
|
164
170
|
.replace(/[\s\-_./]+/g, '');
|
|
165
171
|
}
|
|
166
|
-
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
172
|
+
/** Fuzzy match at a 95% threshold: substring on normalised strings,
|
|
173
|
+
* with up to 5% Levenshtein tolerance. For short queries the budget
|
|
174
|
+
* still rounds to zero so behaviour is substring-only there - the
|
|
175
|
+
* relaxation only kicks in for queries long enough that 5% reaches a
|
|
176
|
+
* full edit (10+ chars).
|
|
171
177
|
*/
|
|
172
178
|
_fuzzyMatch(haystack, needle) {
|
|
173
179
|
if (!needle) {
|
|
@@ -181,7 +187,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
181
187
|
if (h.includes(n)) {
|
|
182
188
|
return true;
|
|
183
189
|
}
|
|
184
|
-
const tol = Math.
|
|
190
|
+
const tol = Math.round(n.length * 0.05);
|
|
185
191
|
if (tol === 0) {
|
|
186
192
|
return false;
|
|
187
193
|
}
|
|
@@ -288,36 +294,47 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
288
294
|
}
|
|
289
295
|
}
|
|
290
296
|
// -------------------------------------------------------------- terminal
|
|
291
|
-
async _resumeInTerminal(session) {
|
|
297
|
+
async _resumeInTerminal(session, forceDangerous = false) {
|
|
292
298
|
// Coalesce concurrent clicks on the same row - subsequent clicks attach
|
|
293
299
|
// to the in-flight promise instead of creating their own terminal.
|
|
294
300
|
const inFlight = this._pendingByPath.get(session.project_path);
|
|
295
301
|
if (inFlight) {
|
|
296
302
|
return inFlight;
|
|
297
303
|
}
|
|
298
|
-
const promise = this._doResumeInTerminal(session).finally(() => {
|
|
304
|
+
const promise = this._doResumeInTerminal(session, forceDangerous).finally(() => {
|
|
299
305
|
this._pendingByPath.delete(session.project_path);
|
|
300
306
|
});
|
|
301
307
|
this._pendingByPath.set(session.project_path, promise);
|
|
302
308
|
return promise;
|
|
303
309
|
}
|
|
304
|
-
async _doResumeInTerminal(session) {
|
|
310
|
+
async _doResumeInTerminal(session, forceDangerous) {
|
|
305
311
|
try {
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
312
|
+
// Always prefer reusing an open terminal for this project. The
|
|
313
|
+
// skip-permissions flag can only be applied to a fresh pty, never
|
|
314
|
+
// retroactively. So if the user wants dangerous mode but an open
|
|
315
|
+
// terminal already exists, show a modal asking them to close it
|
|
316
|
+
// first - we won't auto-close, won't silently reuse the wrong mode.
|
|
317
|
+
// 1. In-memory microcache.
|
|
309
318
|
const cached = this._terminalsByPath.get(session.project_path);
|
|
310
319
|
if (cached && !cached.isDisposed) {
|
|
320
|
+
if (forceDangerous) {
|
|
321
|
+
await this._showCloseExistingDialog();
|
|
322
|
+
this._app.shell.activateById(cached.id);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
311
325
|
this._app.shell.activateById(cached.id);
|
|
312
326
|
return;
|
|
313
327
|
}
|
|
314
|
-
// 2. Walk every live terminal widget JL knows about
|
|
315
|
-
// server for the cwd of EVERY process in its pty's tree. Match if
|
|
316
|
-
// any one of them equals project_path.
|
|
328
|
+
// 2. Walk every live terminal widget JL knows about.
|
|
317
329
|
const found = await this._findTerminalForCwd(session.project_path);
|
|
318
330
|
if (found) {
|
|
319
331
|
this._terminalsByPath.set(session.project_path, found);
|
|
320
332
|
this._wireTerminalDisposal(session.project_path, found);
|
|
333
|
+
if (forceDangerous) {
|
|
334
|
+
await this._showCloseExistingDialog();
|
|
335
|
+
this._app.shell.activateById(found.id);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
321
338
|
this._app.shell.activateById(found.id);
|
|
322
339
|
return;
|
|
323
340
|
}
|
|
@@ -330,7 +347,8 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
330
347
|
method: 'POST',
|
|
331
348
|
body: JSON.stringify({
|
|
332
349
|
project_path: session.project_path,
|
|
333
|
-
session_id: session.session_id
|
|
350
|
+
session_id: session.session_id,
|
|
351
|
+
dangerously_skip_permissions: forceDangerous || this._dangerouslySkip
|
|
334
352
|
})
|
|
335
353
|
});
|
|
336
354
|
const widget = await this._app.commands.execute('terminal:open', {
|
|
@@ -378,6 +396,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
378
396
|
}
|
|
379
397
|
return null;
|
|
380
398
|
}
|
|
399
|
+
async _showCloseExistingDialog() {
|
|
400
|
+
await showDialog({
|
|
401
|
+
title: 'Existing Claude session is running',
|
|
402
|
+
body: 'A terminal for this project is already open. To launch with ' +
|
|
403
|
+
'--dangerously-skip-permissions, close that terminal first then ' +
|
|
404
|
+
'click "Resume (Skip Permissions)" again.',
|
|
405
|
+
buttons: [Dialog.okButton({ label: 'OK' })]
|
|
406
|
+
});
|
|
407
|
+
}
|
|
381
408
|
_wireTerminalDisposal(projectPath, widget) {
|
|
382
409
|
var _a;
|
|
383
410
|
if (!((_a = widget === null || widget === void 0 ? void 0 : widget.disposed) === null || _a === void 0 ? void 0 : _a.connect)) {
|
|
@@ -679,6 +706,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
679
706
|
}
|
|
680
707
|
}
|
|
681
708
|
});
|
|
709
|
+
this._commands.addCommand('claude-code-sessions:resume-dangerous', {
|
|
710
|
+
label: 'Resume (Skip Permissions)',
|
|
711
|
+
icon: shieldIcon,
|
|
712
|
+
execute: () => {
|
|
713
|
+
if (this._activeSession) {
|
|
714
|
+
void this._resumeInTerminal(this._activeSession, true);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
});
|
|
682
718
|
this._commands.addCommand('claude-code-sessions:remove', {
|
|
683
719
|
label: 'Remove from Claude',
|
|
684
720
|
icon: removeIcon,
|
|
@@ -691,6 +727,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
691
727
|
this._contextMenu = new Menu({ commands: this._commands });
|
|
692
728
|
this._contextMenu.addClass('jp-ClaudeSessionsContextMenu');
|
|
693
729
|
this._contextMenu.addItem({ command: 'claude-code-sessions:resume' });
|
|
730
|
+
this._contextMenu.addItem({
|
|
731
|
+
command: 'claude-code-sessions:resume-dangerous'
|
|
732
|
+
});
|
|
694
733
|
this._contextMenu.addItem({
|
|
695
734
|
command: 'claude-code-sessions:toggle-favourite'
|
|
696
735
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab_claude_code_extension",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
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="13" height="13">
|
|
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
package/src/widget.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
2
|
+
import { Dialog, showDialog } from '@jupyterlab/apputils';
|
|
2
3
|
import { ServerConnection } from '@jupyterlab/services';
|
|
3
4
|
import { ITerminalTracker } from '@jupyterlab/terminal';
|
|
4
5
|
import { CommandRegistry } from '@lumino/commands';
|
|
@@ -6,7 +7,13 @@ import { Menu, Widget } from '@lumino/widgets';
|
|
|
6
7
|
import { Message } from '@lumino/messaging';
|
|
7
8
|
|
|
8
9
|
import { requestAPI } from './request';
|
|
9
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
claudeIcon,
|
|
12
|
+
refreshIcon,
|
|
13
|
+
removeIcon,
|
|
14
|
+
shieldIcon,
|
|
15
|
+
starFilledIcon
|
|
16
|
+
} from './icons';
|
|
10
17
|
import {
|
|
11
18
|
IFavouriteResponse,
|
|
12
19
|
ILaunchTerminalResponse,
|
|
@@ -125,6 +132,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
125
132
|
this._render();
|
|
126
133
|
}
|
|
127
134
|
|
|
135
|
+
/** Toggle the --dangerously-skip-permissions flag on launched sessions. */
|
|
136
|
+
setDangerouslySkipPermissions(on: boolean): void {
|
|
137
|
+
this._dangerouslySkip = !!on;
|
|
138
|
+
}
|
|
139
|
+
|
|
128
140
|
protected onAfterShow(_msg: Message): void {
|
|
129
141
|
this.refresh();
|
|
130
142
|
this._startPolling();
|
|
@@ -199,11 +211,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
199
211
|
.replace(/[\s\-_./]+/g, '');
|
|
200
212
|
}
|
|
201
213
|
|
|
202
|
-
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
214
|
+
/** Fuzzy match at a 95% threshold: substring on normalised strings,
|
|
215
|
+
* with up to 5% Levenshtein tolerance. For short queries the budget
|
|
216
|
+
* still rounds to zero so behaviour is substring-only there - the
|
|
217
|
+
* relaxation only kicks in for queries long enough that 5% reaches a
|
|
218
|
+
* full edit (10+ chars).
|
|
207
219
|
*/
|
|
208
220
|
private _fuzzyMatch(haystack: string, needle: string): boolean {
|
|
209
221
|
if (!needle) {
|
|
@@ -217,7 +229,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
217
229
|
if (h.includes(n)) {
|
|
218
230
|
return true;
|
|
219
231
|
}
|
|
220
|
-
const tol = Math.
|
|
232
|
+
const tol = Math.round(n.length * 0.05);
|
|
221
233
|
if (tol === 0) {
|
|
222
234
|
return false;
|
|
223
235
|
}
|
|
@@ -344,38 +356,58 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
344
356
|
|
|
345
357
|
// -------------------------------------------------------------- terminal
|
|
346
358
|
|
|
347
|
-
private async _resumeInTerminal(
|
|
359
|
+
private async _resumeInTerminal(
|
|
360
|
+
session: ISession,
|
|
361
|
+
forceDangerous: boolean = false
|
|
362
|
+
): Promise<void> {
|
|
348
363
|
// Coalesce concurrent clicks on the same row - subsequent clicks attach
|
|
349
364
|
// to the in-flight promise instead of creating their own terminal.
|
|
350
365
|
const inFlight = this._pendingByPath.get(session.project_path);
|
|
351
366
|
if (inFlight) {
|
|
352
367
|
return inFlight;
|
|
353
368
|
}
|
|
354
|
-
const promise = this._doResumeInTerminal(session).finally(
|
|
355
|
-
|
|
356
|
-
|
|
369
|
+
const promise = this._doResumeInTerminal(session, forceDangerous).finally(
|
|
370
|
+
() => {
|
|
371
|
+
this._pendingByPath.delete(session.project_path);
|
|
372
|
+
}
|
|
373
|
+
);
|
|
357
374
|
this._pendingByPath.set(session.project_path, promise);
|
|
358
375
|
return promise;
|
|
359
376
|
}
|
|
360
377
|
|
|
361
|
-
private async _doResumeInTerminal(
|
|
378
|
+
private async _doResumeInTerminal(
|
|
379
|
+
session: ISession,
|
|
380
|
+
forceDangerous: boolean
|
|
381
|
+
): Promise<void> {
|
|
362
382
|
try {
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
383
|
+
// Always prefer reusing an open terminal for this project. The
|
|
384
|
+
// skip-permissions flag can only be applied to a fresh pty, never
|
|
385
|
+
// retroactively. So if the user wants dangerous mode but an open
|
|
386
|
+
// terminal already exists, show a modal asking them to close it
|
|
387
|
+
// first - we won't auto-close, won't silently reuse the wrong mode.
|
|
388
|
+
|
|
389
|
+
// 1. In-memory microcache.
|
|
366
390
|
const cached = this._terminalsByPath.get(session.project_path);
|
|
367
391
|
if (cached && !cached.isDisposed) {
|
|
392
|
+
if (forceDangerous) {
|
|
393
|
+
await this._showCloseExistingDialog();
|
|
394
|
+
this._app.shell.activateById(cached.id);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
368
397
|
this._app.shell.activateById(cached.id);
|
|
369
398
|
return;
|
|
370
399
|
}
|
|
371
400
|
|
|
372
|
-
// 2. Walk every live terminal widget JL knows about
|
|
373
|
-
// server for the cwd of EVERY process in its pty's tree. Match if
|
|
374
|
-
// any one of them equals project_path.
|
|
401
|
+
// 2. Walk every live terminal widget JL knows about.
|
|
375
402
|
const found = await this._findTerminalForCwd(session.project_path);
|
|
376
403
|
if (found) {
|
|
377
404
|
this._terminalsByPath.set(session.project_path, found);
|
|
378
405
|
this._wireTerminalDisposal(session.project_path, found);
|
|
406
|
+
if (forceDangerous) {
|
|
407
|
+
await this._showCloseExistingDialog();
|
|
408
|
+
this._app.shell.activateById(found.id);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
379
411
|
this._app.shell.activateById(found.id);
|
|
380
412
|
return;
|
|
381
413
|
}
|
|
@@ -392,7 +424,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
392
424
|
method: 'POST',
|
|
393
425
|
body: JSON.stringify({
|
|
394
426
|
project_path: session.project_path,
|
|
395
|
-
session_id: session.session_id
|
|
427
|
+
session_id: session.session_id,
|
|
428
|
+
dangerously_skip_permissions:
|
|
429
|
+
forceDangerous || this._dangerouslySkip
|
|
396
430
|
})
|
|
397
431
|
}
|
|
398
432
|
);
|
|
@@ -443,6 +477,17 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
443
477
|
return null;
|
|
444
478
|
}
|
|
445
479
|
|
|
480
|
+
private async _showCloseExistingDialog(): Promise<void> {
|
|
481
|
+
await showDialog({
|
|
482
|
+
title: 'Existing Claude session is running',
|
|
483
|
+
body:
|
|
484
|
+
'A terminal for this project is already open. To launch with ' +
|
|
485
|
+
'--dangerously-skip-permissions, close that terminal first then ' +
|
|
486
|
+
'click "Resume (Skip Permissions)" again.',
|
|
487
|
+
buttons: [Dialog.okButton({ label: 'OK' })]
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
446
491
|
private _wireTerminalDisposal(projectPath: string, widget: any): void {
|
|
447
492
|
if (!widget?.disposed?.connect) {
|
|
448
493
|
return;
|
|
@@ -779,6 +824,16 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
779
824
|
}
|
|
780
825
|
});
|
|
781
826
|
|
|
827
|
+
this._commands.addCommand('claude-code-sessions:resume-dangerous', {
|
|
828
|
+
label: 'Resume (Skip Permissions)',
|
|
829
|
+
icon: shieldIcon,
|
|
830
|
+
execute: () => {
|
|
831
|
+
if (this._activeSession) {
|
|
832
|
+
void this._resumeInTerminal(this._activeSession, true);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
|
|
782
837
|
this._commands.addCommand('claude-code-sessions:remove', {
|
|
783
838
|
label: 'Remove from Claude',
|
|
784
839
|
icon: removeIcon,
|
|
@@ -792,6 +847,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
792
847
|
this._contextMenu = new Menu({ commands: this._commands });
|
|
793
848
|
this._contextMenu.addClass('jp-ClaudeSessionsContextMenu');
|
|
794
849
|
this._contextMenu.addItem({ command: 'claude-code-sessions:resume' });
|
|
850
|
+
this._contextMenu.addItem({
|
|
851
|
+
command: 'claude-code-sessions:resume-dangerous'
|
|
852
|
+
});
|
|
795
853
|
this._contextMenu.addItem({
|
|
796
854
|
command: 'claude-code-sessions:toggle-favourite'
|
|
797
855
|
});
|
|
@@ -848,6 +906,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
848
906
|
private readonly _rootDir: string;
|
|
849
907
|
private _resolveNames: boolean = true;
|
|
850
908
|
private _recentLimit: number = DEFAULT_RECENT_LIMIT;
|
|
909
|
+
private _dangerouslySkip: boolean = false;
|
|
851
910
|
private _displayNames: Map<string, string> = new Map();
|
|
852
911
|
private _filter: string = '';
|
|
853
912
|
}
|