@hyperspaceng/neural-coding-agent 0.61.6 → 0.63.0

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 (162) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +2 -2
  3. package/dist/cli/file-processor.d.ts.map +1 -1
  4. package/dist/cli/file-processor.js +4 -0
  5. package/dist/cli/file-processor.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +10 -3
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +60 -46
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/export-html/index.d.ts +2 -2
  11. package/dist/core/export-html/index.d.ts.map +1 -1
  12. package/dist/core/export-html/index.js +2 -2
  13. package/dist/core/export-html/index.js.map +1 -1
  14. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  15. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  16. package/dist/core/export-html/tool-renderer.js +41 -16
  17. package/dist/core/export-html/tool-renderer.js.map +1 -1
  18. package/dist/core/extensions/index.d.ts +3 -2
  19. package/dist/core/extensions/index.d.ts.map +1 -1
  20. package/dist/core/extensions/index.js.map +1 -1
  21. package/dist/core/extensions/loader.d.ts.map +1 -1
  22. package/dist/core/extensions/loader.js +12 -2
  23. package/dist/core/extensions/loader.js.map +1 -1
  24. package/dist/core/extensions/runner.d.ts +4 -7
  25. package/dist/core/extensions/runner.d.ts.map +1 -1
  26. package/dist/core/extensions/runner.js +27 -38
  27. package/dist/core/extensions/runner.js.map +1 -1
  28. package/dist/core/extensions/types.d.ts +44 -9
  29. package/dist/core/extensions/types.d.ts.map +1 -1
  30. package/dist/core/extensions/types.js.map +1 -1
  31. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  32. package/dist/core/extensions/wrapper.js +2 -8
  33. package/dist/core/extensions/wrapper.js.map +1 -1
  34. package/dist/core/index.d.ts +1 -0
  35. package/dist/core/index.d.ts.map +1 -1
  36. package/dist/core/index.js +1 -0
  37. package/dist/core/index.js.map +1 -1
  38. package/dist/core/output-guard.d.ts +6 -0
  39. package/dist/core/output-guard.d.ts.map +1 -0
  40. package/dist/core/output-guard.js +59 -0
  41. package/dist/core/output-guard.js.map +1 -0
  42. package/dist/core/package-manager.d.ts +1 -0
  43. package/dist/core/package-manager.d.ts.map +1 -1
  44. package/dist/core/package-manager.js +27 -8
  45. package/dist/core/package-manager.js.map +1 -1
  46. package/dist/core/prompt-templates.d.ts +2 -1
  47. package/dist/core/prompt-templates.d.ts.map +1 -1
  48. package/dist/core/prompt-templates.js +30 -32
  49. package/dist/core/prompt-templates.js.map +1 -1
  50. package/dist/core/resource-loader.d.ts +6 -5
  51. package/dist/core/resource-loader.d.ts.map +1 -1
  52. package/dist/core/resource-loader.js +136 -108
  53. package/dist/core/resource-loader.js.map +1 -1
  54. package/dist/core/sdk.d.ts +1 -1
  55. package/dist/core/sdk.d.ts.map +1 -1
  56. package/dist/core/sdk.js.map +1 -1
  57. package/dist/core/skills.d.ts +2 -1
  58. package/dist/core/skills.d.ts.map +1 -1
  59. package/dist/core/skills.js +25 -1
  60. package/dist/core/skills.js.map +1 -1
  61. package/dist/core/slash-commands.d.ts +2 -3
  62. package/dist/core/slash-commands.d.ts.map +1 -1
  63. package/dist/core/slash-commands.js.map +1 -1
  64. package/dist/core/source-info.d.ts +18 -0
  65. package/dist/core/source-info.d.ts.map +1 -0
  66. package/dist/core/source-info.js +19 -0
  67. package/dist/core/source-info.js.map +1 -0
  68. package/dist/core/system-prompt.d.ts.map +1 -1
  69. package/dist/core/system-prompt.js +3 -38
  70. package/dist/core/system-prompt.js.map +1 -1
  71. package/dist/core/tools/bash.d.ts +19 -9
  72. package/dist/core/tools/bash.d.ts.map +1 -1
  73. package/dist/core/tools/bash.js +151 -59
  74. package/dist/core/tools/bash.js.map +1 -1
  75. package/dist/core/tools/edit.d.ts +14 -2
  76. package/dist/core/tools/edit.d.ts.map +1 -1
  77. package/dist/core/tools/edit.js +92 -21
  78. package/dist/core/tools/edit.js.map +1 -1
  79. package/dist/core/tools/find.d.ts +11 -4
  80. package/dist/core/tools/find.d.ts.map +1 -1
  81. package/dist/core/tools/find.js +76 -27
  82. package/dist/core/tools/find.js.map +1 -1
  83. package/dist/core/tools/grep.d.ts +15 -4
  84. package/dist/core/tools/grep.d.ts.map +1 -1
  85. package/dist/core/tools/grep.js +83 -29
  86. package/dist/core/tools/grep.js.map +1 -1
  87. package/dist/core/tools/index.d.ts +57 -19
  88. package/dist/core/tools/index.d.ts.map +1 -1
  89. package/dist/core/tools/index.js +50 -26
  90. package/dist/core/tools/index.js.map +1 -1
  91. package/dist/core/tools/ls.d.ts +9 -3
  92. package/dist/core/tools/ls.d.ts.map +1 -1
  93. package/dist/core/tools/ls.js +67 -13
  94. package/dist/core/tools/ls.js.map +1 -1
  95. package/dist/core/tools/read.d.ts +10 -3
  96. package/dist/core/tools/read.d.ts.map +1 -1
  97. package/dist/core/tools/read.js +110 -51
  98. package/dist/core/tools/read.js.map +1 -1
  99. package/dist/core/tools/render-utils.d.ts +21 -0
  100. package/dist/core/tools/render-utils.d.ts.map +1 -0
  101. package/dist/core/tools/render-utils.js +49 -0
  102. package/dist/core/tools/render-utils.js.map +1 -0
  103. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  104. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  105. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  106. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  107. package/dist/core/tools/write.d.ts +9 -3
  108. package/dist/core/tools/write.d.ts.map +1 -1
  109. package/dist/core/tools/write.js +162 -27
  110. package/dist/core/tools/write.js.map +1 -1
  111. package/dist/index.d.ts +3 -2
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +2 -1
  114. package/dist/index.js.map +1 -1
  115. package/dist/main.d.ts.map +1 -1
  116. package/dist/main.js +29 -9
  117. package/dist/main.js.map +1 -1
  118. package/dist/modes/interactive/components/tool-execution.d.ts +15 -40
  119. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  120. package/dist/modes/interactive/components/tool-execution.js +126 -679
  121. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  122. package/dist/modes/interactive/interactive-mode.d.ts +4 -11
  123. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  124. package/dist/modes/interactive/interactive-mode.js +144 -92
  125. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  126. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  127. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  128. package/dist/modes/interactive/theme/theme.js +14 -0
  129. package/dist/modes/interactive/theme/theme.js.map +1 -1
  130. package/dist/modes/print-mode.d.ts.map +1 -1
  131. package/dist/modes/print-mode.js +5 -11
  132. package/dist/modes/print-mode.js.map +1 -1
  133. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  134. package/dist/modes/rpc/rpc-mode.js +27 -20
  135. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  136. package/dist/modes/rpc/rpc-types.d.ts +3 -4
  137. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  138. package/dist/modes/rpc/rpc-types.js.map +1 -1
  139. package/dist/utils/image-resize.d.ts +5 -5
  140. package/dist/utils/image-resize.d.ts.map +1 -1
  141. package/dist/utils/image-resize.js +45 -94
  142. package/dist/utils/image-resize.js.map +1 -1
  143. package/docs/extensions.md +72 -32
  144. package/docs/tui.md +2 -2
  145. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  146. package/examples/extensions/commands.ts +3 -3
  147. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  148. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  149. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  150. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  151. package/examples/extensions/minimal-mode.ts +14 -14
  152. package/examples/extensions/question.ts +2 -2
  153. package/examples/extensions/questionnaire.ts +2 -2
  154. package/examples/extensions/subagent/index.ts +2 -2
  155. package/examples/extensions/todo.ts +2 -2
  156. package/examples/extensions/truncated-tool.ts +2 -2
  157. package/examples/extensions/with-deps/package-lock.json +2 -2
  158. package/examples/extensions/with-deps/package.json +1 -1
  159. package/examples/sdk/04-skills.ts +8 -2
  160. package/examples/sdk/08-prompt-templates.ts +2 -1
  161. package/examples/sdk/12-full-control.ts +0 -1
  162. package/package.json +4 -4
@@ -20,6 +20,7 @@ import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
20
20
  import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
21
21
  import { copyToClipboard } from "../../utils/clipboard.js";
22
22
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
23
+ import { parseGitUrl } from "../../utils/git.js";
23
24
  import { ensureTool } from "../../utils/tools-manager.js";
24
25
  import { ArminComponent } from "./components/armin.js";
25
26
  import { AssistantMessageComponent } from "./components/assistant-message.js";
@@ -168,6 +169,48 @@ export class InteractiveMode {
168
169
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
169
170
  initTheme(this.settingsManager.getTheme(), true);
170
171
  }
172
+ getAutocompleteSourceTag(sourceInfo) {
173
+ if (!sourceInfo) {
174
+ return undefined;
175
+ }
176
+ const scopePrefix = sourceInfo.scope === "user" ? "u" : sourceInfo.scope === "project" ? "p" : "t";
177
+ const source = sourceInfo.source.trim();
178
+ if (source === "auto" || source === "local" || source === "cli") {
179
+ return scopePrefix;
180
+ }
181
+ if (source.startsWith("npm:")) {
182
+ return `${scopePrefix}:${source}`;
183
+ }
184
+ const gitSource = parseGitUrl(source);
185
+ if (gitSource) {
186
+ const ref = gitSource.ref ? `@${gitSource.ref}` : "";
187
+ return `${scopePrefix}:git:${gitSource.host}/${gitSource.path}${ref}`;
188
+ }
189
+ return scopePrefix;
190
+ }
191
+ prefixAutocompleteDescription(description, sourceInfo) {
192
+ const sourceTag = this.getAutocompleteSourceTag(sourceInfo);
193
+ if (!sourceTag) {
194
+ return description;
195
+ }
196
+ return description ? `[${sourceTag}] ${description}` : `[${sourceTag}]`;
197
+ }
198
+ getBuiltInCommandConflictDiagnostics(extensionRunner) {
199
+ if (!extensionRunner) {
200
+ return [];
201
+ }
202
+ const builtinNames = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
203
+ return extensionRunner
204
+ .getRegisteredCommands()
205
+ .filter((command) => builtinNames.has(command.name))
206
+ .map((command) => ({
207
+ type: "warning",
208
+ message: command.invocationName === command.name
209
+ ? `Extension command '/${command.name}' conflicts with built-in interactive command. Skipping in autocomplete.`
210
+ : `Extension command '/${command.name}' conflicts with built-in interactive command. Available as '/${command.invocationName}'.`,
211
+ path: command.sourceInfo.path,
212
+ }));
213
+ }
171
214
  setupAutocomplete(fdPath) {
172
215
  // Define commands for autocomplete
173
216
  const slashCommands = BUILTIN_SLASH_COMMANDS.map((command) => ({
@@ -203,13 +246,13 @@ export class InteractiveMode {
203
246
  // Convert prompt templates to SlashCommand format for autocomplete
204
247
  const templateCommands = this.session.promptTemplates.map((cmd) => ({
205
248
  name: cmd.name,
206
- description: cmd.description,
249
+ description: this.prefixAutocompleteDescription(cmd.description, cmd.sourceInfo),
207
250
  }));
208
251
  // Convert extension commands to SlashCommand format
209
252
  const builtinCommandNames = new Set(slashCommands.map((c) => c.name));
210
- const extensionCommands = (this.session.extensionRunner?.getRegisteredCommands(builtinCommandNames) ?? []).map((cmd) => ({
211
- name: cmd.name,
212
- description: cmd.description ?? "(extension command)",
253
+ const extensionCommands = (this.session.extensionRunner?.getRegisteredCommands().filter((cmd) => !builtinCommandNames.has(cmd.name)) ?? []).map((cmd) => ({
254
+ name: cmd.invocationName,
255
+ description: this.prefixAutocompleteDescription(cmd.description, cmd.sourceInfo),
213
256
  getArgumentCompletions: cmd.getArgumentCompletions,
214
257
  }));
215
258
  // Build skill commands from session.skills (if enabled)
@@ -219,7 +262,10 @@ export class InteractiveMode {
219
262
  for (const skill of this.session.resourceLoader.getSkills().skills) {
220
263
  const commandName = `skill:${skill.name}`;
221
264
  this.skillCommands.set(commandName, skill.filePath);
222
- skillCommandList.push({ name: commandName, description: skill.description });
265
+ skillCommandList.push({
266
+ name: commandName,
267
+ description: this.prefixAutocompleteDescription(skill.description, skill.sourceInfo),
268
+ });
223
269
  }
224
270
  }
225
271
  // Setup autocomplete
@@ -547,21 +593,21 @@ export class InteractiveMode {
547
593
  /**
548
594
  * Get a short path relative to the package root for display.
549
595
  */
550
- getShortPath(fullPath, source) {
551
- // For npm packages, show path relative to node_modules/pkg/
596
+ getShortPath(fullPath, sourceInfo) {
597
+ const source = sourceInfo?.source ?? "";
552
598
  const npmMatch = fullPath.match(/node_modules\/(@?[^/]+(?:\/[^/]+)?)\/(.*)/);
553
599
  if (npmMatch && source.startsWith("npm:")) {
554
600
  return npmMatch[2];
555
601
  }
556
- // For git packages, show path relative to repo root
557
602
  const gitMatch = fullPath.match(/git\/[^/]+\/[^/]+\/(.*)/);
558
603
  if (gitMatch && source.startsWith("git:")) {
559
604
  return gitMatch[1];
560
605
  }
561
- // For local/auto, just use formatDisplayPath
562
606
  return this.formatDisplayPath(fullPath);
563
607
  }
564
- getDisplaySourceInfo(source, scope) {
608
+ getDisplaySourceInfo(sourceInfo) {
609
+ const source = sourceInfo?.source ?? "local";
610
+ const scope = sourceInfo?.scope ?? "project";
565
611
  if (source === "local") {
566
612
  if (scope === "user") {
567
613
  return { label: "user", color: "muted" };
@@ -580,7 +626,9 @@ export class InteractiveMode {
580
626
  const scopeLabel = scope === "user" ? "user" : scope === "project" ? "project" : scope === "temporary" ? "temp" : undefined;
581
627
  return { label: source, scopeLabel, color: "accent" };
582
628
  }
583
- getScopeGroup(source, scope) {
629
+ getScopeGroup(sourceInfo) {
630
+ const source = sourceInfo?.source ?? "local";
631
+ const scope = sourceInfo?.scope ?? "project";
584
632
  if (source === "cli" || scope === "temporary")
585
633
  return "path";
586
634
  if (scope === "user")
@@ -589,28 +637,27 @@ export class InteractiveMode {
589
637
  return "project";
590
638
  return "path";
591
639
  }
592
- isPackageSource(source) {
640
+ isPackageSource(sourceInfo) {
641
+ const source = sourceInfo?.source ?? "";
593
642
  return source.startsWith("npm:") || source.startsWith("git:");
594
643
  }
595
- buildScopeGroups(paths, metadata) {
644
+ buildScopeGroups(items) {
596
645
  const groups = {
597
646
  user: { scope: "user", paths: [], packages: new Map() },
598
647
  project: { scope: "project", paths: [], packages: new Map() },
599
648
  path: { scope: "path", paths: [], packages: new Map() },
600
649
  };
601
- for (const p of paths) {
602
- const meta = this.findMetadata(p, metadata);
603
- const source = meta?.source ?? "local";
604
- const scope = meta?.scope ?? "project";
605
- const groupKey = this.getScopeGroup(source, scope);
650
+ for (const item of items) {
651
+ const groupKey = this.getScopeGroup(item.sourceInfo);
606
652
  const group = groups[groupKey];
607
- if (this.isPackageSource(source)) {
653
+ const source = item.sourceInfo?.source ?? "local";
654
+ if (this.isPackageSource(item.sourceInfo)) {
608
655
  const list = group.packages.get(source) ?? [];
609
- list.push(p);
656
+ list.push(item);
610
657
  group.packages.set(source, list);
611
658
  }
612
659
  else {
613
- group.paths.push(p);
660
+ group.paths.push(item);
614
661
  }
615
662
  }
616
663
  return [groups.project, groups.user, groups.path].filter((group) => group.paths.length > 0 || group.packages.size > 0);
@@ -619,57 +666,44 @@ export class InteractiveMode {
619
666
  const lines = [];
620
667
  for (const group of groups) {
621
668
  lines.push(` ${theme.fg("accent", group.scope)}`);
622
- const sortedPaths = [...group.paths].sort((a, b) => a.localeCompare(b));
623
- for (const p of sortedPaths) {
624
- lines.push(theme.fg("dim", ` ${options.formatPath(p)}`));
669
+ const sortedPaths = [...group.paths].sort((a, b) => a.path.localeCompare(b.path));
670
+ for (const item of sortedPaths) {
671
+ lines.push(theme.fg("dim", ` ${options.formatPath(item)}`));
625
672
  }
626
673
  const sortedPackages = Array.from(group.packages.entries()).sort(([a], [b]) => a.localeCompare(b));
627
- for (const [source, paths] of sortedPackages) {
674
+ for (const [source, items] of sortedPackages) {
628
675
  lines.push(` ${theme.fg("mdLink", source)}`);
629
- const sortedPackagePaths = [...paths].sort((a, b) => a.localeCompare(b));
630
- for (const p of sortedPackagePaths) {
631
- lines.push(theme.fg("dim", ` ${options.formatPackagePath(p, source)}`));
676
+ const sortedPackagePaths = [...items].sort((a, b) => a.path.localeCompare(b.path));
677
+ for (const item of sortedPackagePaths) {
678
+ lines.push(theme.fg("dim", ` ${options.formatPackagePath(item, source)}`));
632
679
  }
633
680
  }
634
681
  }
635
682
  return lines.join("\n");
636
683
  }
637
- /**
638
- * Find metadata for a path, checking parent directories if exact match fails.
639
- * Package manager stores metadata for directories, but we display file paths.
640
- */
641
- findMetadata(p, metadata) {
642
- // Try exact match first
643
- const exact = metadata.get(p);
684
+ findSourceInfoForPath(p, sourceInfos) {
685
+ const exact = sourceInfos.get(p);
644
686
  if (exact)
645
687
  return exact;
646
- // Try parent directories (package manager stores directory paths)
647
688
  let current = p;
648
689
  while (current.includes("/")) {
649
690
  current = current.substring(0, current.lastIndexOf("/"));
650
- const parent = metadata.get(current);
691
+ const parent = sourceInfos.get(current);
651
692
  if (parent)
652
693
  return parent;
653
694
  }
654
695
  return undefined;
655
696
  }
656
- /**
657
- * Format a path with its source/scope info from metadata.
658
- */
659
- formatPathWithSource(p, metadata) {
660
- const meta = this.findMetadata(p, metadata);
661
- if (meta) {
662
- const shortPath = this.getShortPath(p, meta.source);
663
- const { label, scopeLabel } = this.getDisplaySourceInfo(meta.source, meta.scope);
697
+ formatPathWithSource(p, sourceInfo) {
698
+ if (sourceInfo) {
699
+ const shortPath = this.getShortPath(p, sourceInfo);
700
+ const { label, scopeLabel } = this.getDisplaySourceInfo(sourceInfo);
664
701
  const labelText = scopeLabel ? `${label} (${scopeLabel})` : label;
665
702
  return `${labelText} ${shortPath}`;
666
703
  }
667
704
  return this.formatDisplayPath(p);
668
705
  }
669
- /**
670
- * Format resource diagnostics with nice collision display using metadata.
671
- */
672
- formatDiagnostics(diagnostics, metadata) {
706
+ formatDiagnostics(diagnostics, sourceInfos) {
673
707
  const lines = [];
674
708
  // Group collision diagnostics by name
675
709
  const collisions = new Map();
@@ -690,21 +724,17 @@ export class InteractiveMode {
690
724
  if (!first)
691
725
  continue;
692
726
  lines.push(theme.fg("warning", ` "${name}" collision:`));
693
- // Show winner
694
- lines.push(theme.fg("dim", ` ${theme.fg("success", "✓")} ${this.formatPathWithSource(first.winnerPath, metadata)}`));
695
- // Show all losers
727
+ lines.push(theme.fg("dim", ` ${theme.fg("success", "✓")} ${this.formatPathWithSource(first.winnerPath, this.findSourceInfoForPath(first.winnerPath, sourceInfos))}`));
696
728
  for (const d of collisionList) {
697
729
  if (d.collision) {
698
- lines.push(theme.fg("dim", ` ${theme.fg("warning", "✗")} ${this.formatPathWithSource(d.collision.loserPath, metadata)} (skipped)`));
730
+ lines.push(theme.fg("dim", ` ${theme.fg("warning", "✗")} ${this.formatPathWithSource(d.collision.loserPath, this.findSourceInfoForPath(d.collision.loserPath, sourceInfos))} (skipped)`));
699
731
  }
700
732
  }
701
733
  }
702
- // Format other diagnostics (skill name collisions, parse errors, etc.)
703
734
  for (const d of otherDiagnostics) {
704
735
  if (d.path) {
705
- // Use metadata-aware formatting for paths
706
- const sourceInfo = this.formatPathWithSource(d.path, metadata);
707
- lines.push(theme.fg(d.type === "error" ? "error" : "warning", ` ${sourceInfo}`));
736
+ const formattedPath = this.formatPathWithSource(d.path, this.findSourceInfoForPath(d.path, sourceInfos));
737
+ lines.push(theme.fg(d.type === "error" ? "error" : "warning", ` ${formattedPath}`));
708
738
  lines.push(theme.fg(d.type === "error" ? "error" : "warning", ` ${d.message}`));
709
739
  }
710
740
  else {
@@ -719,11 +749,36 @@ export class InteractiveMode {
719
749
  if (!showListing && !showDiagnostics) {
720
750
  return;
721
751
  }
722
- const metadata = this.session.resourceLoader.getPathMetadata();
723
752
  const sectionHeader = (name, color = "mdHeading") => theme.fg(color, `[${name}]`);
724
753
  const skillsResult = this.session.resourceLoader.getSkills();
725
754
  const promptsResult = this.session.resourceLoader.getPrompts();
726
755
  const themesResult = this.session.resourceLoader.getThemes();
756
+ const extensions = options?.extensions ??
757
+ this.session.resourceLoader.getExtensions().extensions.map((extension) => ({
758
+ path: extension.path,
759
+ sourceInfo: extension.sourceInfo,
760
+ }));
761
+ const sourceInfos = new Map();
762
+ for (const extension of extensions) {
763
+ if (extension.sourceInfo) {
764
+ sourceInfos.set(extension.path, extension.sourceInfo);
765
+ }
766
+ }
767
+ for (const skill of skillsResult.skills) {
768
+ if (skill.sourceInfo) {
769
+ sourceInfos.set(skill.filePath, skill.sourceInfo);
770
+ }
771
+ }
772
+ for (const prompt of promptsResult.prompts) {
773
+ if (prompt.sourceInfo) {
774
+ sourceInfos.set(prompt.filePath, prompt.sourceInfo);
775
+ }
776
+ }
777
+ for (const loadedTheme of themesResult.themes) {
778
+ if (loadedTheme.sourcePath && loadedTheme.sourceInfo) {
779
+ sourceInfos.set(loadedTheme.sourcePath, loadedTheme.sourceInfo);
780
+ }
781
+ }
727
782
  if (showListing) {
728
783
  const contextFiles = this.session.resourceLoader.getAgentsFiles().agentsFiles;
729
784
  if (contextFiles.length > 0) {
@@ -736,39 +791,36 @@ export class InteractiveMode {
736
791
  }
737
792
  const skills = skillsResult.skills;
738
793
  if (skills.length > 0) {
739
- const skillPaths = skills.map((s) => s.filePath);
740
- const groups = this.buildScopeGroups(skillPaths, metadata);
794
+ const groups = this.buildScopeGroups(skills.map((skill) => ({ path: skill.filePath, sourceInfo: skill.sourceInfo })));
741
795
  const skillList = this.formatScopeGroups(groups, {
742
- formatPath: (p) => this.formatDisplayPath(p),
743
- formatPackagePath: (p, source) => this.getShortPath(p, source),
796
+ formatPath: (item) => this.formatDisplayPath(item.path),
797
+ formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
744
798
  });
745
799
  this.chatContainer.addChild(new Text(`${sectionHeader("Skills")}\n${skillList}`, 0, 0));
746
800
  this.chatContainer.addChild(new Spacer(1));
747
801
  }
748
802
  const templates = this.session.promptTemplates;
749
803
  if (templates.length > 0) {
750
- const templatePaths = templates.map((t) => t.filePath);
751
- const groups = this.buildScopeGroups(templatePaths, metadata);
804
+ const groups = this.buildScopeGroups(templates.map((template) => ({ path: template.filePath, sourceInfo: template.sourceInfo })));
752
805
  const templateByPath = new Map(templates.map((t) => [t.filePath, t]));
753
806
  const templateList = this.formatScopeGroups(groups, {
754
- formatPath: (p) => {
755
- const template = templateByPath.get(p);
756
- return template ? `/${template.name}` : this.formatDisplayPath(p);
807
+ formatPath: (item) => {
808
+ const template = templateByPath.get(item.path);
809
+ return template ? `/${template.name}` : this.formatDisplayPath(item.path);
757
810
  },
758
- formatPackagePath: (p) => {
759
- const template = templateByPath.get(p);
760
- return template ? `/${template.name}` : this.formatDisplayPath(p);
811
+ formatPackagePath: (item) => {
812
+ const template = templateByPath.get(item.path);
813
+ return template ? `/${template.name}` : this.formatDisplayPath(item.path);
761
814
  },
762
815
  });
763
816
  this.chatContainer.addChild(new Text(`${sectionHeader("Prompts")}\n${templateList}`, 0, 0));
764
817
  this.chatContainer.addChild(new Spacer(1));
765
818
  }
766
- const extensionPaths = options?.extensionPaths ?? [];
767
- if (extensionPaths.length > 0) {
768
- const groups = this.buildScopeGroups(extensionPaths, metadata);
819
+ if (extensions.length > 0) {
820
+ const groups = this.buildScopeGroups(extensions);
769
821
  const extList = this.formatScopeGroups(groups, {
770
- formatPath: (p) => this.formatDisplayPath(p),
771
- formatPackagePath: (p, source) => this.getShortPath(p, source),
822
+ formatPath: (item) => this.formatDisplayPath(item.path),
823
+ formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
772
824
  });
773
825
  this.chatContainer.addChild(new Text(`${sectionHeader("Extensions", "mdHeading")}\n${extList}`, 0, 0));
774
826
  this.chatContainer.addChild(new Spacer(1));
@@ -777,11 +829,13 @@ export class InteractiveMode {
777
829
  const loadedThemes = themesResult.themes;
778
830
  const customThemes = loadedThemes.filter((t) => t.sourcePath);
779
831
  if (customThemes.length > 0) {
780
- const themePaths = customThemes.map((t) => t.sourcePath);
781
- const groups = this.buildScopeGroups(themePaths, metadata);
832
+ const groups = this.buildScopeGroups(customThemes.map((loadedTheme) => ({
833
+ path: loadedTheme.sourcePath,
834
+ sourceInfo: loadedTheme.sourceInfo,
835
+ })));
782
836
  const themeList = this.formatScopeGroups(groups, {
783
- formatPath: (p) => this.formatDisplayPath(p),
784
- formatPackagePath: (p, source) => this.getShortPath(p, source),
837
+ formatPath: (item) => this.formatDisplayPath(item.path),
838
+ formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
785
839
  });
786
840
  this.chatContainer.addChild(new Text(`${sectionHeader("Themes")}\n${themeList}`, 0, 0));
787
841
  this.chatContainer.addChild(new Spacer(1));
@@ -790,13 +844,13 @@ export class InteractiveMode {
790
844
  if (showDiagnostics) {
791
845
  const skillDiagnostics = skillsResult.diagnostics;
792
846
  if (skillDiagnostics.length > 0) {
793
- const warningLines = this.formatDiagnostics(skillDiagnostics, metadata);
847
+ const warningLines = this.formatDiagnostics(skillDiagnostics, sourceInfos);
794
848
  this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Skill conflicts]")}\n${warningLines}`, 0, 0));
795
849
  this.chatContainer.addChild(new Spacer(1));
796
850
  }
797
851
  const promptDiagnostics = promptsResult.diagnostics;
798
852
  if (promptDiagnostics.length > 0) {
799
- const warningLines = this.formatDiagnostics(promptDiagnostics, metadata);
853
+ const warningLines = this.formatDiagnostics(promptDiagnostics, sourceInfos);
800
854
  this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Prompt conflicts]")}\n${warningLines}`, 0, 0));
801
855
  this.chatContainer.addChild(new Spacer(1));
802
856
  }
@@ -809,16 +863,17 @@ export class InteractiveMode {
809
863
  }
810
864
  const commandDiagnostics = this.session.extensionRunner?.getCommandDiagnostics() ?? [];
811
865
  extensionDiagnostics.push(...commandDiagnostics);
866
+ extensionDiagnostics.push(...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner));
812
867
  const shortcutDiagnostics = this.session.extensionRunner?.getShortcutDiagnostics() ?? [];
813
868
  extensionDiagnostics.push(...shortcutDiagnostics);
814
869
  if (extensionDiagnostics.length > 0) {
815
- const warningLines = this.formatDiagnostics(extensionDiagnostics, metadata);
870
+ const warningLines = this.formatDiagnostics(extensionDiagnostics, sourceInfos);
816
871
  this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0));
817
872
  this.chatContainer.addChild(new Spacer(1));
818
873
  }
819
874
  const themeDiagnostics = themesResult.diagnostics;
820
875
  if (themeDiagnostics.length > 0) {
821
- const warningLines = this.formatDiagnostics(themeDiagnostics, metadata);
876
+ const warningLines = this.formatDiagnostics(themeDiagnostics, sourceInfos);
822
877
  this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
823
878
  this.chatContainer.addChild(new Spacer(1));
824
879
  }
@@ -907,19 +962,17 @@ export class InteractiveMode {
907
962
  this.setupAutocomplete(this.fdPath);
908
963
  const extensionRunner = this.session.extensionRunner;
909
964
  if (!extensionRunner) {
910
- this.showLoadedResources({ extensionPaths: [], force: false });
965
+ this.showLoadedResources({ extensions: [], force: false });
911
966
  return;
912
967
  }
913
968
  this.setupExtensionShortcuts(extensionRunner);
914
- this.showLoadedResources({ extensionPaths: extensionRunner.getExtensionPaths(), force: false });
969
+ this.showLoadedResources({ force: false });
915
970
  }
916
971
  /**
917
972
  * Get a registered tool definition by name (for custom rendering).
918
973
  */
919
974
  getRegisteredToolDefinition(toolName) {
920
- const tools = this.session.extensionRunner?.getAllRegisteredTools() ?? [];
921
- const registeredTool = tools.find((t) => t.definition.name === toolName);
922
- return registeredTool?.definition;
975
+ return this.session.getToolDefinition(toolName);
923
976
  }
924
977
  /**
925
978
  * Set up keyboard shortcuts registered by extensions.
@@ -1809,7 +1862,7 @@ export class InteractiveMode {
1809
1862
  for (const content of this.streamingMessage.content) {
1810
1863
  if (content.type === "toolCall") {
1811
1864
  if (!this.pendingTools.has(content.id)) {
1812
- const component = new ToolExecutionComponent(content.name, content.arguments, {
1865
+ const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
1813
1866
  showImages: this.settingsManager.getShowImages(),
1814
1867
  }, this.getRegisteredToolDefinition(content.name), this.ui);
1815
1868
  component.setExpanded(this.toolOutputExpanded);
@@ -1869,7 +1922,7 @@ export class InteractiveMode {
1869
1922
  case "tool_execution_start": {
1870
1923
  let component = this.pendingTools.get(event.toolCallId);
1871
1924
  if (!component) {
1872
- component = new ToolExecutionComponent(event.toolName, event.args, {
1925
+ component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
1873
1926
  showImages: this.settingsManager.getShowImages(),
1874
1927
  }, this.getRegisteredToolDefinition(event.toolName), this.ui);
1875
1928
  component.setExpanded(this.toolOutputExpanded);
@@ -2125,7 +2178,7 @@ export class InteractiveMode {
2125
2178
  // Render tool call components
2126
2179
  for (const content of message.content) {
2127
2180
  if (content.type === "toolCall") {
2128
- const component = new ToolExecutionComponent(content.name, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui);
2181
+ const component = new ToolExecutionComponent(content.name, content.id, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui);
2129
2182
  component.setExpanded(this.toolOutputExpanded);
2130
2183
  this.chatContainer.addChild(component);
2131
2184
  if (message.stopReason === "aborted" || message.stopReason === "error") {
@@ -3286,7 +3339,6 @@ export class InteractiveMode {
3286
3339
  this.rebuildChatFromMessages();
3287
3340
  dismissLoader(this.editor);
3288
3341
  this.showLoadedResources({
3289
- extensionPaths: runner?.getExtensionPaths() ?? [],
3290
3342
  force: false,
3291
3343
  showDiagnosticsWhenQuiet: true,
3292
3344
  });