@caupulican/pi-adaptative 0.80.48 → 0.80.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [0.80.49] - 2026-06-26
2
+
3
+ ### Added
4
+
5
+ - Added Edit-in-$EDITOR action (key `e`) in the Manage Library browser to directly edit the highlighted resource's source file and reload upon return.
6
+ - Added consistent "Profile / Situation" labeling across all settings selectors.
7
+ - Added friendly empty-state messaging in kind tabs with no available resources.
8
+
1
9
  ## [0.80.48] - 2026-06-26
2
10
 
3
11
  ### Added
@@ -18,10 +18,12 @@ export interface ProfileResourceEditorOptions {
18
18
  onSave: (resources: ResourceProfileSettings) => void;
19
19
  onCancel: () => void;
20
20
  onScopeChange?: () => void;
21
+ onEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;
21
22
  cwd?: string;
22
23
  agentDir?: string;
23
24
  externalResourceRoots?: string[];
24
25
  }
26
+ export declare function resolveResourceEditPath(_id: string, filePath: string | undefined, kind: ResourceProfileKind): string | undefined;
25
27
  export declare function classifyResourceSource(filePath: string | undefined, cwd: string, agentDir: string, externalRoots: string[]): "catalog" | "user" | "project" | "bundled";
26
28
  export declare class ProfileResourceEditorComponent extends Container implements Focusable {
27
29
  private profileName;
@@ -49,6 +51,7 @@ export declare class ProfileResourceEditorComponent extends Container implements
49
51
  private onSave;
50
52
  private onCancel;
51
53
  private onScopeChange?;
54
+ private onEdit?;
52
55
  constructor(options: ProfileResourceEditorOptions);
53
56
  private getKindHeaderText;
54
57
  private getCurrentKind;
@@ -1 +1 @@
1
- {"version":3,"file":"profile-resource-editor.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"AACA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAKL,MAAM,oBAAoB,CAAC;AAO5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAKtG,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACzC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,4BAA4B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,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;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAwBD,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EAAE,GACrB,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CA8B5C;AAED,qBAAa,8BAA+B,SAAQ,SAAU,YAAW,SAAS;IACjF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,aAAa,CAAoD;IACzE,OAAO,CAAC,aAAa,CAAwD;IAC7E,OAAO,CAAC,gBAAgB,CAAoD;IAC5E,OAAO,CAAC,gBAAgB,CAAK;IAE7B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,qBAAqB,CAAW;IAExC,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,eAAe,CAAO;IAC9B,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;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAa;IAEnC,YAAY,OAAO,EAAE,4BAA4B,EA8FhD;IAED,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,UAAU;IAqDlB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,UAAU;IAwDlB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA+F9B;IAED,OAAO,CAAC,cAAc;IAgBtB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { isAbsolute, resolve } from \"node:path\";\nimport {\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 {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} 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 ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\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\tonScopeChange?: () => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\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\tprivate onScopeChange?: () => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\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: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change.`,\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\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// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\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.refresh();\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 framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\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\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\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.items.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} ${i.description ?? \"\"}`) : 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\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\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\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\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\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 framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\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"]}
1
+ {"version":3,"file":"profile-resource-editor.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport {\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 {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} 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 ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\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\tonScopeChange?: () => void;\n\tonEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function resolveResourceEditPath(\n\t_id: string,\n\tfilePath: string | undefined,\n\tkind: ResourceProfileKind,\n): string | undefined {\n\tif (kind === \"tools\") {\n\t\treturn undefined;\n\t}\n\n\tif (!filePath) {\n\t\treturn undefined;\n\t}\n\n\tif (kind === \"extensions\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst indexTs = path.join(filePath, \"index.ts\");\n\t\t\t\tif (fs.existsSync(indexTs)) {\n\t\t\t\t\treturn indexTs;\n\t\t\t\t}\n\t\t\t\tconst indexJs = path.join(filePath, \"index.js\");\n\t\t\t\tif (fs.existsSync(indexJs)) {\n\t\t\t\t\treturn indexJs;\n\t\t\t\t}\n\t\t\t\tconst pkgJsonPath = path.join(filePath, \"package.json\");\n\t\t\t\tif (fs.existsSync(pkgJsonPath)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst pkg = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\t\t\t\t\t\tif (typeof pkg.main === \"string\") {\n\t\t\t\t\t\t\tconst resolvedMain = path.resolve(filePath, pkg.main);\n\t\t\t\t\t\t\tif (fs.existsSync(resolvedMain)) {\n\t\t\t\t\t\t\t\treturn resolvedMain;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t}\n\t\t\t\treturn indexTs;\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\tif (kind === \"skills\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst skillMd = path.join(filePath, \"SKILL.md\");\n\t\t\t\tif (fs.existsSync(skillMd)) {\n\t\t\t\t\treturn skillMd;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\treturn filePath;\n}\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\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\tprivate onScopeChange?: () => void;\n\tprivate onEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.onEdit = options.onEdit;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\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: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change. Edit: ${theme.fg(\"accent\", \"e\")}.`,\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\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// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\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.refresh();\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 framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\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\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\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.items.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\t`${theme.fg(\"accent\", \"e\")} edit`,\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} ${i.description ?? \"\"}`) : 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\tconst kind = this.getCurrentKind();\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tconst totalAvailableCount = kind.items.length + missingSet.size;\n\n\t\tif (totalAvailableCount === 0) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", \" (none available — add a Source or install resources)\"), 0, 0),\n\t\t\t);\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\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\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\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\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Edit highlighted resource\n\t\tif (data === \"e\" && this.searchInput.getValue() === \"\") {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && !item.isMissing && item.path) {\n\t\t\t\tconst kind = this.getCurrentKind().kind;\n\t\t\t\tif (this.onEdit) {\n\t\t\t\t\tthis.onEdit(item.id, item.path, kind);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\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\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 framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\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"]}
@@ -1,3 +1,5 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
1
3
  import { isAbsolute, resolve } from "node:path";
2
4
  import { Container, fuzzyFilter, getKeybindings, Input, Key, matchesKey, Spacer, Text, } from "@caupulican/pi-tui";
3
5
  import { decodeResourceSelection, detectResourceFraming, encodeResourceSelectionWithFraming, } from "../../../core/profile-resource-selection.js";
@@ -16,6 +18,59 @@ const TOOL_DESCRIPTIONS = {
16
18
  skillify: "Convert a set of files/instructions into a skill.",
17
19
  extensionify: "Package a tool or script as an extension.",
18
20
  };
21
+ export function resolveResourceEditPath(_id, filePath, kind) {
22
+ if (kind === "tools") {
23
+ return undefined;
24
+ }
25
+ if (!filePath) {
26
+ return undefined;
27
+ }
28
+ if (kind === "extensions") {
29
+ try {
30
+ const stats = fs.statSync(filePath);
31
+ if (stats.isDirectory()) {
32
+ const indexTs = path.join(filePath, "index.ts");
33
+ if (fs.existsSync(indexTs)) {
34
+ return indexTs;
35
+ }
36
+ const indexJs = path.join(filePath, "index.js");
37
+ if (fs.existsSync(indexJs)) {
38
+ return indexJs;
39
+ }
40
+ const pkgJsonPath = path.join(filePath, "package.json");
41
+ if (fs.existsSync(pkgJsonPath)) {
42
+ try {
43
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
44
+ if (typeof pkg.main === "string") {
45
+ const resolvedMain = path.resolve(filePath, pkg.main);
46
+ if (fs.existsSync(resolvedMain)) {
47
+ return resolvedMain;
48
+ }
49
+ }
50
+ }
51
+ catch { }
52
+ }
53
+ return indexTs;
54
+ }
55
+ }
56
+ catch { }
57
+ return filePath;
58
+ }
59
+ if (kind === "skills") {
60
+ try {
61
+ const stats = fs.statSync(filePath);
62
+ if (stats.isDirectory()) {
63
+ const skillMd = path.join(filePath, "SKILL.md");
64
+ if (fs.existsSync(skillMd)) {
65
+ return skillMd;
66
+ }
67
+ }
68
+ }
69
+ catch { }
70
+ return filePath;
71
+ }
72
+ return filePath;
73
+ }
19
74
  export function classifyResourceSource(filePath, cwd, agentDir, externalRoots) {
20
75
  if (!filePath) {
21
76
  return "bundled";
@@ -72,6 +127,7 @@ export class ProfileResourceEditorComponent extends Container {
72
127
  onSave;
73
128
  onCancel;
74
129
  onScopeChange;
130
+ onEdit;
75
131
  constructor(options) {
76
132
  super();
77
133
  this.profileName = options.profileName;
@@ -80,6 +136,7 @@ export class ProfileResourceEditorComponent extends Container {
80
136
  this.onSave = options.onSave;
81
137
  this.onCancel = options.onCancel;
82
138
  this.onScopeChange = options.onScopeChange;
139
+ this.onEdit = options.onEdit;
83
140
  this.cwd = options.cwd || process.cwd();
84
141
  this.agentDir = options.agentDir || "";
85
142
  this.externalResourceRoots = options.externalResourceRoots || [];
@@ -118,7 +175,7 @@ export class ProfileResourceEditorComponent extends Container {
118
175
  // Header
119
176
  this.addChild(new DynamicBorder());
120
177
  this.addChild(new Text(theme.fg("accent", theme.bold(`Library — editing "${this.profileName}" (${this.profileScope})`)), 0, 0));
121
- this.addChild(new Text(theme.fg("muted", `Navigate kinds: ${keyText("tui.input.tab")}. Toggle: ${keyText("tui.select.confirm")}. Mode: ${theme.fg("accent", "a")} allow / ${theme.fg("accent", "b")} block. Scope: ${theme.fg("accent", "s")} change.`), 0, 0));
178
+ this.addChild(new Text(theme.fg("muted", `Navigate kinds: ${keyText("tui.input.tab")}. Toggle: ${keyText("tui.select.confirm")}. Mode: ${theme.fg("accent", "a")} allow / ${theme.fg("accent", "b")} block. Scope: ${theme.fg("accent", "s")} change. Edit: ${theme.fg("accent", "e")}.`), 0, 0));
122
179
  this.addChild(new Spacer(1));
123
180
  // Kind selector header
124
181
  this.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);
@@ -215,6 +272,7 @@ export class ProfileResourceEditorComponent extends Container {
215
272
  `${keyText("tui.select.confirm")} toggle`,
216
273
  `${keyText("tui.input.tab")} kind`,
217
274
  `${keyText("app.models.save")} save`,
275
+ `${theme.fg("accent", "e")} edit`,
218
276
  countText,
219
277
  ];
220
278
  return this.isDirty
@@ -231,6 +289,14 @@ export class ProfileResourceEditorComponent extends Container {
231
289
  }
232
290
  updateList() {
233
291
  this.listContainer.clear();
292
+ const kind = this.getCurrentKind();
293
+ const missingSet = this.missingIdsByKind.get(kind.kind) || new Set();
294
+ const totalAvailableCount = kind.items.length + missingSet.size;
295
+ if (totalAvailableCount === 0) {
296
+ this.listContainer.addChild(new Text(theme.fg("muted", " (none available — add a Source or install resources)"), 0, 0));
297
+ this.descriptionText.setText(theme.fg("muted", " No description available"));
298
+ return;
299
+ }
234
300
  if (this.filteredItems.length === 0) {
235
301
  this.listContainer.addChild(new Text(theme.fg("muted", " No matching resources"), 0, 0));
236
302
  this.descriptionText.setText(theme.fg("muted", " No description available"));
@@ -315,6 +381,17 @@ export class ProfileResourceEditorComponent extends Container {
315
381
  this.refresh();
316
382
  return;
317
383
  }
384
+ // Edit highlighted resource
385
+ if (data === "e" && this.searchInput.getValue() === "") {
386
+ const item = this.filteredItems[this.selectedIndex];
387
+ if (item && !item.isMissing && item.path) {
388
+ const kind = this.getCurrentKind().kind;
389
+ if (this.onEdit) {
390
+ this.onEdit(item.id, item.path, kind);
391
+ }
392
+ }
393
+ return;
394
+ }
318
395
  // Switch scope: Scope selector
319
396
  if (matchesKey(data, Key.ctrl("o")) || (this.searchInput.getValue() === "" && data === "s")) {
320
397
  if (this.onScopeChange) {
@@ -1 +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 { isAbsolute, resolve } from \"node:path\";\nimport {\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 {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} 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 ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\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\tonScopeChange?: () => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\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\tprivate onScopeChange?: () => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\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: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change.`,\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\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// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\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.refresh();\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 framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\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\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\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.items.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} ${i.description ?? \"\"}`) : 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\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\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\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\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\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 framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\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"]}
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 * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport {\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 {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} 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 ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\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\tonScopeChange?: () => void;\n\tonEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function resolveResourceEditPath(\n\t_id: string,\n\tfilePath: string | undefined,\n\tkind: ResourceProfileKind,\n): string | undefined {\n\tif (kind === \"tools\") {\n\t\treturn undefined;\n\t}\n\n\tif (!filePath) {\n\t\treturn undefined;\n\t}\n\n\tif (kind === \"extensions\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst indexTs = path.join(filePath, \"index.ts\");\n\t\t\t\tif (fs.existsSync(indexTs)) {\n\t\t\t\t\treturn indexTs;\n\t\t\t\t}\n\t\t\t\tconst indexJs = path.join(filePath, \"index.js\");\n\t\t\t\tif (fs.existsSync(indexJs)) {\n\t\t\t\t\treturn indexJs;\n\t\t\t\t}\n\t\t\t\tconst pkgJsonPath = path.join(filePath, \"package.json\");\n\t\t\t\tif (fs.existsSync(pkgJsonPath)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst pkg = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\t\t\t\t\t\tif (typeof pkg.main === \"string\") {\n\t\t\t\t\t\t\tconst resolvedMain = path.resolve(filePath, pkg.main);\n\t\t\t\t\t\t\tif (fs.existsSync(resolvedMain)) {\n\t\t\t\t\t\t\t\treturn resolvedMain;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t}\n\t\t\t\treturn indexTs;\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\tif (kind === \"skills\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst skillMd = path.join(filePath, \"SKILL.md\");\n\t\t\t\tif (fs.existsSync(skillMd)) {\n\t\t\t\t\treturn skillMd;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\treturn filePath;\n}\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\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\tprivate onScopeChange?: () => void;\n\tprivate onEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.onEdit = options.onEdit;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\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: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change. Edit: ${theme.fg(\"accent\", \"e\")}.`,\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\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// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\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.refresh();\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 framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\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\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\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.items.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\t`${theme.fg(\"accent\", \"e\")} edit`,\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} ${i.description ?? \"\"}`) : 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\tconst kind = this.getCurrentKind();\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tconst totalAvailableCount = kind.items.length + missingSet.size;\n\n\t\tif (totalAvailableCount === 0) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", \" (none available — add a Source or install resources)\"), 0, 0),\n\t\t\t);\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\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\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\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\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\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// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Edit highlighted resource\n\t\tif (data === \"e\" && this.searchInput.getValue() === \"\") {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && !item.isMissing && item.path) {\n\t\t\t\tconst kind = this.getCurrentKind().kind;\n\t\t\t\tif (this.onEdit) {\n\t\t\t\t\tthis.onEdit(item.id, item.path, kind);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\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\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 framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\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"]}