xtralab 0.15.0 → 0.15.2

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 (237) hide show
  1. package/README.md +65 -30
  2. package/lib/about/icons.d.ts +0 -5
  3. package/lib/about/icons.js +0 -5
  4. package/lib/about/index.d.ts +4 -8
  5. package/lib/about/index.js +6 -17
  6. package/lib/agentSessions.d.ts +9 -29
  7. package/lib/agentSessions.js +2 -7
  8. package/lib/askAgent/editorSelection.d.ts +20 -26
  9. package/lib/askAgent/editorSelection.js +11 -26
  10. package/lib/askAgent/icons.d.ts +2 -3
  11. package/lib/askAgent/icons.js +2 -3
  12. package/lib/askAgent/index.d.ts +4 -26
  13. package/lib/askAgent/index.js +54 -158
  14. package/lib/askAgent/popup.d.ts +12 -16
  15. package/lib/askAgent/popup.js +18 -37
  16. package/lib/askAgent/prompt.d.ts +3 -5
  17. package/lib/askAgent/prompt.js +6 -19
  18. package/lib/askAgent/queue.d.ts +11 -21
  19. package/lib/askAgent/queue.js +9 -19
  20. package/lib/askAgent/queuePanel.d.ts +13 -18
  21. package/lib/askAgent/queuePanel.js +15 -37
  22. package/lib/askAgent/targetPicker.d.ts +2 -9
  23. package/lib/askAgent/targetPicker.js +6 -11
  24. package/lib/askAgent/tokens.d.ts +32 -46
  25. package/lib/askAgent/tokens.js +4 -6
  26. package/lib/commandBar/index.d.ts +4 -13
  27. package/lib/commandBar/index.js +21 -39
  28. package/lib/customPanel/index.js +15 -0
  29. package/lib/editorBreadcrumbs/index.d.ts +3 -6
  30. package/lib/editorBreadcrumbs/index.js +10 -6
  31. package/lib/editorBreadcrumbs/widget.d.ts +17 -4
  32. package/lib/editorBreadcrumbs/widget.js +11 -17
  33. package/lib/editorIndent/index.d.ts +4 -12
  34. package/lib/editorIndent/index.js +8 -23
  35. package/lib/fileBrowser/commands.d.ts +29 -19
  36. package/lib/fileBrowser/commands.js +83 -97
  37. package/lib/fileBrowser/contents.d.ts +3 -0
  38. package/lib/fileBrowser/dragAndDrop.d.ts +17 -4
  39. package/lib/fileBrowser/dragAndDrop.js +2 -7
  40. package/lib/fileBrowser/fileBrowser.d.ts +19 -9
  41. package/lib/fileBrowser/fileBrowser.js +70 -222
  42. package/lib/fileBrowser/gitStatus.d.ts +16 -10
  43. package/lib/fileBrowser/gitStatus.js +31 -30
  44. package/lib/fileBrowser/gitignore.d.ts +4 -17
  45. package/lib/fileBrowser/gitignore.js +4 -25
  46. package/lib/fileBrowser/icons.d.ts +7 -17
  47. package/lib/fileBrowser/icons.js +25 -80
  48. package/lib/fileBrowser/index.js +2 -4
  49. package/lib/fileBrowser/widget.d.ts +141 -48
  50. package/lib/fileBrowser/widget.js +102 -4
  51. package/lib/fileTypeIcons/index.js +0 -10
  52. package/lib/git/api.d.ts +5 -13
  53. package/lib/git/api.js +10 -36
  54. package/lib/git/askRequest.d.ts +4 -8
  55. package/lib/git/askRequest.js +6 -20
  56. package/lib/git/commands.d.ts +45 -5
  57. package/lib/git/commands.js +20 -13
  58. package/lib/git/diffModel.js +4 -0
  59. package/lib/git/diffPreferences.d.ts +90 -0
  60. package/lib/git/diffPreferences.js +107 -0
  61. package/lib/git/diffProvider.js +10 -2
  62. package/lib/git/diffSurface.d.ts +64 -63
  63. package/lib/git/diffSurface.js +72 -213
  64. package/lib/git/diffTheme.d.ts +7 -0
  65. package/lib/git/diffTheme.js +23 -0
  66. package/lib/git/diffWidget.d.ts +70 -14
  67. package/lib/git/diffWidget.js +100 -94
  68. package/lib/git/imageDiff.d.ts +14 -6
  69. package/lib/git/imageDiff.js +19 -75
  70. package/lib/git/index.d.ts +0 -3
  71. package/lib/git/index.js +44 -10
  72. package/lib/git/notebookDiff.d.ts +110 -33
  73. package/lib/git/notebookDiff.js +55 -197
  74. package/lib/git/tokens.d.ts +73 -31
  75. package/lib/git/tokens.js +2 -3
  76. package/lib/highlight/index.d.ts +4 -10
  77. package/lib/highlight/index.js +10 -41
  78. package/lib/index.d.ts +0 -9
  79. package/lib/index.js +0 -9
  80. package/lib/launcher/agents.d.ts +55 -51
  81. package/lib/launcher/agents.js +15 -57
  82. package/lib/launcher/availability.d.ts +3 -9
  83. package/lib/launcher/availability.js +3 -9
  84. package/lib/launcher/commands.d.ts +7 -32
  85. package/lib/launcher/commands.js +14 -45
  86. package/lib/launcher/dashboard.d.ts +19 -24
  87. package/lib/launcher/dashboard.js +21 -67
  88. package/lib/launcher/editorRegistry.d.ts +10 -22
  89. package/lib/launcher/editorRegistry.js +14 -18
  90. package/lib/launcher/editors.d.ts +39 -45
  91. package/lib/launcher/editors.js +8 -34
  92. package/lib/launcher/icons.d.ts +0 -12
  93. package/lib/launcher/icons.js +5 -39
  94. package/lib/launcher/index.js +12 -55
  95. package/lib/launcher/invocation.d.ts +4 -10
  96. package/lib/launcher/invocation.js +7 -22
  97. package/lib/launcher/registry.d.ts +9 -7
  98. package/lib/launcher/registry.js +9 -7
  99. package/lib/launcher/tokens.d.ts +10 -22
  100. package/lib/launcher/tokens.js +5 -8
  101. package/lib/menuBar/index.d.ts +3 -10
  102. package/lib/menuBar/index.js +11 -28
  103. package/lib/menus/index.d.ts +3 -9
  104. package/lib/menus/index.js +14 -30
  105. package/lib/omnibox/files.d.ts +2 -4
  106. package/lib/omnibox/files.js +5 -11
  107. package/lib/omnibox/index.d.ts +4 -14
  108. package/lib/omnibox/index.js +9 -27
  109. package/lib/omnibox/model.d.ts +38 -10
  110. package/lib/omnibox/model.js +15 -33
  111. package/lib/omnibox/recents.d.ts +15 -17
  112. package/lib/omnibox/recents.js +12 -17
  113. package/lib/omnibox/tokens.d.ts +5 -10
  114. package/lib/omnibox/tokens.js +2 -6
  115. package/lib/omnibox/widget.d.ts +11 -7
  116. package/lib/omnibox/widget.js +8 -16
  117. package/lib/searchReplace/index.d.ts +4 -20
  118. package/lib/searchReplace/index.js +7 -27
  119. package/lib/showOutput/index.d.ts +3 -12
  120. package/lib/showOutput/index.js +6 -22
  121. package/lib/sidebar/index.d.ts +4 -7
  122. package/lib/sidebar/index.js +5 -12
  123. package/lib/terminalNotifications/index.d.ts +4 -10
  124. package/lib/terminalNotifications/index.js +6 -20
  125. package/lib/terminals/agentTerminals.d.ts +12 -5
  126. package/lib/terminals/agentTerminals.js +25 -44
  127. package/lib/terminals/detection.d.ts +4 -8
  128. package/lib/terminals/detection.js +4 -8
  129. package/lib/terminals/index.d.ts +0 -35
  130. package/lib/terminals/index.js +12 -101
  131. package/lib/terminals/model.d.ts +64 -139
  132. package/lib/terminals/model.js +80 -220
  133. package/lib/terminals/tokens.d.ts +11 -22
  134. package/lib/terminals/tokens.js +2 -2
  135. package/lib/terminals/widget.d.ts +43 -13
  136. package/lib/terminals/widget.js +33 -18
  137. package/lib/topBar/icons.d.ts +3 -8
  138. package/lib/topBar/icons.js +3 -8
  139. package/lib/topBar/index.d.ts +4 -11
  140. package/lib/topBar/index.js +10 -38
  141. package/lib/walkthrough/index.d.ts +4 -9
  142. package/lib/walkthrough/index.js +6 -20
  143. package/lib/walkthrough/panel.d.ts +40 -9
  144. package/lib/walkthrough/panel.js +4 -10
  145. package/package.json +5 -3
  146. package/schema/git-diff-preferences.json +42 -0
  147. package/src/about/icons.ts +0 -5
  148. package/src/about/index.tsx +6 -17
  149. package/src/agentSessions.ts +9 -29
  150. package/src/askAgent/editorSelection.ts +22 -29
  151. package/src/askAgent/icons.ts +2 -3
  152. package/src/askAgent/index.ts +54 -158
  153. package/src/askAgent/popup.tsx +23 -45
  154. package/src/askAgent/prompt.ts +6 -19
  155. package/src/askAgent/queue.ts +13 -24
  156. package/src/askAgent/queuePanel.tsx +21 -46
  157. package/src/askAgent/targetPicker.tsx +5 -15
  158. package/src/askAgent/tokens.ts +32 -46
  159. package/src/commandBar/index.ts +24 -39
  160. package/src/customPanel/index.ts +15 -0
  161. package/src/editorBreadcrumbs/index.ts +10 -6
  162. package/src/editorBreadcrumbs/widget.ts +23 -17
  163. package/src/editorIndent/index.ts +17 -23
  164. package/src/fileBrowser/commands.ts +119 -96
  165. package/src/fileBrowser/contents.ts +3 -0
  166. package/src/fileBrowser/dragAndDrop.ts +17 -7
  167. package/src/fileBrowser/fileBrowser.tsx +86 -230
  168. package/src/fileBrowser/gitStatus.ts +36 -33
  169. package/src/fileBrowser/gitignore.ts +4 -25
  170. package/src/fileBrowser/icons.ts +25 -80
  171. package/src/fileBrowser/index.ts +2 -4
  172. package/src/fileBrowser/widget.tsx +171 -50
  173. package/src/fileTypeIcons/index.ts +0 -10
  174. package/src/git/api.ts +10 -36
  175. package/src/git/askRequest.ts +6 -20
  176. package/src/git/commands.ts +83 -11
  177. package/src/git/diffModel.ts +25 -0
  178. package/src/git/diffPreferences.ts +192 -0
  179. package/src/git/diffProvider.tsx +11 -2
  180. package/src/git/diffSurface.tsx +165 -286
  181. package/src/git/diffTheme.ts +24 -0
  182. package/src/git/diffWidget.tsx +194 -134
  183. package/src/git/imageDiff.tsx +56 -84
  184. package/src/git/index.ts +54 -10
  185. package/src/git/notebookDiff.tsx +214 -240
  186. package/src/git/tokens.ts +73 -31
  187. package/src/highlight/index.ts +10 -41
  188. package/src/index.ts +0 -9
  189. package/src/launcher/agents.ts +64 -95
  190. package/src/launcher/availability.ts +3 -9
  191. package/src/launcher/commands.ts +14 -51
  192. package/src/launcher/dashboard.tsx +49 -88
  193. package/src/launcher/editorRegistry.ts +21 -29
  194. package/src/launcher/editors.ts +39 -58
  195. package/src/launcher/icons.ts +5 -39
  196. package/src/launcher/index.ts +12 -55
  197. package/src/launcher/invocation.ts +7 -22
  198. package/src/launcher/registry.ts +9 -7
  199. package/src/launcher/tokens.ts +10 -22
  200. package/src/menuBar/index.ts +11 -28
  201. package/src/menus/index.ts +14 -30
  202. package/src/omnibox/files.ts +10 -14
  203. package/src/omnibox/index.ts +9 -27
  204. package/src/omnibox/model.ts +50 -38
  205. package/src/omnibox/recents.ts +17 -22
  206. package/src/omnibox/tokens.ts +5 -10
  207. package/src/omnibox/widget.tsx +14 -19
  208. package/src/searchReplace/index.ts +7 -27
  209. package/src/showOutput/index.ts +6 -22
  210. package/src/sidebar/index.ts +20 -12
  211. package/src/terminalNotifications/index.ts +42 -25
  212. package/src/terminals/agentTerminals.ts +25 -49
  213. package/src/terminals/detection.ts +4 -8
  214. package/src/terminals/index.ts +12 -101
  215. package/src/terminals/model.ts +130 -246
  216. package/src/terminals/tokens.ts +11 -22
  217. package/src/terminals/widget.tsx +48 -31
  218. package/src/topBar/icons.ts +3 -8
  219. package/src/topBar/index.ts +21 -44
  220. package/src/walkthrough/index.ts +6 -20
  221. package/src/walkthrough/panel.ts +40 -10
  222. package/style/about.css +2 -7
  223. package/style/askAgent.css +8 -40
  224. package/style/base.css +9 -45
  225. package/style/chrome.css +108 -308
  226. package/style/commandBar.css +4 -20
  227. package/style/customPanel.css +1 -4
  228. package/style/git.css +26 -186
  229. package/style/highlight.css +1 -3
  230. package/style/launcher.css +6 -70
  231. package/style/omnibox.css +2 -14
  232. package/style/showOutput.css +0 -6
  233. package/style/sidebar.css +29 -9
  234. package/style/tabs.css +5 -13
  235. package/style/terminals.css +6 -53
  236. package/style/topBar.css +2 -22
  237. package/style/walkthrough.css +0 -8
package/src/git/tokens.ts CHANGED
@@ -1,21 +1,33 @@
1
1
  /**
2
- * Types shared by the git plugin. Mirror the response shapes returned by the
3
- * `jupyterlab_git` server extension's REST API so the frontend can stay
4
- * tightly coupled to a single version of those endpoints.
2
+ * Types shared by the git plugin, mirroring the response shapes of the
3
+ * `jupyterlab_git` server extension's REST API.
5
4
  */
6
5
 
7
6
  /**
8
- * A single entry in the `files` array returned by `POST /git/<path>/status`.
9
- * `x` and `y` are the porcelain-format index/worktree status codes (see
10
- * `git status --porcelain`); `to` is the file's current path relative to the
11
- * git repository root, `from` is the source path for renames (and equal to
12
- * `to` for any other status).
7
+ * One entry of the `files` array from `POST /git/<path>/status`. `x`/`y` are
8
+ * the porcelain index/worktree codes; `to` is the current repo-relative path
9
+ * and `from` the rename source (equal to `to` for any other status).
13
10
  */
14
11
  export interface IGitStatusFile {
12
+ /**
13
+ * The porcelain status code for the index side.
14
+ */
15
15
  x: string;
16
+ /**
17
+ * The porcelain status code for the worktree side.
18
+ */
16
19
  y: string;
20
+ /**
21
+ * The current repo-relative path of the file.
22
+ */
17
23
  to: string;
24
+ /**
25
+ * The rename source path; equal to `to` for any other status.
26
+ */
18
27
  from: string;
28
+ /**
29
+ * Whether the file content is binary; `null` when undetermined.
30
+ */
19
31
  is_binary: boolean | null;
20
32
  }
21
33
 
@@ -23,52 +35,68 @@ export interface IGitStatusFile {
23
35
  * Response shape of `POST /git/<path>/status`.
24
36
  */
25
37
  export interface IGitStatusResult {
38
+ /**
39
+ * The return code of the git command.
40
+ */
26
41
  code: number;
42
+ /**
43
+ * The current branch name; `null` when it is not available.
44
+ */
27
45
  branch: string | null;
46
+ /**
47
+ * The upstream remote branch; `null` when none is set.
48
+ */
28
49
  remote: string | null;
50
+ /**
51
+ * The number of commits ahead of the upstream branch.
52
+ */
29
53
  ahead: number;
54
+ /**
55
+ * The number of commits behind the upstream branch.
56
+ */
30
57
  behind: number;
58
+ /**
59
+ * The changed files reported by `git status`.
60
+ */
31
61
  files: IGitStatusFile[];
62
+ /**
63
+ * The in-progress repository state (merge, rebase, ...), when reported.
64
+ */
32
65
  state?: number;
66
+ /**
67
+ * An error message, present when the command fails.
68
+ */
33
69
  message?: string;
34
70
  }
35
71
 
36
72
  /**
37
- * Reference accepted by `POST /git/<path>/content` to identify which version
38
- * of a file to fetch. `WORKING` is the on-disk copy, `INDEX` is the staged
39
- * copy, and a `git` value is any commit-ish (`HEAD`, a SHA, …).
73
+ * Reference accepted by `POST /git/<path>/content`: `WORKING` is the on-disk
74
+ * copy, `INDEX` the staged copy, `git` any commit-ish.
40
75
  */
41
76
  export type GitReference =
42
77
  | { special: 'WORKING' | 'INDEX' | 'BASE' }
43
78
  | { git: string };
44
79
 
45
80
  /**
46
- * Response shape of `POST /git/<path>/content`. The server only returns text
47
- * content here; binary files are reported as binary in the status response,
48
- * and we surface them in the UI without attempting to render their diff.
81
+ * Response shape of `POST /git/<path>/content`.
49
82
  */
50
83
  export interface IGitContentResult {
84
+ /**
85
+ * The return code of the git command.
86
+ */
51
87
  code: number;
88
+ /**
89
+ * The file content at the requested reference.
90
+ */
52
91
  content: string;
92
+ /**
93
+ * An error message, present when the command fails.
94
+ */
53
95
  message?: string;
54
96
  }
55
97
 
56
- /**
57
- * Where a file's change lives relative to the index.
58
- *
59
- * - `staged` → present in the index, may differ from HEAD
60
- * - `unstaged` → present in the worktree, differs from the index
61
- *
62
- * A single file can appear in both groups when the worktree has further
63
- * changes on top of an already-staged version. The panel models that as two
64
- * separate entries, one per group.
65
- */
66
98
  type FileChangeGroup = 'staged' | 'unstaged';
67
99
 
68
- /**
69
- * The user-facing status of a file change. Drives the single-letter badge
70
- * (M/A/D/R/U/?) shown next to each entry in the panel.
71
- */
72
100
  export type FileChangeStatus =
73
101
  | 'modified'
74
102
  | 'added'
@@ -80,14 +108,28 @@ export type FileChangeStatus =
80
108
  | 'unknown';
81
109
 
82
110
  /**
83
- * One row in the changes panel. `path` is the file's path relative to the
84
- * git repository root. `from` is non-`undefined` only for renames, in which
85
- * case it carries the original path.
111
+ * One row in the changes panel. `path` is repo-relative; `from` is set only
112
+ * for renames and carries the original path.
86
113
  */
87
114
  export interface IFileChange {
115
+ /**
116
+ * The repo-relative path of the file.
117
+ */
88
118
  path: string;
119
+ /**
120
+ * The rename source path, set only for renames.
121
+ */
89
122
  from?: string;
123
+ /**
124
+ * Whether the change is staged or unstaged.
125
+ */
90
126
  group: FileChangeGroup;
127
+ /**
128
+ * The change status derived from the porcelain codes.
129
+ */
91
130
  status: FileChangeStatus;
131
+ /**
132
+ * Whether the file content is binary; `null` when undetermined.
133
+ */
92
134
  isBinary: boolean | null;
93
135
  }
@@ -13,22 +13,13 @@ import type { Widget } from '@lumino/widgets';
13
13
 
14
14
  const PLUGIN_ID = 'xtralab:highlight';
15
15
 
16
- /**
17
- * Highlight a contiguous range of lines, or clear every highlight. These are
18
- * the only two commands the plugin contributes; both are designed to be driven
19
- * by a coding agent over the MCP command bridge to walk a user through code.
20
- */
21
16
  const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
22
17
  const CLEAR_HIGHLIGHTS_COMMAND = 'xtralab:clear-highlights';
23
18
 
24
- /**
25
- * CodeMirror line-decoration class; styled in style/highlight.css.
26
- */
27
19
  const HIGHLIGHT_LINE_CLASS = 'jp-xtralab-highlightLine';
28
20
 
29
21
  /**
30
- * A CodeMirror effect carrying the 1-indexed, inclusive line range to
31
- * highlight, or `null` to clear the editor's highlights.
22
+ * Carries the 1-indexed inclusive line range to highlight, or `null` to clear.
32
23
  */
33
24
  const setHighlight = StateEffect.define<{
34
25
  start: number;
@@ -36,9 +27,8 @@ const setHighlight = StateEffect.define<{
36
27
  } | null>();
37
28
 
38
29
  /**
39
- * Holds the highlight decorations for one editor. It is injected lazily (the
40
- * first time an editor is highlighted) via `StateEffect.appendConfig`, so
41
- * editors that are never highlighted pay nothing.
30
+ * Highlight decorations for one editor; injected lazily via
31
+ * `StateEffect.appendConfig` so editors never highlighted pay nothing.
42
32
  */
43
33
  const highlightField = StateField.define<DecorationSet>({
44
34
  create: () => Decoration.none,
@@ -68,11 +58,6 @@ const highlightField = StateField.define<DecorationSet>({
68
58
  provide: field => EditorView.decorations.from(field)
69
59
  });
70
60
 
71
- /**
72
- * Return the CodeMirror view backing a widget's file editor, or `null` when
73
- * the widget is not a CodeMirror-based text editor (a notebook, a terminal,
74
- * the settings editor, …).
75
- */
76
61
  function viewForWidget(widget: Widget | null): EditorView | null {
77
62
  const content = (widget as { content?: unknown } | null)?.content;
78
63
  return content instanceof FileEditor &&
@@ -81,26 +66,16 @@ function viewForWidget(widget: Widget | null): EditorView | null {
81
66
  : null;
82
67
  }
83
68
 
84
- /**
85
- * Coerce a JSON command argument to a positive integer line number, falling
86
- * back to `fallback` when it is missing or not a finite number.
87
- */
88
69
  function toLine(value: unknown, fallback: number): number {
89
70
  const n = Math.trunc(Number(value));
90
71
  return Number.isFinite(n) && n >= 1 ? n : fallback;
91
72
  }
92
73
 
93
74
  /**
94
- * Contribute `xtralab:highlight-lines` and `xtralab:clear-highlights`.
95
- *
96
- * The built-in command surface can open a file (`docmanager:open`) and move
97
- * the cursor to a line (`fileeditor:go-to-line`), but it cannot persistently
98
- * highlight a span of lines — `documentsearch:start` only marks text matches
99
- * and hijacks the find box. This plugin fills that gap with a CodeMirror line
100
- * decoration, so an agent can point at "lines 31–43 of src/index.ts" while it
101
- * narrates a walkthrough. `xtralab:highlight-lines` opens the file when needed,
102
- * scrolls the range into view, and replaces any previous highlight in that
103
- * editor; `xtralab:clear-highlights` removes every highlight.
75
+ * Contributes `xtralab:highlight-lines` and `xtralab:clear-highlights`,
76
+ * filling the persistent span-highlight gap in the core command surface
77
+ * (`documentsearch:start` only marks text matches and hijacks the find box)
78
+ * so an agent can point at lines while narrating a walkthrough.
104
79
  */
105
80
  const plugin: JupyterFrontEndPlugin<void> = {
106
81
  id: PLUGIN_ID,
@@ -117,8 +92,6 @@ const plugin: JupyterFrontEndPlugin<void> = {
117
92
  const { commands, shell } = app;
118
93
  const trans = (translator ?? nullTranslator).load('jupyterlab');
119
94
 
120
- // Editors that currently carry a highlight, so a single clear can reach
121
- // them all. Disposed editors are dropped on the next clear.
122
95
  const highlighted = new Set<EditorView>();
123
96
 
124
97
  const ensureField = (view: EditorView): void => {
@@ -130,8 +103,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
130
103
  commands.addCommand(HIGHLIGHT_LINES_COMMAND, {
131
104
  label: trans.__('Highlight Lines'),
132
105
  caption: trans.__('Highlight a range of lines in a text editor'),
133
- // Advertise the argument shape so agents listing commands over the MCP
134
- // bridge can see how to call it, the same way core commands do.
106
+ // Advertise the argument shape to agents listing commands over the MCP bridge.
135
107
  describedBy: {
136
108
  args: {
137
109
  type: 'object',
@@ -165,9 +137,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
165
137
  typeof args['path'] === 'string' ? args['path'] : undefined;
166
138
  const reveal = args['reveal'] !== false;
167
139
 
168
- // With a path, bind strictly to that document: never fall back to the
169
- // active widget, or an explicit path could silently highlight an
170
- // unrelated editor that happens to be focused.
140
+ // With a path, bind strictly to that document — falling back to the
141
+ // active widget could silently highlight an unrelated editor.
171
142
  let widget: Widget | null;
172
143
  if (path) {
173
144
  // Fail fast on a missing path instead of opening a phantom widget
@@ -181,7 +152,6 @@ const plugin: JupyterFrontEndPlugin<void> = {
181
152
  if (!opened) {
182
153
  throw new Error(`xtralab: could not open "${path}"`);
183
154
  }
184
- // Wait for the model and the editor view to be ready.
185
155
  await opened.context.ready;
186
156
  await opened.revealed;
187
157
  widget = opened;
@@ -213,7 +183,6 @@ const plugin: JupyterFrontEndPlugin<void> = {
213
183
  effects.push(EditorView.scrollIntoView(anchor, { y: 'center' }));
214
184
  }
215
185
  view.dispatch({ effects });
216
- // Forget editors that have since been closed before tracking this one.
217
186
  for (const tracked of highlighted) {
218
187
  if (!tracked.dom.isConnected) {
219
188
  highlighted.delete(tracked);
package/src/index.ts CHANGED
@@ -23,15 +23,6 @@ import terminalsPlugin from './terminals';
23
23
  import topBarPlugin from './topBar';
24
24
  import walkthroughPlugin from './walkthrough';
25
25
 
26
- /**
27
- * Every plugin contributed by `xtralab`. The entry point of the
28
- * labextension is an array because the package bundles several independent
29
- * enhancements (file browser, git diff providers, …) — JupyterLab activates
30
- * each plugin individually and only the ones whose required tokens are
31
- * available end up running. `gitPlugins` and `launcherPlugins` are themselves
32
- * arrays (the git diff providers; the launcher plus its editor registry), so
33
- * they are spread in.
34
- */
35
26
  const plugins: JupyterFrontEndPlugin<unknown>[] = [
36
27
  aboutPlugin,
37
28
  agentSessionsPlugin,
@@ -3,92 +3,98 @@ import { LabIcon, terminalIcon } from '@jupyterlab/ui-components';
3
3
  import { BUILTIN_AGENT_ICONS } from './icons';
4
4
 
5
5
  /**
6
- * The shape of an agent card on the launcher. The `command` is the literal
7
- * text typed into the new terminal session — interactive shells expand
8
- * aliases, so users can point at `claude`, `cl`, or whatever runs their
9
- * preferred wrapper.
6
+ * An agent card on the launcher. `command` is the literal text typed into the
7
+ * new terminal, so interactive-shell aliases resolve.
10
8
  */
11
9
  export interface IAgent {
10
+ /**
11
+ * Stable id; keys the settings merge and the per-agent launch command.
12
+ */
12
13
  id: string;
14
+ /**
15
+ * Card label, e.g. "Claude".
16
+ */
13
17
  label: string;
18
+ /**
19
+ * Card tooltip; also the palette command caption.
20
+ */
14
21
  caption: string;
22
+ /**
23
+ * The literal command typed into the new terminal.
24
+ */
15
25
  command: string;
26
+ /**
27
+ * Brand icon for the card.
28
+ */
16
29
  icon: LabIcon;
30
+ /**
31
+ * Sort position among the launcher's agent cards.
32
+ */
17
33
  rank: number;
18
34
  /**
19
- * When false, the launcher skips the `which`-based availability check for
20
- * this entry — useful for aliases or shell functions that aren't on PATH
21
- * but still resolve when typed in a real terminal. Defaults to true.
35
+ * When false, skip the `which`-based availability check — for aliases or
36
+ * shell functions not on PATH. Defaults to true.
22
37
  */
23
38
  requireAvailable: boolean;
24
39
  /**
25
- * Argv tokens spliced between `command` and a shell-quoted prompt when
26
- * the user types one into the launcher's prompt box. The semantics:
27
- *
28
- * - `[]` → prompt is appended as a positional argument:
29
- * `<command> 'PROMPT'`. (Used by claude, codex, vibe, pi.)
30
- * - `['-i']` / `['--prompt']` → the prompt is preceded by a flag:
31
- * `<command> -i 'PROMPT'`. (Used by copilot, opencode.)
32
- * - `undefined` → the agent does not accept an initial prompt. The
33
- * launcher dims the agent's button while the prompt textarea is
34
- * non-empty, so the user gets a clear signal rather than a silently
35
- * dropped prompt.
36
- *
37
- * The prompt itself is always single-quoted with embedded single quotes
38
- * escaped, so multi-line prompts and shell metacharacters are safe.
40
+ * Argv tokens spliced between `command` and the shell-quoted prompt:
41
+ * `[]` appends the prompt positionally, `['-i']` prefixes a flag, and
42
+ * `undefined` means no prompt support (the launcher dims the button).
39
43
  */
40
44
  promptArgs?: string[];
41
45
  }
42
46
 
43
47
  /**
44
- * The settings-side shape: every field except `id` is optional, so a user
45
- * can override a single field on a default agent (e.g. swap the command for
46
- * an alias) without restating the whole entry. New ids define brand-new
47
- * agent cards.
48
+ * The settings-side shape: every field except `id` is optional so a user can
49
+ * override a single field on a default agent; new ids define new agent cards.
48
50
  */
49
51
  export interface IAgentSettings {
52
+ /**
53
+ * Id of the agent to override; a new id defines a new card.
54
+ */
50
55
  id: string;
56
+ /**
57
+ * See `IAgent.label`.
58
+ */
51
59
  label?: string;
60
+ /**
61
+ * See `IAgent.caption`.
62
+ */
52
63
  caption?: string;
64
+ /**
65
+ * See `IAgent.command`.
66
+ */
53
67
  command?: string;
54
68
  /**
55
- * Inline SVG for a custom agent's icon. Required for new ids whose icon
56
- * isn't shipped with xtralab; ignored for default ids unless explicitly
57
- * set (in which case it overrides the built-in).
69
+ * Inline SVG icon. Required for new ids; overrides the built-in when set on
70
+ * a default id.
58
71
  */
59
72
  iconSvg?: string;
73
+ /**
74
+ * See `IAgent.rank`.
75
+ */
60
76
  rank?: number;
61
77
  /**
62
- * When false, the agent is hidden from the launcher and the command
63
- * palette. Defaults to true.
78
+ * When false, hides the agent from the launcher and the command palette.
64
79
  */
65
80
  enabled?: boolean;
66
81
  /**
67
- * See `IAgent.requireAvailable`. Defaults to true for a built-in agent that
68
- * still uses its shipped command, and to false once you override the
69
- * `command` (a user-chosen command — often a shell alias — is trusted and
70
- * always shown). Set it explicitly to force the check on or off.
82
+ * See `IAgent.requireAvailable`. Defaults to true, but flips to false once
83
+ * `command` is overridden (a user-chosen alias is trusted).
71
84
  */
72
85
  requireAvailable?: boolean;
73
86
  /**
74
- * See `IAgent.promptArgs`. Pass an empty array to mark the agent as
75
- * accepting a positional prompt; pass an array like `["-p"]` to use a
76
- * flag. Pass `null` to explicitly turn off prompt support for an agent
77
- * that has it on by default.
87
+ * See `IAgent.promptArgs`. `null` explicitly turns off an agent's default
88
+ * prompt support.
78
89
  */
79
90
  promptArgs?: string[] | null;
80
91
  }
81
92
 
82
93
  /**
83
- * Default agent cards shipped with xtralab. Seven of these (Claude, Codex,
84
- * Copilot, Goose, OpenCode, Kiro, Mistral Vibe) mirror first-class personas
85
- * in `jupyter-ai-contrib/jupyter-ai-acp-client` (which is also where their
86
- * icons come from), but use the bare CLI names a user would type in a
87
- * terminal rather than the ACP wrapper binaries that project spawns.
88
- * Antigravity (Google's agent-first IDE/CLI, `agy`) is the Google agent.
89
- * Pi (https://pi.dev, `earendil-works/pi`) is a minimal extensible coding
90
- * agent; its mark comes straight from its site. Anything not on the user's
91
- * `$PATH` is filtered out at activation time, so the wider list is harmless.
94
+ * Default agent cards. Most mirror `jupyter-ai-contrib/jupyter-ai-acp-client`
95
+ * personas but use the bare CLI names a user would type, not the ACP wrappers.
96
+ * Anything not on `$PATH` is filtered out at activation, so the wide list is
97
+ * harmless.
92
98
  */
93
99
  const DEFAULTS: IAgent[] = [
94
100
  {
@@ -119,13 +125,8 @@ const DEFAULTS: IAgent[] = [
119
125
  icon: BUILTIN_AGENT_ICONS.antigravity,
120
126
  rank: 2,
121
127
  requireAvailable: true
122
- // `agy` is the Antigravity launcher binary — a VS Code-derived editor
123
- // CLI that opens the Agent Manager / workspace, not a terminal REPL.
124
- // Its positional args are file/folder paths, so there is no inline
125
- // natural-language prompt form; like goose/kiro we omit promptArgs so a
126
- // typed prompt dims the button instead of being mangled into a path.
127
- // The command stays the bare binary (no `agy .`) so the server-side
128
- // `shutil.which` availability check still resolves it.
128
+ // `agy` opens the Antigravity editor; its positional args are paths, not
129
+ // prompts, and the bare binary keeps the server-side `which` check working.
129
130
  },
130
131
  {
131
132
  id: 'copilot',
@@ -135,8 +136,7 @@ const DEFAULTS: IAgent[] = [
135
136
  icon: BUILTIN_AGENT_ICONS.copilot,
136
137
  rank: 3,
137
138
  requireAvailable: true,
138
- // `-i "PROMPT"` is the documented way to start interactive mode and
139
- // auto-execute the prompt; `-p` exits after responding (non-interactive).
139
+ // `-i` starts interactive mode with the prompt; `-p` exits after responding.
140
140
  promptArgs: ['-i']
141
141
  },
142
142
  {
@@ -147,8 +147,7 @@ const DEFAULTS: IAgent[] = [
147
147
  icon: BUILTIN_AGENT_ICONS.goose,
148
148
  rank: 4,
149
149
  requireAvailable: true
150
- // `goose` does not accept an inline interactive prompt; users would
151
- // need to type the prompt after `goose session` starts.
150
+ // `goose` takes no inline prompt; it must be typed after `goose session`.
152
151
  },
153
152
  {
154
153
  id: 'opencode',
@@ -168,8 +167,7 @@ const DEFAULTS: IAgent[] = [
168
167
  icon: BUILTIN_AGENT_ICONS.kiro,
169
168
  rank: 6,
170
169
  requireAvailable: true
171
- // The Kiro chat session only takes initial prompts via the in-session
172
- // `/chat new <prompt>` slash command, not a CLI argument.
170
+ // Kiro only takes initial prompts via the in-session `/chat new` command.
173
171
  },
174
172
  {
175
173
  id: 'mistral-vibe',
@@ -189,17 +187,13 @@ const DEFAULTS: IAgent[] = [
189
187
  icon: BUILTIN_AGENT_ICONS.pi,
190
188
  rank: 8,
191
189
  requireAvailable: true,
192
- // `pi [messages...]` starts the interactive TUI and sends the messages
193
- // as the first user prompt, so the positional form applies.
194
190
  promptArgs: []
195
191
  }
196
192
  ];
197
193
 
198
194
  /**
199
- * The built-in agents projected into the JSON settings shape
200
- * ({@link IAgentSettings}), dropping the runtime-only {@link LabIcon}.
201
- * {@link registerLauncherSchemaDefaults} injects this as the `agents` setting's
202
- * schema default so the Settings Editor shows the shipped list.
195
+ * The built-in agents projected into the settings shape (no runtime LabIcon),
196
+ * injected as the `agents` schema default so the Settings Editor shows them.
203
197
  */
204
198
  export function defaultAgentSettings(): IAgentSettings[] {
205
199
  return DEFAULTS.map(agent => {
@@ -211,8 +205,6 @@ export function defaultAgentSettings(): IAgentSettings[] {
211
205
  rank: agent.rank,
212
206
  requireAvailable: agent.requireAvailable
213
207
  };
214
- // Omit `promptArgs` for agents that don't take an inline prompt, so the
215
- // default doesn't show a prompt form they lack.
216
208
  if (agent.promptArgs !== undefined) {
217
209
  entry.promptArgs = agent.promptArgs;
218
210
  }
@@ -228,15 +220,10 @@ function resolveIcon(id: string, iconSvg: string | undefined): LabIcon {
228
220
  }
229
221
 
230
222
  /**
231
- * Merge xtralab's defaults with the user's settings. Default entries keep
232
- * their built-in fields unless explicitly overridden; user-only entries are
233
- * appended. `enabled: false` filters an entry out of the result entirely
234
- * (so callers don't need to check the flag again).
235
- *
236
- * Overriding a built-in agent's `command` also turns its `requireAvailable`
237
- * off, so the card survives the launcher's `which`-based availability filter
238
- * even when the new command is a shell alias the server can't resolve. See the
239
- * built-in branch below for the rationale.
223
+ * Merge xtralab's defaults with the user's settings: defaults keep their
224
+ * fields unless overridden, user-only ids are appended, and `enabled: false`
225
+ * removes an entry entirely. Overriding a built-in's `command` also turns
226
+ * `requireAvailable` off so an aliased command survives the `which` filter.
240
227
  */
241
228
  export function mergeAgents(overrides: IAgentSettings[]): IAgent[] {
242
229
  const overrideById = new Map(overrides.map(entry => [entry.id, entry]));
@@ -262,24 +249,10 @@ export function mergeAgents(overrides: IAgentSettings[]): IAgent[] {
262
249
  ? resolveIcon(base.id, override.iconSvg)
263
250
  : base.icon,
264
251
  rank: override.rank ?? base.rank,
265
- // The availability filter exists to prune xtralab's *built-in* command
266
- // from the launcher when it isn't installed. Once the user points the
267
- // agent at their own command — e.g. the `ccm` alias wrapping `claude
268
- // --effort=max …` — the server's `shutil.which` probe can't see it
269
- // (aliases and shell functions only exist inside an interactive shell),
270
- // so keeping the check on would wrongly hide the card. We therefore only
271
- // require availability while the command is still xtralab's default; a
272
- // user-chosen command is trusted and always shown. An explicit
273
- // `requireAvailable` still applies to the unchanged default command, so a
274
- // user can alias `claude` itself and set `requireAvailable: false` (or
275
- // force the check back on).
276
252
  requireAvailable:
277
253
  command === base.command
278
254
  ? (override.requireAvailable ?? base.requireAvailable)
279
255
  : false,
280
- // `null` is the explicit way to opt out of an agent's default prompt
281
- // support; an absent key keeps the default. `undefined` from `??` is
282
- // pruned below.
283
256
  promptArgs:
284
257
  override.promptArgs === null
285
258
  ? undefined
@@ -287,10 +260,6 @@ export function mergeAgents(overrides: IAgentSettings[]): IAgent[] {
287
260
  });
288
261
  }
289
262
 
290
- // What remains in `overrideById` are entries with ids that don't match a
291
- // default — treat them as new agents the user is adding. Skip silently
292
- // when a required field is missing rather than throwing; the settings
293
- // schema validates the shape, so this is just a defensive fallback.
294
263
  let nextRank =
295
264
  merged.reduce((max, agent) => Math.max(max, agent.rank), -1) + 1;
296
265
  for (const entry of overrideById.values()) {
@@ -2,15 +2,9 @@ import { URLExt } from '@jupyterlab/coreutils';
2
2
  import { ServerConnection } from '@jupyterlab/services';
3
3
 
4
4
  /**
5
- * Hit the xtralab server extension's `which`-proxy endpoint and turn the
6
- * response into a Set of commands that resolved to a real binary on the
7
- * server's `$PATH`. The frontend uses this set to filter the launcher's
8
- * agent cards.
9
- *
10
- * On any failure (server extension not loaded, network error, malformed
11
- * response) the returned Set is `null` — callers should treat that as
12
- * "availability unknown" and either fall back to showing all agents or
13
- * surface the error, depending on context.
5
+ * Ask the server extension's `which`-proxy endpoint which commands resolve on
6
+ * its `$PATH`. Returns `null` on any failure — callers should treat that as
7
+ * "availability unknown", not as "nothing available".
14
8
  */
15
9
  export async function fetchAvailableCommands(
16
10
  commands: string[]