@caupulican/pi-adaptative 0.80.47 → 0.80.48

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 (26) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/core/profile-resource-selection.d.ts +9 -0
  3. package/dist/core/profile-resource-selection.d.ts.map +1 -1
  4. package/dist/core/profile-resource-selection.js +49 -0
  5. package/dist/core/profile-resource-selection.js.map +1 -1
  6. package/dist/modes/interactive/components/profile-resource-editor.d.ts +20 -6
  7. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  8. package/dist/modes/interactive/components/profile-resource-editor.js +176 -23
  9. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  10. package/dist/modes/interactive/components/settings-selector.d.ts +1 -7
  11. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  12. package/dist/modes/interactive/components/settings-selector.js +41 -108
  13. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  14. package/dist/modes/interactive/interactive-mode.d.ts +12 -2
  15. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  16. package/dist/modes/interactive/interactive-mode.js +447 -95
  17. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  18. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  19. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  20. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  21. package/examples/extensions/sandbox/package-lock.json +2 -2
  22. package/examples/extensions/sandbox/package.json +1 -1
  23. package/examples/extensions/with-deps/package-lock.json +2 -2
  24. package/examples/extensions/with-deps/package.json +1 -1
  25. package/npm-shrinkwrap.json +12 -12
  26. package/package.json +4 -4
@@ -32,6 +32,7 @@ import { hasProjectTrustInputs, ProjectTrustStore } from "../../core/trust-manag
32
32
  import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
33
33
  import { copyToClipboard } from "../../utils/clipboard.js";
34
34
  import { readClipboardImage } from "../../utils/clipboard-image.js";
35
+ import { parseFrontmatter } from "../../utils/frontmatter.js";
35
36
  import { parseGitUrl } from "../../utils/git.js";
36
37
  import { getCwdRelativePath, resolvePath } from "../../utils/paths.js";
37
38
  import { getPiUserAgent } from "../../utils/pi-user-agent.js";
@@ -5088,40 +5089,462 @@ export class InteractiveMode {
5088
5089
  this.updateAutoLearnFooter();
5089
5090
  this.showStatus(`Auto Learn settings saved to ${scope}. Use /auto-learn status or /auto-learn run.`);
5090
5091
  },
5091
- onProfileChange: (profile) => {
5092
+ onResourcesHubAction: (action) => {
5092
5093
  done();
5093
- void this.applyProfile(profile);
5094
+ void this.handleResourcesHubAction(action);
5094
5095
  },
5095
- onProfileCreate: () => {
5096
+ onCancel: () => {
5096
5097
  done();
5097
- void this.createProfileFlow();
5098
+ this.ui.requestRender();
5098
5099
  },
5099
- onProfileEdit: (profileName) => {
5100
+ });
5101
+ return { component: selector, focus: selector.getSettingsList() };
5102
+ });
5103
+ }
5104
+ async handleResourcesHubAction(action) {
5105
+ switch (action) {
5106
+ case "nudge-add-source":
5107
+ void this.addExternalResourceRootFlow().then(() => {
5108
+ void this.showSettingsSelector();
5109
+ });
5110
+ break;
5111
+ case "active-profile":
5112
+ void this.openActiveProfileSelector();
5113
+ break;
5114
+ case "manage-library":
5115
+ void this.openLibraryManagerFlow();
5116
+ break;
5117
+ case "manage-profiles":
5118
+ void this.openManageProfilesFlow();
5119
+ break;
5120
+ case "sources":
5121
+ void this.openSourcesManagerFlow();
5122
+ break;
5123
+ }
5124
+ }
5125
+ async openActiveProfileSelector() {
5126
+ const registry = this.settingsManager.getProfileRegistry();
5127
+ const profiles = registry.listProfiles();
5128
+ const activeNames = this.settingsManager.getActiveResourceProfileNames();
5129
+ const options = [
5130
+ { value: "(none)", label: "(none)", description: "No active profile (all resources enabled)" },
5131
+ ...profiles.map((p) => ({
5132
+ value: p.name,
5133
+ label: p.name,
5134
+ description: p.description || p.source,
5135
+ })),
5136
+ ];
5137
+ this.showSelector((done) => {
5138
+ const selector = new SelectSubmenu("Profile / Situation", "Select the active runtime profile for this session. This is session-only unless saved elsewhere.", options, activeNames[0] || "(none)", (value) => {
5139
+ done();
5140
+ void this.applyProfile(value === "(none)" ? "" : value).then(() => {
5141
+ void this.showSettingsSelector();
5142
+ });
5143
+ }, () => {
5144
+ done();
5145
+ void this.showSettingsSelector();
5146
+ });
5147
+ return { component: selector, focus: selector.getSelectList() };
5148
+ });
5149
+ }
5150
+ async openManageProfilesFlow() {
5151
+ const registry = this.settingsManager.getProfileRegistry();
5152
+ const profiles = registry.listProfiles();
5153
+ const editableProfiles = profiles.map((p) => ({
5154
+ value: p.name,
5155
+ label: p.name,
5156
+ description: p.description || p.source,
5157
+ }));
5158
+ const options = [
5159
+ { value: "create", label: "+ Create profile...", description: "Create a new resource profile definition." },
5160
+ ];
5161
+ if (this.settingsManager.getActiveResourceProfileNames().length > 0) {
5162
+ options.push({
5163
+ value: "persist",
5164
+ label: "Persist active profile to...",
5165
+ description: "Save the current active-profile selection so it survives restart.",
5166
+ });
5167
+ }
5168
+ if (editableProfiles.length > 0) {
5169
+ options.push({
5170
+ value: "delete",
5171
+ label: "Delete profile...",
5172
+ description: "Remove a profile definition from where it is stored.",
5173
+ });
5174
+ }
5175
+ this.showSelector((done) => {
5176
+ const selector = new SelectSubmenu("Manage Profiles", "Create, delete, or persist profile definitions.", options, "", (value) => {
5177
+ done();
5178
+ if (value === "create") {
5179
+ void this.createProfileFlow().then(() => {
5180
+ void this.showSettingsSelector();
5181
+ });
5182
+ }
5183
+ else if (value === "persist") {
5184
+ void this.openPersistProfileSelector();
5185
+ }
5186
+ else if (value === "delete") {
5187
+ void this.openDeleteProfileSelector();
5188
+ }
5189
+ }, () => {
5190
+ done();
5191
+ void this.showSettingsSelector();
5192
+ });
5193
+ return { component: selector, focus: selector.getSelectList() };
5194
+ });
5195
+ }
5196
+ async openPersistProfileSelector() {
5197
+ const scopeOptions = [
5198
+ { value: "session", label: "session", description: "Runtime only (not written to disk)" },
5199
+ {
5200
+ value: "directory",
5201
+ label: "directory",
5202
+ description: "~/.pi/agent/resource-profiles/<hash>/settings.json",
5203
+ },
5204
+ { value: "project", label: "project", description: ".pi/settings.json" },
5205
+ { value: "global", label: "global", description: "~/.pi/agent/settings.json" },
5206
+ ];
5207
+ this.showSelector((done) => {
5208
+ const selector = new SelectSubmenu("Persist Active Profile", "Choose where to write the active-profile selection.", scopeOptions, "directory", (value) => {
5209
+ done();
5210
+ this.persistActiveProfile(value);
5211
+ void this.showSettingsSelector();
5212
+ }, () => {
5213
+ done();
5214
+ void this.openManageProfilesFlow();
5215
+ });
5216
+ return { component: selector, focus: selector.getSelectList() };
5217
+ });
5218
+ }
5219
+ async openDeleteProfileSelector() {
5220
+ const registry = this.settingsManager.getProfileRegistry();
5221
+ const editableProfiles = registry.listProfiles().map((p) => ({
5222
+ value: p.name,
5223
+ label: p.name,
5224
+ description: p.description || p.source,
5225
+ }));
5226
+ this.showSelector((done) => {
5227
+ const selector = new SelectSubmenu("Delete Profile", "Pick a profile to delete.", editableProfiles, "", (value) => {
5228
+ done();
5229
+ this.deleteProfileFromSource(value);
5230
+ void this.showSettingsSelector();
5231
+ }, () => {
5232
+ done();
5233
+ void this.openManageProfilesFlow();
5234
+ });
5235
+ return { component: selector, focus: selector.getSelectList() };
5236
+ });
5237
+ }
5238
+ async openSourcesManagerFlow() {
5239
+ const externalRoots = this.settingsManager.getExternalResourceRoots();
5240
+ const trustedRoots = this.settingsManager.getTrustedResourceRoots();
5241
+ const options = [
5242
+ {
5243
+ value: "add",
5244
+ label: "+ Add external root...",
5245
+ description: "Register a new external directory root (requires trust)",
5246
+ },
5247
+ ];
5248
+ for (const r of externalRoots) {
5249
+ const isTrusted = trustedRoots.includes(r);
5250
+ options.push({
5251
+ value: `remove:${r}`,
5252
+ label: `Remove: ${r}`,
5253
+ description: isTrusted ? "Trusted external root" : "Untrusted external root",
5254
+ });
5255
+ }
5256
+ this.showSelector((done) => {
5257
+ const selector = new SelectSubmenu("Sources", "Manage external resource roots. Adding a root requires trust confirmation.", options, "", (value) => {
5258
+ done();
5259
+ if (value === "add") {
5260
+ void this.addExternalResourceRootFlow().then(() => {
5261
+ void this.showSettingsSelector();
5262
+ });
5263
+ }
5264
+ else if (value.startsWith("remove:")) {
5265
+ const root = value.slice("remove:".length);
5266
+ void this.removeExternalResourceRootFlow(root).then(() => {
5267
+ void this.showSettingsSelector();
5268
+ });
5269
+ }
5270
+ }, () => {
5271
+ done();
5272
+ void this.showSettingsSelector();
5273
+ });
5274
+ return { component: selector, focus: selector.getSelectList() };
5275
+ });
5276
+ }
5277
+ async openLibraryManagerFlow() {
5278
+ const activeNames = this.settingsManager.getActiveResourceProfileNames();
5279
+ const activeName = activeNames[0];
5280
+ if (!activeName || activeName === "(none)") {
5281
+ this.showSelector((done) => {
5282
+ const selector = new SelectSubmenu("No Active Profile", "Select or create a profile to manage the library.", [
5283
+ {
5284
+ value: "select",
5285
+ label: "Select existing profile...",
5286
+ description: "Choose an existing profile to activate.",
5287
+ },
5288
+ { value: "create", label: "Create new profile...", description: "Create a new profile definition." },
5289
+ ], "select", (value) => {
5100
5290
  done();
5101
- void this.openProfileResourceEditor(profileName);
5102
- },
5103
- onProfilePersistActive: (scope) => {
5291
+ if (value === "create") {
5292
+ void this.createProfileAndOpenLibraryFlow();
5293
+ }
5294
+ else {
5295
+ void this.selectProfileAndOpenLibraryFlow();
5296
+ }
5297
+ }, () => {
5104
5298
  done();
5105
- this.persistActiveProfile(scope);
5106
- },
5107
- onProfileDelete: (profileName) => {
5299
+ void this.showSettingsSelector();
5300
+ });
5301
+ return { component: selector, focus: selector.getSelectList() };
5302
+ });
5303
+ return;
5304
+ }
5305
+ const registry = this.settingsManager.getProfileRegistry();
5306
+ const profile = registry.getProfile(activeName);
5307
+ if (!profile) {
5308
+ this.showError(`Active profile "${activeName}" not found in registry.`);
5309
+ return;
5310
+ }
5311
+ const scope = this.scopeForProfileSource(profile.source);
5312
+ void this.openLibraryEditorForProfile(profile.name, scope);
5313
+ }
5314
+ async createProfileAndOpenLibraryFlow() {
5315
+ const name = await new Promise((resolve) => {
5316
+ this.showSelector((done) => {
5317
+ const input = new ExtensionInputComponent("Create Profile", "Enter profile name", (value) => {
5108
5318
  done();
5109
- this.deleteProfileFromSource(profileName);
5110
- },
5111
- onAddExternalResourceRoot: () => {
5319
+ resolve(value);
5320
+ }, () => {
5112
5321
  done();
5113
- void this.addExternalResourceRootFlow();
5114
- },
5115
- onRemoveExternalResourceRoot: (root) => {
5322
+ resolve(undefined);
5323
+ }, { tui: this.ui });
5324
+ return { component: input, focus: input };
5325
+ });
5326
+ });
5327
+ if (name === undefined) {
5328
+ void this.openLibraryManagerFlow();
5329
+ return;
5330
+ }
5331
+ const trimmed = name.trim();
5332
+ if (!trimmed) {
5333
+ this.showWarning("Profile name cannot be empty.");
5334
+ void this.openLibraryManagerFlow();
5335
+ return;
5336
+ }
5337
+ try {
5338
+ this.settingsManager.setProfileDefinition(trimmed, {
5339
+ name: trimmed,
5340
+ resources: {},
5341
+ }, "directory");
5342
+ await this.applyProfile(trimmed);
5343
+ void this.openLibraryEditorForProfile(trimmed, "directory");
5344
+ }
5345
+ catch (error) {
5346
+ this.showError(error instanceof Error ? error.message : String(error));
5347
+ void this.openLibraryManagerFlow();
5348
+ }
5349
+ }
5350
+ async selectProfileAndOpenLibraryFlow() {
5351
+ const registry = this.settingsManager.getProfileRegistry();
5352
+ const profiles = registry.listProfiles();
5353
+ const editableProfiles = profiles.map((p) => ({
5354
+ value: p.name,
5355
+ label: p.name,
5356
+ description: p.description || p.source,
5357
+ }));
5358
+ if (editableProfiles.length === 0) {
5359
+ this.showWarning("No existing profiles to select. Please create one.");
5360
+ void this.createProfileAndOpenLibraryFlow();
5361
+ return;
5362
+ }
5363
+ this.showSelector((done) => {
5364
+ const selector = new SelectSubmenu("Select Profile", "Pick a profile to activate and edit.", editableProfiles, "", (value) => {
5365
+ done();
5366
+ void this.applyProfile(value).then(() => {
5367
+ const profile = registry.getProfile(value);
5368
+ const scope = this.scopeForProfileSource(profile.source);
5369
+ void this.openLibraryEditorForProfile(value, scope);
5370
+ });
5371
+ }, () => {
5372
+ done();
5373
+ void this.openLibraryManagerFlow();
5374
+ });
5375
+ return { component: selector, focus: selector.getSelectList() };
5376
+ });
5377
+ }
5378
+ async getProfileResourceKinds() {
5379
+ const loader = this.session.resourceLoader;
5380
+ const base = (p) => p.split(/[\\/]/).pop() ?? p;
5381
+ const allDiscoverableExtensions = await loader.getDiscoverableExtensionPaths();
5382
+ const skills = loader.getSkills().skills;
5383
+ const prompts = loader.getPrompts().prompts;
5384
+ const themes = getAvailableThemesWithPaths();
5385
+ const agents = loader.getAgentsFiles().agentsFiles;
5386
+ const getAgentDescription = (filePath) => {
5387
+ try {
5388
+ const content = fs.readFileSync(filePath, "utf-8");
5389
+ const { frontmatter } = parseFrontmatter(content);
5390
+ if (typeof frontmatter.description === "string") {
5391
+ return frontmatter.description;
5392
+ }
5393
+ }
5394
+ catch { }
5395
+ return undefined;
5396
+ };
5397
+ const getExtensionDescription = (filePath) => {
5398
+ try {
5399
+ let dir = filePath;
5400
+ if (fs.existsSync(filePath) && !fs.statSync(filePath).isDirectory()) {
5401
+ dir = path.dirname(filePath);
5402
+ }
5403
+ const pkgPath = path.join(dir, "package.json");
5404
+ if (fs.existsSync(pkgPath)) {
5405
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
5406
+ if (typeof pkg.description === "string") {
5407
+ return pkg.description;
5408
+ }
5409
+ }
5410
+ }
5411
+ catch { }
5412
+ return undefined;
5413
+ };
5414
+ return [
5415
+ {
5416
+ kind: "tools",
5417
+ label: "Tools",
5418
+ items: Array.from(allToolNames).map((name) => ({ id: name })),
5419
+ },
5420
+ {
5421
+ kind: "skills",
5422
+ label: "Skills",
5423
+ items: skills.map((s) => ({
5424
+ id: s.name,
5425
+ path: s.filePath,
5426
+ description: s.description,
5427
+ })),
5428
+ },
5429
+ {
5430
+ kind: "extensions",
5431
+ label: "Extensions",
5432
+ items: allDiscoverableExtensions.map((e) => ({
5433
+ id: base(e),
5434
+ path: e,
5435
+ description: getExtensionDescription(e),
5436
+ })),
5437
+ },
5438
+ {
5439
+ kind: "agents",
5440
+ label: "Agents",
5441
+ items: agents.map((f) => ({
5442
+ id: base(f.path),
5443
+ path: f.path,
5444
+ description: getAgentDescription(f.path),
5445
+ })),
5446
+ },
5447
+ {
5448
+ kind: "prompts",
5449
+ label: "Prompts",
5450
+ items: prompts.map((p) => ({
5451
+ id: p.name,
5452
+ path: p.filePath,
5453
+ description: p.description,
5454
+ })),
5455
+ },
5456
+ {
5457
+ kind: "themes",
5458
+ label: "Themes",
5459
+ items: themes.map((t) => ({
5460
+ id: t.name,
5461
+ path: t.path,
5462
+ })),
5463
+ },
5464
+ ];
5465
+ }
5466
+ async openLibraryEditorForProfile(profileName, initialScope) {
5467
+ const currentScope = initialScope;
5468
+ const registry = this.settingsManager.getProfileRegistry();
5469
+ const profile = registry.getProfile(profileName);
5470
+ if (!profile) {
5471
+ this.showError(`Profile not found: ${profileName}`);
5472
+ return;
5473
+ }
5474
+ const kinds = await this.getProfileResourceKinds();
5475
+ const originalResources = profile.resources;
5476
+ const isActiveProfile = this.settingsManager.getActiveResourceProfileNames().includes(profile.name);
5477
+ this.showSelector((done) => {
5478
+ const editor = new ProfileResourceEditorComponent({
5479
+ profileName: profile.name,
5480
+ profileScope: currentScope,
5481
+ initialResources: profile.resources,
5482
+ kinds,
5483
+ cwd: this.sessionManager.getCwd(),
5484
+ agentDir: getAgentDir(),
5485
+ externalResourceRoots: this.settingsManager.getExternalResourceRoots(),
5486
+ onSave: (resources) => {
5116
5487
  done();
5117
- void this.removeExternalResourceRootFlow(root);
5488
+ try {
5489
+ this.settingsManager.setProfileDefinition(profileName, {
5490
+ name: profileName,
5491
+ description: profile.description,
5492
+ model: profile.model,
5493
+ thinking: profile.thinking,
5494
+ resources,
5495
+ }, currentScope);
5496
+ this.showStatus(`Saved profile "${profileName}" to ${currentScope}.`);
5497
+ if (isActiveProfile) {
5498
+ const extensionsChanged = originalResources.extensions !== resources.extensions;
5499
+ const otherResourcesChanged = originalResources.tools !== resources.tools ||
5500
+ originalResources.skills !== resources.skills ||
5501
+ originalResources.agents !== resources.agents ||
5502
+ originalResources.prompts !== resources.prompts ||
5503
+ originalResources.themes !== resources.themes;
5504
+ if (extensionsChanged && !otherResourcesChanged) {
5505
+ void this.reconcileExtensionsAndRefreshUI(profileName);
5506
+ }
5507
+ else {
5508
+ void this.refreshAfterProfileMutation(profileName);
5509
+ }
5510
+ }
5511
+ }
5512
+ catch (error) {
5513
+ this.showError(error instanceof Error ? error.message : String(error));
5514
+ }
5118
5515
  },
5119
5516
  onCancel: () => {
5120
5517
  done();
5121
- this.ui.requestRender();
5518
+ void this.openLibraryManagerFlow();
5519
+ },
5520
+ onScopeChange: () => {
5521
+ done();
5522
+ void this.promptScopeChangeForProfile(profileName, currentScope);
5122
5523
  },
5123
5524
  });
5124
- return { component: selector, focus: selector.getSettingsList() };
5525
+ return { component: editor, focus: editor };
5526
+ });
5527
+ }
5528
+ async promptScopeChangeForProfile(profileName, currentScope) {
5529
+ const scopeOptions = [
5530
+ { value: "session", label: "session", description: "Runtime only (not written to disk)" },
5531
+ {
5532
+ value: "directory",
5533
+ label: "directory",
5534
+ description: "~/.pi/agent/resource-profiles/<hash>/settings.json",
5535
+ },
5536
+ { value: "project", label: "project", description: ".pi/settings.json" },
5537
+ { value: "global", label: "global", description: "~/.pi/agent/settings.json" },
5538
+ ];
5539
+ this.showSelector((done) => {
5540
+ const selector = new SelectSubmenu("Change Profile Scope", `Select new scope for profile "${profileName}".`, scopeOptions, currentScope, (value) => {
5541
+ done();
5542
+ void this.openLibraryEditorForProfile(profileName, value);
5543
+ }, () => {
5544
+ done();
5545
+ void this.openLibraryEditorForProfile(profileName, currentScope);
5546
+ });
5547
+ return { component: selector, focus: selector.getSelectList() };
5125
5548
  });
5126
5549
  }
5127
5550
  async handleProfilesCommand(profileName) {
@@ -5216,24 +5639,6 @@ export class InteractiveMode {
5216
5639
  this.showError(error instanceof Error ? error.message : String(error));
5217
5640
  }
5218
5641
  }
5219
- async getProfileResourceKinds() {
5220
- const loader = this.session.resourceLoader;
5221
- const base = (p) => p.split(/[\\/]/).pop() ?? p;
5222
- // Get all discoverable extension paths (enabled and disabled) for profile filtering
5223
- const allDiscoverableExtensions = await loader.getDiscoverableExtensionPaths();
5224
- return [
5225
- { kind: "tools", label: "Tools", allIds: [...allToolNames] },
5226
- { kind: "skills", label: "Skills", allIds: loader.getSkills().skills.map((s) => s.name) },
5227
- {
5228
- kind: "extensions",
5229
- label: "Extensions",
5230
- allIds: allDiscoverableExtensions.map((e) => base(e)),
5231
- },
5232
- { kind: "agents", label: "Agents", allIds: loader.getAgentsFiles().agentsFiles.map((f) => base(f.path)) },
5233
- { kind: "prompts", label: "Prompts", allIds: loader.getPrompts().prompts.map((p) => p.name) },
5234
- { kind: "themes", label: "Themes", allIds: getAvailableThemes() },
5235
- ];
5236
- }
5237
5642
  /** Map where a profile currently lives to the scope we should write it back to. */
5238
5643
  scopeForProfileSource(source) {
5239
5644
  switch (source) {
@@ -5300,8 +5705,12 @@ export class InteractiveMode {
5300
5705
  this.showSelector((done) => {
5301
5706
  const editor = new ProfileResourceEditorComponent({
5302
5707
  profileName,
5708
+ profileScope: scope,
5303
5709
  initialResources: {},
5304
5710
  kinds,
5711
+ cwd: this.sessionManager.getCwd(),
5712
+ agentDir: getAgentDir(),
5713
+ externalResourceRoots: this.settingsManager.getExternalResourceRoots(),
5305
5714
  onSave: (resources) => {
5306
5715
  done();
5307
5716
  try {
@@ -5324,63 +5733,6 @@ export class InteractiveMode {
5324
5733
  return { component: editor, focus: editor };
5325
5734
  });
5326
5735
  }
5327
- async openProfileResourceEditor(profileName) {
5328
- const profile = this.settingsManager.getProfileRegistry().getProfile(profileName);
5329
- if (!profile) {
5330
- this.showError(`Profile not found: ${profileName}`);
5331
- return;
5332
- }
5333
- const scope = this.scopeForProfileSource(profile.source);
5334
- const kinds = await this.getProfileResourceKinds();
5335
- const isActiveProfile = this.settingsManager.getActiveResourceProfileNames().includes(profile.name);
5336
- const originalResources = profile.resources;
5337
- this.showSelector((done) => {
5338
- const editor = new ProfileResourceEditorComponent({
5339
- profileName: profile.name,
5340
- initialResources: profile.resources,
5341
- kinds,
5342
- onSave: (resources) => {
5343
- done();
5344
- try {
5345
- this.settingsManager.setProfileDefinition(profile.name, {
5346
- name: profile.name,
5347
- description: profile.description,
5348
- model: profile.model,
5349
- thinking: profile.thinking,
5350
- resources,
5351
- }, scope);
5352
- this.showStatus(`Saved profile "${profile.name}" to ${scope}.`);
5353
- // For active profiles, detect if only extensions changed to avoid full reload
5354
- if (isActiveProfile) {
5355
- const extensionsChanged = originalResources.extensions !== resources.extensions;
5356
- const otherResourcesChanged = originalResources.tools !== resources.tools ||
5357
- originalResources.skills !== resources.skills ||
5358
- originalResources.agents !== resources.agents ||
5359
- originalResources.prompts !== resources.prompts ||
5360
- originalResources.themes !== resources.themes;
5361
- if (extensionsChanged && !otherResourcesChanged) {
5362
- // Only extensions changed: use live reconciliation
5363
- void this.reconcileExtensionsAndRefreshUI(profile.name);
5364
- }
5365
- else {
5366
- // Other resources changed or mixed: use full reload
5367
- void this.refreshAfterProfileMutation(profile.name);
5368
- }
5369
- }
5370
- // Non-active profiles don't need refresh
5371
- }
5372
- catch (error) {
5373
- this.showError(error instanceof Error ? error.message : String(error));
5374
- }
5375
- },
5376
- onCancel: () => {
5377
- done();
5378
- this.ui.requestRender();
5379
- },
5380
- });
5381
- return { component: editor, focus: editor };
5382
- });
5383
- }
5384
5736
  persistActiveProfile(scope) {
5385
5737
  const active = this.settingsManager.getActiveResourceProfileNames()[0];
5386
5738
  if (!active) {