@caupulican/pi-adaptative 0.80.40 → 0.80.44

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 (105) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/bundled-resources/prompts/extensionify.md +20 -0
  3. package/dist/bundled-resources/prompts/learn.md +27 -0
  4. package/dist/bundled-resources/prompts/skillify.md +21 -0
  5. package/dist/bundled-resources/skills/pi-harness-learning/SKILL.md +217 -0
  6. package/dist/bundled-resources/skills/skill-architect/SKILL.md +162 -0
  7. package/dist/config.d.ts +17 -0
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +30 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +37 -2
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +217 -11
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/compaction/compaction.d.ts +1 -1
  16. package/dist/core/compaction/compaction.d.ts.map +1 -1
  17. package/dist/core/compaction/compaction.js +4 -2
  18. package/dist/core/compaction/compaction.js.map +1 -1
  19. package/dist/core/context-gc.d.ts.map +1 -1
  20. package/dist/core/context-gc.js +27 -5
  21. package/dist/core/context-gc.js.map +1 -1
  22. package/dist/core/extensions/loader.d.ts +10 -3
  23. package/dist/core/extensions/loader.d.ts.map +1 -1
  24. package/dist/core/extensions/loader.js +38 -7
  25. package/dist/core/extensions/loader.js.map +1 -1
  26. package/dist/core/extensions/runner.d.ts +6 -0
  27. package/dist/core/extensions/runner.d.ts.map +1 -1
  28. package/dist/core/extensions/runner.js +53 -6
  29. package/dist/core/extensions/runner.js.map +1 -1
  30. package/dist/core/extensions/types.d.ts +16 -2
  31. package/dist/core/extensions/types.d.ts.map +1 -1
  32. package/dist/core/extensions/types.js.map +1 -1
  33. package/dist/core/profile-registry.d.ts +39 -0
  34. package/dist/core/profile-registry.d.ts.map +1 -0
  35. package/dist/core/profile-registry.js +230 -0
  36. package/dist/core/profile-registry.js.map +1 -0
  37. package/dist/core/profile-resource-selection.d.ts +19 -0
  38. package/dist/core/profile-resource-selection.d.ts.map +1 -0
  39. package/dist/core/profile-resource-selection.js +84 -0
  40. package/dist/core/profile-resource-selection.js.map +1 -0
  41. package/dist/core/resource-loader.d.ts +33 -3
  42. package/dist/core/resource-loader.d.ts.map +1 -1
  43. package/dist/core/resource-loader.js +68 -11
  44. package/dist/core/resource-loader.js.map +1 -1
  45. package/dist/core/settings-manager.d.ts +44 -2
  46. package/dist/core/settings-manager.d.ts.map +1 -1
  47. package/dist/core/settings-manager.js +557 -27
  48. package/dist/core/settings-manager.js.map +1 -1
  49. package/dist/core/skills.d.ts +5 -0
  50. package/dist/core/skills.d.ts.map +1 -1
  51. package/dist/core/skills.js +2 -2
  52. package/dist/core/skills.js.map +1 -1
  53. package/dist/core/slash-commands.d.ts.map +1 -1
  54. package/dist/core/slash-commands.js +1 -0
  55. package/dist/core/slash-commands.js.map +1 -1
  56. package/dist/core/system-prompt.d.ts.map +1 -1
  57. package/dist/core/system-prompt.js +5 -17
  58. package/dist/core/system-prompt.js.map +1 -1
  59. package/dist/core/tools/extensionify.d.ts +33 -0
  60. package/dist/core/tools/extensionify.d.ts.map +1 -0
  61. package/dist/core/tools/extensionify.js +146 -0
  62. package/dist/core/tools/extensionify.js.map +1 -0
  63. package/dist/core/tools/index.d.ts +10 -1
  64. package/dist/core/tools/index.d.ts.map +1 -1
  65. package/dist/core/tools/index.js +36 -1
  66. package/dist/core/tools/index.js.map +1 -1
  67. package/dist/core/tools/skill-audit.d.ts +63 -0
  68. package/dist/core/tools/skill-audit.d.ts.map +1 -0
  69. package/dist/core/tools/skill-audit.js +175 -0
  70. package/dist/core/tools/skill-audit.js.map +1 -0
  71. package/dist/core/tools/skillify.d.ts +30 -0
  72. package/dist/core/tools/skillify.d.ts.map +1 -0
  73. package/dist/core/tools/skillify.js +91 -0
  74. package/dist/core/tools/skillify.js.map +1 -0
  75. package/dist/index.d.ts +1 -0
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +4 -0
  78. package/dist/index.js.map +1 -1
  79. package/dist/modes/interactive/components/profile-resource-editor.d.ts +50 -0
  80. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -0
  81. package/dist/modes/interactive/components/profile-resource-editor.js +232 -0
  82. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -0
  83. package/dist/modes/interactive/components/profile-selector.d.ts +8 -0
  84. package/dist/modes/interactive/components/profile-selector.d.ts.map +1 -0
  85. package/dist/modes/interactive/components/profile-selector.js +77 -0
  86. package/dist/modes/interactive/components/profile-selector.js.map +1 -0
  87. package/dist/modes/interactive/components/settings-selector.d.ts +7 -0
  88. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  89. package/dist/modes/interactive/components/settings-selector.js +62 -0
  90. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  91. package/dist/modes/interactive/interactive-mode.d.ts +12 -0
  92. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  93. package/dist/modes/interactive/interactive-mode.js +362 -24
  94. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  95. package/docs/settings.md +20 -1
  96. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  97. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  98. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  99. package/examples/extensions/sandbox/package-lock.json +2 -2
  100. package/examples/extensions/sandbox/package.json +1 -1
  101. package/examples/extensions/with-deps/package-lock.json +2 -2
  102. package/examples/extensions/with-deps/package.json +1 -1
  103. package/examples/sdk/12-full-control.ts +4 -0
  104. package/npm-shrinkwrap.json +12 -12
  105. package/package.json +6 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-resource-editor.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAKL,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAKtG,MAAM,WAAW,yBAAyB;IACzC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,KAAK,EAAE,yBAAyB,EAAE,CAAC;IACnC,MAAM,EAAE,CAAC,SAAS,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAOD;;;;GAIG;AACH,qBAAa,8BAA+B,SAAQ,SAAU,YAAW,SAAS;IACjF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,aAAa,CAAoD;IACzE,OAAO,CAAC,gBAAgB,CAAK;IAE7B,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,QAAQ,CAAS;IAEzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,OAAO,CAAC,MAAM,CAA+C;IAC7D,OAAO,CAAC,QAAQ,CAAa;IAE7B,YAAY,OAAO,EAAE,4BAA4B,EAmDhD;IAED,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,UAAU;IA+BlB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAqE9B;IAED,OAAO,CAAC,cAAc;IAetB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@caupulican/pi-tui\";\nimport { decodeResourceSelection, encodeResourceSelection } from \"../../../core/profile-resource-selection.ts\";\nimport type { ResourceProfileKind, ResourceProfileSettings } from \"../../../core/settings-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\tallIds: string[]; // the full universe of selectable ids for this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tinitialResources: ResourceProfileSettings; // existing profile.resources; may be {}\n\tkinds: ProfileResourceEditorKind[]; // the six kinds, with their universes\n\tonSave: (resources: ResourceProfileSettings) => void; // called on ctrl+s with the encoded result\n\tonCancel: () => void; // called on esc\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n}\n\n/**\n * TUI component for editing per-kind resource toggles in a profile.\n * Shows a selectable list of resources for each kind (tools, skills, etc.),\n * with space-to-toggle, search filtering, and save/cancel callbacks.\n */\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate isDirty = false;\n\tprivate maxVisible = 8;\n\n\tprivate _focused = false;\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tprivate onSave: (resources: ResourceProfileSettings) => void;\n\tprivate onCancel: () => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\n\t\t// Initialize enabled sets for each kind via decoding\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst enabledSet = decodeResourceSelection(filter, kind.allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(`Edit Resources: ${this.profileName}`)), 0, 0));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`Navigate kinds with ${keyText(\"tui.input.tab\")}. Toggle with ${keyText(\"tui.select.confirm\")}.`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Kind selector header (shows current kind label and count)\n\t\tthis.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);\n\t\tthis.addChild(this.kindHeaderText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate getKindHeaderText(): string {\n\t\tconst kind = this.kinds[this.currentKindIndex]!;\n\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\tconst countText = `${enabledSet.size}/${kind.allIds.length} enabled`;\n\t\tconst kindIndicator = this.kinds\n\t\t\t.map((k, i) => {\n\t\t\t\tconst marker = i === this.currentKindIndex ? \"●\" : \"○\";\n\t\t\t\treturn theme.fg(i === this.currentKindIndex ? \"accent\" : \"muted\", `${marker} ${k.label}`);\n\t\t\t})\n\t\t\t.join(\" \");\n\t\treturn `${kindIndicator} ${theme.fg(\"muted\", countText)}`;\n\t}\n\n\tprivate getCurrentKind(): ProfileResourceEditorKind {\n\t\treturn this.kinds[this.currentKindIndex]!;\n\t}\n\n\tprivate getCurrentEnabledSet(): Set<string> {\n\t\tconst kind = this.getCurrentKind();\n\t\treturn this.enabledByKind.get(kind.kind)!;\n\t}\n\n\tprivate buildItems(): ResourceItem[] {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t// Return items sorted: enabled first (in order), then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const id of kind.allIds) {\n\t\t\tif (enabledSet.has(id)) {\n\t\t\t\tenabled.push({ id, enabled: true });\n\t\t\t} else {\n\t\t\t\tdisabled.push({ id, enabled: false });\n\t\t\t}\n\t\t}\n\t\treturn [...enabled, ...disabled];\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\tconst countText = `${enabledSet.size}/${kind.allIds.length} enabled`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"tui.input.tab\")} kind`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => i.id) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching resources\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\t\t\tconst status = item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${resourceText}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation within list\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch kind with Tab (cycles forward)\n\t\tif (kb.matches(data, \"tui.input.tab\")) {\n\t\t\tthis.currentKindIndex = (this.currentKindIndex + 1) % this.kinds.length;\n\t\t\tthis.selectedIndex = 0;\n\t\t\tthis.searchInput.setValue(\"\");\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on space/enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t\t\tif (enabledSet.has(item.id)) {\n\t\t\t\t\tenabledSet.delete(item.id);\n\t\t\t\t} else {\n\t\t\t\t\tenabledSet.add(item.id);\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.persistChanges();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tprivate persistChanges(): void {\n\t\t// Encode each kind and build the result\n\t\tconst resources: ResourceProfileSettings = {};\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\t\tconst encoded = encodeResourceSelection(enabledSet, kind.allIds);\n\t\t\tif (encoded !== undefined) {\n\t\t\t\tresources[kind.kind] = encoded;\n\t\t\t}\n\t\t}\n\t\tthis.onSave(resources);\n\t\tthis.isDirty = false;\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -0,0 +1,232 @@
1
+ import { Container, fuzzyFilter, getKeybindings, Input, Key, matchesKey, Spacer, Text, } from "@caupulican/pi-tui";
2
+ import { decodeResourceSelection, encodeResourceSelection } from "../../../core/profile-resource-selection.js";
3
+ import { theme } from "../theme/theme.js";
4
+ import { DynamicBorder } from "./dynamic-border.js";
5
+ import { keyText } from "./keybinding-hints.js";
6
+ /**
7
+ * TUI component for editing per-kind resource toggles in a profile.
8
+ * Shows a selectable list of resources for each kind (tools, skills, etc.),
9
+ * with space-to-toggle, search filtering, and save/cancel callbacks.
10
+ */
11
+ export class ProfileResourceEditorComponent extends Container {
12
+ profileName;
13
+ kinds;
14
+ enabledByKind = new Map();
15
+ currentKindIndex = 0;
16
+ filteredItems = [];
17
+ selectedIndex = 0;
18
+ searchInput;
19
+ kindHeaderText;
20
+ listContainer;
21
+ footerText;
22
+ isDirty = false;
23
+ maxVisible = 8;
24
+ _focused = false;
25
+ get focused() {
26
+ return this._focused;
27
+ }
28
+ set focused(value) {
29
+ this._focused = value;
30
+ this.searchInput.focused = value;
31
+ }
32
+ onSave;
33
+ onCancel;
34
+ constructor(options) {
35
+ super();
36
+ this.profileName = options.profileName;
37
+ this.kinds = options.kinds;
38
+ this.onSave = options.onSave;
39
+ this.onCancel = options.onCancel;
40
+ // Initialize enabled sets for each kind via decoding
41
+ for (const kind of this.kinds) {
42
+ const filter = options.initialResources[kind.kind];
43
+ const enabledSet = decodeResourceSelection(filter, kind.allIds);
44
+ this.enabledByKind.set(kind.kind, enabledSet);
45
+ }
46
+ // Header
47
+ this.addChild(new DynamicBorder());
48
+ this.addChild(new Text(theme.fg("accent", theme.bold(`Edit Resources: ${this.profileName}`)), 0, 0));
49
+ this.addChild(new Text(theme.fg("muted", `Navigate kinds with ${keyText("tui.input.tab")}. Toggle with ${keyText("tui.select.confirm")}.`), 0, 0));
50
+ this.addChild(new Spacer(1));
51
+ // Kind selector header (shows current kind label and count)
52
+ this.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);
53
+ this.addChild(this.kindHeaderText);
54
+ this.addChild(new Spacer(1));
55
+ // Search input
56
+ this.searchInput = new Input();
57
+ this.addChild(this.searchInput);
58
+ this.addChild(new Spacer(1));
59
+ // List container
60
+ this.listContainer = new Container();
61
+ this.addChild(this.listContainer);
62
+ // Footer hint
63
+ this.addChild(new Spacer(1));
64
+ this.footerText = new Text(this.getFooterText(), 0, 0);
65
+ this.addChild(this.footerText);
66
+ this.addChild(new DynamicBorder());
67
+ this.updateList();
68
+ }
69
+ getKindHeaderText() {
70
+ const kind = this.kinds[this.currentKindIndex];
71
+ const enabledSet = this.enabledByKind.get(kind.kind);
72
+ const countText = `${enabledSet.size}/${kind.allIds.length} enabled`;
73
+ const kindIndicator = this.kinds
74
+ .map((k, i) => {
75
+ const marker = i === this.currentKindIndex ? "●" : "○";
76
+ return theme.fg(i === this.currentKindIndex ? "accent" : "muted", `${marker} ${k.label}`);
77
+ })
78
+ .join(" ");
79
+ return `${kindIndicator} ${theme.fg("muted", countText)}`;
80
+ }
81
+ getCurrentKind() {
82
+ return this.kinds[this.currentKindIndex];
83
+ }
84
+ getCurrentEnabledSet() {
85
+ const kind = this.getCurrentKind();
86
+ return this.enabledByKind.get(kind.kind);
87
+ }
88
+ buildItems() {
89
+ const kind = this.getCurrentKind();
90
+ const enabledSet = this.getCurrentEnabledSet();
91
+ // Return items sorted: enabled first (in order), then disabled
92
+ const enabled = [];
93
+ const disabled = [];
94
+ for (const id of kind.allIds) {
95
+ if (enabledSet.has(id)) {
96
+ enabled.push({ id, enabled: true });
97
+ }
98
+ else {
99
+ disabled.push({ id, enabled: false });
100
+ }
101
+ }
102
+ return [...enabled, ...disabled];
103
+ }
104
+ getFooterText() {
105
+ const kind = this.getCurrentKind();
106
+ const enabledSet = this.getCurrentEnabledSet();
107
+ const countText = `${enabledSet.size}/${kind.allIds.length} enabled`;
108
+ const parts = [
109
+ `${keyText("tui.select.confirm")} toggle`,
110
+ `${keyText("tui.input.tab")} kind`,
111
+ `${keyText("app.models.save")} save`,
112
+ countText,
113
+ ];
114
+ return this.isDirty
115
+ ? theme.fg("dim", ` ${parts.join(" · ")} `) + theme.fg("warning", "(unsaved)")
116
+ : theme.fg("dim", ` ${parts.join(" · ")}`);
117
+ }
118
+ refresh() {
119
+ const query = this.searchInput.getValue();
120
+ const items = this.buildItems();
121
+ this.filteredItems = query ? fuzzyFilter(items, query, (i) => i.id) : items;
122
+ this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));
123
+ this.updateList();
124
+ this.footerText.setText(this.getFooterText());
125
+ }
126
+ updateList() {
127
+ this.listContainer.clear();
128
+ if (this.filteredItems.length === 0) {
129
+ this.listContainer.addChild(new Text(theme.fg("muted", " No matching resources"), 0, 0));
130
+ return;
131
+ }
132
+ const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible));
133
+ const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);
134
+ for (let i = startIndex; i < endIndex; i++) {
135
+ const item = this.filteredItems[i];
136
+ const isSelected = i === this.selectedIndex;
137
+ const prefix = isSelected ? theme.fg("accent", "→ ") : " ";
138
+ const resourceText = isSelected ? theme.fg("accent", item.id) : item.id;
139
+ const status = item.enabled ? theme.fg("success", " ✓") : theme.fg("dim", " ✗");
140
+ this.listContainer.addChild(new Text(`${prefix}${resourceText}${status}`, 0, 0));
141
+ }
142
+ // Add scroll indicator if needed
143
+ if (startIndex > 0 || endIndex < this.filteredItems.length) {
144
+ this.listContainer.addChild(new Text(theme.fg("muted", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0));
145
+ }
146
+ }
147
+ handleInput(data) {
148
+ const kb = getKeybindings();
149
+ // Navigation within list
150
+ if (kb.matches(data, "tui.select.up")) {
151
+ if (this.filteredItems.length === 0)
152
+ return;
153
+ this.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;
154
+ this.updateList();
155
+ return;
156
+ }
157
+ if (kb.matches(data, "tui.select.down")) {
158
+ if (this.filteredItems.length === 0)
159
+ return;
160
+ this.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;
161
+ this.updateList();
162
+ return;
163
+ }
164
+ // Switch kind with Tab (cycles forward)
165
+ if (kb.matches(data, "tui.input.tab")) {
166
+ this.currentKindIndex = (this.currentKindIndex + 1) % this.kinds.length;
167
+ this.selectedIndex = 0;
168
+ this.searchInput.setValue("");
169
+ this.kindHeaderText.setText(this.getKindHeaderText());
170
+ this.refresh();
171
+ return;
172
+ }
173
+ // Toggle on space/enter
174
+ if (kb.matches(data, "tui.select.confirm")) {
175
+ const item = this.filteredItems[this.selectedIndex];
176
+ if (item) {
177
+ const enabledSet = this.getCurrentEnabledSet();
178
+ if (enabledSet.has(item.id)) {
179
+ enabledSet.delete(item.id);
180
+ }
181
+ else {
182
+ enabledSet.add(item.id);
183
+ }
184
+ this.isDirty = true;
185
+ this.refresh();
186
+ }
187
+ return;
188
+ }
189
+ // Save/persist to settings
190
+ if (kb.matches(data, "app.models.save")) {
191
+ this.persistChanges();
192
+ return;
193
+ }
194
+ // Ctrl+C - clear search or cancel if empty
195
+ if (matchesKey(data, Key.ctrl("c"))) {
196
+ if (this.searchInput.getValue()) {
197
+ this.searchInput.setValue("");
198
+ this.refresh();
199
+ }
200
+ else {
201
+ this.onCancel();
202
+ }
203
+ return;
204
+ }
205
+ // Escape - cancel
206
+ if (matchesKey(data, Key.escape)) {
207
+ this.onCancel();
208
+ return;
209
+ }
210
+ // Pass everything else to search input
211
+ this.searchInput.handleInput(data);
212
+ this.refresh();
213
+ }
214
+ persistChanges() {
215
+ // Encode each kind and build the result
216
+ const resources = {};
217
+ for (const kind of this.kinds) {
218
+ const enabledSet = this.enabledByKind.get(kind.kind);
219
+ const encoded = encodeResourceSelection(enabledSet, kind.allIds);
220
+ if (encoded !== undefined) {
221
+ resources[kind.kind] = encoded;
222
+ }
223
+ }
224
+ this.onSave(resources);
225
+ this.isDirty = false;
226
+ this.footerText.setText(this.getFooterText());
227
+ }
228
+ getSearchInput() {
229
+ return this.searchInput;
230
+ }
231
+ }
232
+ //# sourceMappingURL=profile-resource-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-resource-editor.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@caupulican/pi-tui\";\nimport { decodeResourceSelection, encodeResourceSelection } from \"../../../core/profile-resource-selection.ts\";\nimport type { ResourceProfileKind, ResourceProfileSettings } from \"../../../core/settings-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\tallIds: string[]; // the full universe of selectable ids for this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tinitialResources: ResourceProfileSettings; // existing profile.resources; may be {}\n\tkinds: ProfileResourceEditorKind[]; // the six kinds, with their universes\n\tonSave: (resources: ResourceProfileSettings) => void; // called on ctrl+s with the encoded result\n\tonCancel: () => void; // called on esc\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n}\n\n/**\n * TUI component for editing per-kind resource toggles in a profile.\n * Shows a selectable list of resources for each kind (tools, skills, etc.),\n * with space-to-toggle, search filtering, and save/cancel callbacks.\n */\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate isDirty = false;\n\tprivate maxVisible = 8;\n\n\tprivate _focused = false;\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tprivate onSave: (resources: ResourceProfileSettings) => void;\n\tprivate onCancel: () => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\n\t\t// Initialize enabled sets for each kind via decoding\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst enabledSet = decodeResourceSelection(filter, kind.allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(`Edit Resources: ${this.profileName}`)), 0, 0));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`Navigate kinds with ${keyText(\"tui.input.tab\")}. Toggle with ${keyText(\"tui.select.confirm\")}.`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Kind selector header (shows current kind label and count)\n\t\tthis.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);\n\t\tthis.addChild(this.kindHeaderText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate getKindHeaderText(): string {\n\t\tconst kind = this.kinds[this.currentKindIndex]!;\n\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\tconst countText = `${enabledSet.size}/${kind.allIds.length} enabled`;\n\t\tconst kindIndicator = this.kinds\n\t\t\t.map((k, i) => {\n\t\t\t\tconst marker = i === this.currentKindIndex ? \"●\" : \"○\";\n\t\t\t\treturn theme.fg(i === this.currentKindIndex ? \"accent\" : \"muted\", `${marker} ${k.label}`);\n\t\t\t})\n\t\t\t.join(\" \");\n\t\treturn `${kindIndicator} ${theme.fg(\"muted\", countText)}`;\n\t}\n\n\tprivate getCurrentKind(): ProfileResourceEditorKind {\n\t\treturn this.kinds[this.currentKindIndex]!;\n\t}\n\n\tprivate getCurrentEnabledSet(): Set<string> {\n\t\tconst kind = this.getCurrentKind();\n\t\treturn this.enabledByKind.get(kind.kind)!;\n\t}\n\n\tprivate buildItems(): ResourceItem[] {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t// Return items sorted: enabled first (in order), then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const id of kind.allIds) {\n\t\t\tif (enabledSet.has(id)) {\n\t\t\t\tenabled.push({ id, enabled: true });\n\t\t\t} else {\n\t\t\t\tdisabled.push({ id, enabled: false });\n\t\t\t}\n\t\t}\n\t\treturn [...enabled, ...disabled];\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\tconst countText = `${enabledSet.size}/${kind.allIds.length} enabled`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"tui.input.tab\")} kind`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => i.id) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching resources\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\t\t\tconst status = item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${resourceText}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation within list\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch kind with Tab (cycles forward)\n\t\tif (kb.matches(data, \"tui.input.tab\")) {\n\t\t\tthis.currentKindIndex = (this.currentKindIndex + 1) % this.kinds.length;\n\t\t\tthis.selectedIndex = 0;\n\t\t\tthis.searchInput.setValue(\"\");\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on space/enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t\t\tif (enabledSet.has(item.id)) {\n\t\t\t\t\tenabledSet.delete(item.id);\n\t\t\t\t} else {\n\t\t\t\t\tenabledSet.add(item.id);\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.persistChanges();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tprivate persistChanges(): void {\n\t\t// Encode each kind and build the result\n\t\tconst resources: ResourceProfileSettings = {};\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\t\tconst encoded = encodeResourceSelection(enabledSet, kind.allIds);\n\t\t\tif (encoded !== undefined) {\n\t\t\t\tresources[kind.kind] = encoded;\n\t\t\t}\n\t\t}\n\t\tthis.onSave(resources);\n\t\tthis.isDirty = false;\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { Container, SelectList } from "@caupulican/pi-tui";
2
+ import type { NormalizedProfile } from "../../../core/profile-registry.ts";
3
+ export declare class ProfileSelectorComponent extends Container {
4
+ private selectList;
5
+ constructor(profiles: NormalizedProfile[], activeProfileNames: string[], onSelect: (profileName: string) => void, onCancel: () => void);
6
+ getSelectList(): SelectList;
7
+ }
8
+ //# sourceMappingURL=profile-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAsC,MAAM,oBAAoB,CAAC;AAChH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AA4B3E,qBAAa,wBAAyB,SAAQ,SAAS;IACtD,OAAO,CAAC,UAAU,CAAa;IAE/B,YACC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,kBAAkB,EAAE,MAAM,EAAE,EAC5B,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,QAAQ,EAAE,MAAM,IAAI,EAgDpB;IAED,aAAa,IAAI,UAAU,CAE1B;CACD","sourcesContent":["import { Container, type SelectItem, SelectList, type SelectListLayoutOptions, Text } from \"@caupulican/pi-tui\";\nimport type { NormalizedProfile } from \"../../../core/profile-registry.ts\";\nimport { getSelectListTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nconst PROFILE_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nfunction profileSourceLabel(profile: NormalizedProfile): string {\n\tswitch (profile.source) {\n\t\tcase \"global-settings\":\n\t\t\treturn \"global settings\";\n\t\tcase \"project-settings\":\n\t\t\treturn \"project settings\";\n\t\tcase \"directory-overlay\":\n\t\t\treturn \"directory overlay\";\n\t\tcase \"profile-file\":\n\t\t\treturn \"profile file\";\n\t\tcase \"inline\":\n\t\t\treturn \"runtime\";\n\t\tcase \"embedded\":\n\t\t\treturn \"embedded\";\n\t\tcase \"bundle\":\n\t\t\treturn \"bundle\";\n\t}\n}\n\nexport class ProfileSelectorComponent extends Container {\n\tprivate selectList: SelectList;\n\n\tconstructor(\n\t\tprofiles: NormalizedProfile[],\n\t\tactiveProfileNames: string[],\n\t\tonSelect: (profileName: string) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Profiles\")), 1, 0));\n\n\t\tconst active = new Set(activeProfileNames);\n\t\tconst items: SelectItem[] = [\n\t\t\t{\n\t\t\t\tvalue: \"(none)\",\n\t\t\t\tlabel: \"(none)\",\n\t\t\t\tdescription: active.size === 0 ? \"active\" : \"Use configured profile selection (session default)\",\n\t\t\t},\n\t\t\t...profiles.map((profile) => ({\n\t\t\t\tvalue: profile.name,\n\t\t\t\tlabel: profile.name,\n\t\t\t\tdescription: [\n\t\t\t\t\tactive.has(profile.name) ? \"active\" : undefined,\n\t\t\t\t\tprofileSourceLabel(profile),\n\t\t\t\t\tprofile.description,\n\t\t\t\t]\n\t\t\t\t\t.filter((part): part is string => Boolean(part))\n\t\t\t\t\t.join(\" · \"),\n\t\t\t})),\n\t\t];\n\n\t\tif (profiles.length === 0) {\n\t\t\titems.push({\n\t\t\t\tvalue: \"\",\n\t\t\t\tlabel: \"No profiles found\",\n\t\t\t\tdescription: \"Create profiles under ~/.pi/agent/profiles/ or settings.resourceProfiles\",\n\t\t\t});\n\t\t}\n\n\t\tthis.selectList = new SelectList(items, 10, getSelectListTheme(), PROFILE_SELECT_LIST_LAYOUT);\n\t\tconst activeIndex = items.findIndex((item) => item.value && active.has(item.value));\n\t\tconst selectedIndex = activeIndex >= 0 ? activeIndex : items.length > 0 && items[0].value === \"(none)\" ? 0 : -1;\n\t\tif (selectedIndex >= 0) {\n\t\t\tthis.selectList.setSelectedIndex(selectedIndex);\n\t\t}\n\t\tthis.selectList.onSelect = (item) => {\n\t\t\tif (!item.value) return;\n\t\t\tonSelect(item.value);\n\t\t};\n\t\tthis.selectList.onCancel = onCancel;\n\t\tthis.addChild(this.selectList);\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetSelectList(): SelectList {\n\t\treturn this.selectList;\n\t}\n}\n"]}
@@ -0,0 +1,77 @@
1
+ import { Container, SelectList, Text } from "@caupulican/pi-tui";
2
+ import { getSelectListTheme, theme } from "../theme/theme.js";
3
+ import { DynamicBorder } from "./dynamic-border.js";
4
+ const PROFILE_SELECT_LIST_LAYOUT = {
5
+ minPrimaryColumnWidth: 12,
6
+ maxPrimaryColumnWidth: 32,
7
+ };
8
+ function profileSourceLabel(profile) {
9
+ switch (profile.source) {
10
+ case "global-settings":
11
+ return "global settings";
12
+ case "project-settings":
13
+ return "project settings";
14
+ case "directory-overlay":
15
+ return "directory overlay";
16
+ case "profile-file":
17
+ return "profile file";
18
+ case "inline":
19
+ return "runtime";
20
+ case "embedded":
21
+ return "embedded";
22
+ case "bundle":
23
+ return "bundle";
24
+ }
25
+ }
26
+ export class ProfileSelectorComponent extends Container {
27
+ selectList;
28
+ constructor(profiles, activeProfileNames, onSelect, onCancel) {
29
+ super();
30
+ this.addChild(new DynamicBorder());
31
+ this.addChild(new Text(theme.fg("accent", theme.bold("Profiles")), 1, 0));
32
+ const active = new Set(activeProfileNames);
33
+ const items = [
34
+ {
35
+ value: "(none)",
36
+ label: "(none)",
37
+ description: active.size === 0 ? "active" : "Use configured profile selection (session default)",
38
+ },
39
+ ...profiles.map((profile) => ({
40
+ value: profile.name,
41
+ label: profile.name,
42
+ description: [
43
+ active.has(profile.name) ? "active" : undefined,
44
+ profileSourceLabel(profile),
45
+ profile.description,
46
+ ]
47
+ .filter((part) => Boolean(part))
48
+ .join(" · "),
49
+ })),
50
+ ];
51
+ if (profiles.length === 0) {
52
+ items.push({
53
+ value: "",
54
+ label: "No profiles found",
55
+ description: "Create profiles under ~/.pi/agent/profiles/ or settings.resourceProfiles",
56
+ });
57
+ }
58
+ this.selectList = new SelectList(items, 10, getSelectListTheme(), PROFILE_SELECT_LIST_LAYOUT);
59
+ const activeIndex = items.findIndex((item) => item.value && active.has(item.value));
60
+ const selectedIndex = activeIndex >= 0 ? activeIndex : items.length > 0 && items[0].value === "(none)" ? 0 : -1;
61
+ if (selectedIndex >= 0) {
62
+ this.selectList.setSelectedIndex(selectedIndex);
63
+ }
64
+ this.selectList.onSelect = (item) => {
65
+ if (!item.value)
66
+ return;
67
+ onSelect(item.value);
68
+ };
69
+ this.selectList.onCancel = onCancel;
70
+ this.addChild(this.selectList);
71
+ this.addChild(new DynamicBorder());
72
+ }
73
+ getSelectList() {
74
+ return this.selectList;
75
+ }
76
+ }
77
+ //# sourceMappingURL=profile-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, type SelectItem, SelectList, type SelectListLayoutOptions, Text } from \"@caupulican/pi-tui\";\nimport type { NormalizedProfile } from \"../../../core/profile-registry.ts\";\nimport { getSelectListTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nconst PROFILE_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nfunction profileSourceLabel(profile: NormalizedProfile): string {\n\tswitch (profile.source) {\n\t\tcase \"global-settings\":\n\t\t\treturn \"global settings\";\n\t\tcase \"project-settings\":\n\t\t\treturn \"project settings\";\n\t\tcase \"directory-overlay\":\n\t\t\treturn \"directory overlay\";\n\t\tcase \"profile-file\":\n\t\t\treturn \"profile file\";\n\t\tcase \"inline\":\n\t\t\treturn \"runtime\";\n\t\tcase \"embedded\":\n\t\t\treturn \"embedded\";\n\t\tcase \"bundle\":\n\t\t\treturn \"bundle\";\n\t}\n}\n\nexport class ProfileSelectorComponent extends Container {\n\tprivate selectList: SelectList;\n\n\tconstructor(\n\t\tprofiles: NormalizedProfile[],\n\t\tactiveProfileNames: string[],\n\t\tonSelect: (profileName: string) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Profiles\")), 1, 0));\n\n\t\tconst active = new Set(activeProfileNames);\n\t\tconst items: SelectItem[] = [\n\t\t\t{\n\t\t\t\tvalue: \"(none)\",\n\t\t\t\tlabel: \"(none)\",\n\t\t\t\tdescription: active.size === 0 ? \"active\" : \"Use configured profile selection (session default)\",\n\t\t\t},\n\t\t\t...profiles.map((profile) => ({\n\t\t\t\tvalue: profile.name,\n\t\t\t\tlabel: profile.name,\n\t\t\t\tdescription: [\n\t\t\t\t\tactive.has(profile.name) ? \"active\" : undefined,\n\t\t\t\t\tprofileSourceLabel(profile),\n\t\t\t\t\tprofile.description,\n\t\t\t\t]\n\t\t\t\t\t.filter((part): part is string => Boolean(part))\n\t\t\t\t\t.join(\" · \"),\n\t\t\t})),\n\t\t];\n\n\t\tif (profiles.length === 0) {\n\t\t\titems.push({\n\t\t\t\tvalue: \"\",\n\t\t\t\tlabel: \"No profiles found\",\n\t\t\t\tdescription: \"Create profiles under ~/.pi/agent/profiles/ or settings.resourceProfiles\",\n\t\t\t});\n\t\t}\n\n\t\tthis.selectList = new SelectList(items, 10, getSelectListTheme(), PROFILE_SELECT_LIST_LAYOUT);\n\t\tconst activeIndex = items.findIndex((item) => item.value && active.has(item.value));\n\t\tconst selectedIndex = activeIndex >= 0 ? activeIndex : items.length > 0 && items[0].value === \"(none)\" ? 0 : -1;\n\t\tif (selectedIndex >= 0) {\n\t\t\tthis.selectList.setSelectedIndex(selectedIndex);\n\t\t}\n\t\tthis.selectList.onSelect = (item) => {\n\t\t\tif (!item.value) return;\n\t\t\tonSelect(item.value);\n\t\t};\n\t\tthis.selectList.onCancel = onCancel;\n\t\tthis.addChild(this.selectList);\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetSelectList(): SelectList {\n\t\treturn this.selectList;\n\t}\n}\n"]}
@@ -32,6 +32,7 @@ export interface SettingsConfig {
32
32
  selfModification: {
33
33
  enabled: boolean;
34
34
  sourcePath?: string;
35
+ sourcePaths?: string[];
35
36
  };
36
37
  selfModificationScope?: SettingsScope;
37
38
  autonomy: AutonomySettings;
@@ -40,6 +41,8 @@ export interface SettingsConfig {
40
41
  autoLearnScope?: SettingsScope;
41
42
  currentModelPattern?: string;
42
43
  autoLearnModelOptions?: SelectItem[];
44
+ activeProfileName?: string;
45
+ profileOptions?: SelectItem[];
43
46
  }
44
47
  export interface SettingsCallbacks {
45
48
  onAutoCompactChange: (enabled: boolean) => void;
@@ -70,6 +73,10 @@ export interface SettingsCallbacks {
70
73
  onSelfModificationChange: (settings: SelfModificationSettings, scope: SettingsScope) => void;
71
74
  onAutonomyChange: (settings: AutonomySettings, scope: SettingsScope) => void;
72
75
  onAutoLearnChange: (settings: AutoLearnSettings, scope: SettingsScope) => void;
76
+ onProfileChange?: (profileName: string) => void;
77
+ onProfileEdit?: (profileName: string) => void;
78
+ onProfilePersistActive?: (scope: "session" | "directory" | "project" | "global") => void;
79
+ onProfileDelete?: (profileName: string) => void;
73
80
  onCancel: () => void;
74
81
  }
75
82
  /**