@caupulican/pi-adaptative 0.80.99 → 0.80.102
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 +203 -0
- package/dist/core/agent-session.d.ts +72 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +437 -46
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.d.ts +5 -0
- package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.js +28 -4
- package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
- package/dist/core/autonomy/path-scope.d.ts.map +1 -1
- package/dist/core/autonomy/path-scope.js +33 -2
- package/dist/core/autonomy/path-scope.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +15 -7
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/context/brain-curator.d.ts +10 -0
- package/dist/core/context/brain-curator.d.ts.map +1 -1
- package/dist/core/context/brain-curator.js +19 -2
- package/dist/core/context/brain-curator.js.map +1 -1
- package/dist/core/context-gc.d.ts +8 -3
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +5 -0
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/delegation/worker-actions.d.ts +0 -10
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +5 -7
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/worker-result.d.ts +6 -0
- package/dist/core/delegation/worker-result.d.ts.map +1 -1
- package/dist/core/delegation/worker-result.js +10 -4
- package/dist/core/delegation/worker-result.js.map +1 -1
- package/dist/core/delegation/worker-runner.d.ts +3 -0
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +6 -1
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/doctor.d.ts +66 -0
- package/dist/core/doctor.d.ts.map +1 -0
- package/dist/core/doctor.js +144 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/learning/learning-audit.d.ts +13 -1
- package/dist/core/learning/learning-audit.d.ts.map +1 -1
- package/dist/core/learning/learning-audit.js +22 -1
- package/dist/core/learning/learning-audit.js.map +1 -1
- package/dist/core/learning/learning-gate.d.ts +3 -0
- package/dist/core/learning/learning-gate.d.ts.map +1 -1
- package/dist/core/learning/learning-gate.js +13 -4
- package/dist/core/learning/learning-gate.js.map +1 -1
- package/dist/core/models/default-model-suggestions.d.ts +34 -0
- package/dist/core/models/default-model-suggestions.d.ts.map +1 -0
- package/dist/core/models/default-model-suggestions.js +58 -0
- package/dist/core/models/default-model-suggestions.js.map +1 -0
- package/dist/core/models/local-registration.d.ts +2 -0
- package/dist/core/models/local-registration.d.ts.map +1 -1
- package/dist/core/models/local-registration.js +2 -1
- package/dist/core/models/local-registration.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts +76 -2
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +233 -21
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/profile-resource-selection.d.ts +15 -3
- package/dist/core/profile-resource-selection.d.ts.map +1 -1
- package/dist/core/profile-resource-selection.js +19 -9
- package/dist/core/profile-resource-selection.js.map +1 -1
- package/dist/core/resource-loader.d.ts +20 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +42 -16
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +30 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +65 -3
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +14 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +8 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/fff-search-backend.d.ts +34 -1
- package/dist/core/tools/fff-search-backend.d.ts.map +1 -1
- package/dist/core/tools/fff-search-backend.js +43 -4
- package/dist/core/tools/fff-search-backend.js.map +1 -1
- package/dist/core/tools/find.d.ts +16 -0
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +17 -3
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +20 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +15 -3
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +17 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.d.ts +6 -2
- package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.js +55 -45
- package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -1
- package/dist/modes/interactive/components/model-suggestion-selector.d.ts +13 -0
- package/dist/modes/interactive/components/model-suggestion-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/model-suggestion-selector.js +40 -0
- package/dist/modes/interactive/components/model-suggestion-selector.js.map +1 -0
- package/dist/modes/interactive/components/profile-resource-editor.d.ts +3 -0
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +16 -2
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +98 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +8 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +57 -10
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/safe-write-stream.d.ts +8 -0
- package/dist/utils/safe-write-stream.d.ts.map +1 -1
- package/dist/utils/safe-write-stream.js +32 -0
- package/dist/utils/safe-write-stream.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +92 -2
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +172 -32
- package/dist/utils/tools-manager.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -36
- package/package.json +4 -4
|
@@ -5,61 +5,71 @@ const FITNESS_ROLE_SELECT_LIST_LAYOUT = {
|
|
|
5
5
|
minPrimaryColumnWidth: 18,
|
|
6
6
|
maxPrimaryColumnWidth: 34,
|
|
7
7
|
};
|
|
8
|
+
/** The role options in presentation order — one source of truth for the list AND pre-selection. */
|
|
9
|
+
const FITNESS_ROLE_ITEMS = [
|
|
10
|
+
{
|
|
11
|
+
value: "curator",
|
|
12
|
+
label: "Context curator",
|
|
13
|
+
description: "Local brain: digests GC-packed stubs, scores stale-chunk relevance (enables curation)",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: "executor",
|
|
17
|
+
label: "Toolkit executor",
|
|
18
|
+
description: "Owns direct toolkit commands end-to-end (Level-0 exact hits route here; needs tool-call fitness)",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
value: "router-cheap",
|
|
22
|
+
label: "Router: cheap tier",
|
|
23
|
+
description: "Handles trivial/read-only routed turns (model router settings)",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: "router-medium",
|
|
27
|
+
label: "Router: medium tier",
|
|
28
|
+
description: "Handles normal implementation turns (model router settings)",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
value: "router-expensive",
|
|
32
|
+
label: "Router: expensive tier",
|
|
33
|
+
description: "Handles high-impact/architecture turns (model router settings)",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
value: "judge",
|
|
37
|
+
label: "Routing judge",
|
|
38
|
+
description: "Routing-only tier verdicts; needs strong instruction following",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
value: "learning",
|
|
42
|
+
label: "Learning / reflection",
|
|
43
|
+
description: "Background reflection and learning passes",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
value: "none",
|
|
47
|
+
label: "No role for now",
|
|
48
|
+
description: "Keep the result in the fitness store only (assign later from /settings)",
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
/** The assignable roles in the order the selector presents them (index 0 is the default landing). */
|
|
52
|
+
export const FITNESS_ROLE_ORDER = FITNESS_ROLE_ITEMS.map((item) => item.value);
|
|
8
53
|
/**
|
|
9
54
|
* Post-probe role assignment: after /fitness measures a model, this selector turns the result
|
|
10
|
-
* into configuration in one step instead of sending the user to hand-edit settings JSON.
|
|
55
|
+
* into configuration in one step instead of sending the user to hand-edit settings JSON. When a
|
|
56
|
+
* model arrives from a validated suggestion, `preselectRole` lands the shaped role already
|
|
57
|
+
* highlighted so a single keypress confirms it (an unknown/absent role falls back to the default).
|
|
11
58
|
*/
|
|
12
59
|
export class FitnessRoleSelectorComponent extends Container {
|
|
13
60
|
selectList;
|
|
14
|
-
constructor(modelRef, onSelect, onCancel) {
|
|
61
|
+
constructor(modelRef, onSelect, onCancel, preselectRole) {
|
|
15
62
|
super();
|
|
16
|
-
const items = [
|
|
17
|
-
{
|
|
18
|
-
value: "curator",
|
|
19
|
-
label: "Context curator",
|
|
20
|
-
description: "Local brain: digests GC-packed stubs, scores stale-chunk relevance (enables curation)",
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
value: "executor",
|
|
24
|
-
label: "Toolkit executor",
|
|
25
|
-
description: "Owns direct toolkit commands end-to-end (Level-0 exact hits route here; needs tool-call fitness)",
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
value: "router-cheap",
|
|
29
|
-
label: "Router: cheap tier",
|
|
30
|
-
description: "Handles trivial/read-only routed turns (model router settings)",
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
value: "router-medium",
|
|
34
|
-
label: "Router: medium tier",
|
|
35
|
-
description: "Handles normal implementation turns (model router settings)",
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
value: "router-expensive",
|
|
39
|
-
label: "Router: expensive tier",
|
|
40
|
-
description: "Handles high-impact/architecture turns (model router settings)",
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
value: "judge",
|
|
44
|
-
label: "Routing judge",
|
|
45
|
-
description: "Routing-only tier verdicts; needs strong instruction following",
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
value: "learning",
|
|
49
|
-
label: "Learning / reflection",
|
|
50
|
-
description: "Background reflection and learning passes",
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
value: "none",
|
|
54
|
-
label: "No role for now",
|
|
55
|
-
description: "Keep the result in the fitness store only (assign later from /settings)",
|
|
56
|
-
},
|
|
57
|
-
];
|
|
58
63
|
this.addChild(new DynamicBorder());
|
|
59
64
|
this.addChild(new Text(`Assign a role for ${modelRef}:`, 1, 0));
|
|
60
|
-
this.selectList = new SelectList(
|
|
65
|
+
this.selectList = new SelectList([...FITNESS_ROLE_ITEMS], 8, getSelectListTheme(), FITNESS_ROLE_SELECT_LIST_LAYOUT);
|
|
61
66
|
this.selectList.onSelect = (item) => onSelect(item.value);
|
|
62
67
|
this.selectList.onCancel = () => onCancel();
|
|
68
|
+
if (preselectRole) {
|
|
69
|
+
const index = FITNESS_ROLE_ORDER.indexOf(preselectRole);
|
|
70
|
+
if (index >= 0)
|
|
71
|
+
this.selectList.setSelectedIndex(index);
|
|
72
|
+
}
|
|
63
73
|
this.addChild(this.selectList);
|
|
64
74
|
this.addChild(new DynamicBorder());
|
|
65
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fitness-role-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/fitness-role-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAgC,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,+BAA+B,GAA4B;IAChE,qBAAqB,EAAE,EAAE;IACzB,qBAAqB,EAAE,EAAE;CACzB,CAAC;AAaF
|
|
1
|
+
{"version":3,"file":"fitness-role-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/fitness-role-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAgC,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,+BAA+B,GAA4B;IAChE,qBAAqB,EAAE,EAAE;IACzB,qBAAqB,EAAE,EAAE;CACzB,CAAC;AAaF,qGAAmG;AACnG,MAAM,kBAAkB,GAA0B;IACjD;QACC,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,uFAAuF;KACpG;IACD;QACC,KAAK,EAAE,UAAU;QACjB,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,kGAAkG;KAC/G;IACD;QACC,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,gEAAgE;KAC7E;IACD;QACC,KAAK,EAAE,eAAe;QACtB,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,6DAA6D;KAC1E;IACD;QACC,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,wBAAwB;QAC/B,WAAW,EAAE,gEAAgE;KAC7E;IACD;QACC,KAAK,EAAE,OAAO;QACd,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,gEAAgE;KAC7E;IACD;QACC,KAAK,EAAE,UAAU;QACjB,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EAAE,2CAA2C;KACxD;IACD;QACC,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,yEAAyE;KACtF;CACD,CAAC;AAEF,qGAAqG;AACrG,MAAM,CAAC,MAAM,kBAAkB,GAA2B,kBAAkB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAoB,CAAC,CAAC;AAEtH;;;;;GAKG;AACH,MAAM,OAAO,4BAA6B,SAAQ,SAAS;IAClD,UAAU,CAAa;IAE/B,YACC,QAAgB,EAChB,QAAqC,EACrC,QAAoB,EACpB,aAA2B,EAC1B;QACD,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,qBAAqB,QAAQ,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAC/B,CAAC,GAAG,kBAAkB,CAAC,EACvB,CAAC,EACD,kBAAkB,EAAE,EACpB,+BAA+B,CAC/B,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAoB,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC5C,IAAI,aAAa,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YACxD,IAAI,KAAK,IAAI,CAAC;gBAAE,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;IAAA,CACnC;IAED,WAAW,CAAC,IAAY,EAAQ;QAC/B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAAA,CAClC;CACD","sourcesContent":["import { Container, type SelectItem, SelectList, type SelectListLayoutOptions, Text } from \"@caupulican/pi-tui\";\nimport { getSelectListTheme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nconst FITNESS_ROLE_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 18,\n\tmaxPrimaryColumnWidth: 34,\n};\n\n/** Roles a freshly probed model can be assigned to, mapped to their settings by the caller. */\nexport type FitnessRole =\n\t| \"curator\"\n\t| \"executor\"\n\t| \"router-cheap\"\n\t| \"router-medium\"\n\t| \"router-expensive\"\n\t| \"judge\"\n\t| \"learning\"\n\t| \"none\";\n\n/** The role options in presentation order — one source of truth for the list AND pre-selection. */\nconst FITNESS_ROLE_ITEMS: readonly SelectItem[] = [\n\t{\n\t\tvalue: \"curator\",\n\t\tlabel: \"Context curator\",\n\t\tdescription: \"Local brain: digests GC-packed stubs, scores stale-chunk relevance (enables curation)\",\n\t},\n\t{\n\t\tvalue: \"executor\",\n\t\tlabel: \"Toolkit executor\",\n\t\tdescription: \"Owns direct toolkit commands end-to-end (Level-0 exact hits route here; needs tool-call fitness)\",\n\t},\n\t{\n\t\tvalue: \"router-cheap\",\n\t\tlabel: \"Router: cheap tier\",\n\t\tdescription: \"Handles trivial/read-only routed turns (model router settings)\",\n\t},\n\t{\n\t\tvalue: \"router-medium\",\n\t\tlabel: \"Router: medium tier\",\n\t\tdescription: \"Handles normal implementation turns (model router settings)\",\n\t},\n\t{\n\t\tvalue: \"router-expensive\",\n\t\tlabel: \"Router: expensive tier\",\n\t\tdescription: \"Handles high-impact/architecture turns (model router settings)\",\n\t},\n\t{\n\t\tvalue: \"judge\",\n\t\tlabel: \"Routing judge\",\n\t\tdescription: \"Routing-only tier verdicts; needs strong instruction following\",\n\t},\n\t{\n\t\tvalue: \"learning\",\n\t\tlabel: \"Learning / reflection\",\n\t\tdescription: \"Background reflection and learning passes\",\n\t},\n\t{\n\t\tvalue: \"none\",\n\t\tlabel: \"No role for now\",\n\t\tdescription: \"Keep the result in the fitness store only (assign later from /settings)\",\n\t},\n];\n\n/** The assignable roles in the order the selector presents them (index 0 is the default landing). */\nexport const FITNESS_ROLE_ORDER: readonly FitnessRole[] = FITNESS_ROLE_ITEMS.map((item) => item.value as FitnessRole);\n\n/**\n * Post-probe role assignment: after /fitness measures a model, this selector turns the result\n * into configuration in one step instead of sending the user to hand-edit settings JSON. When a\n * model arrives from a validated suggestion, `preselectRole` lands the shaped role already\n * highlighted so a single keypress confirms it (an unknown/absent role falls back to the default).\n */\nexport class FitnessRoleSelectorComponent extends Container {\n\tprivate selectList: SelectList;\n\n\tconstructor(\n\t\tmodelRef: string,\n\t\tonSelect: (role: FitnessRole) => void,\n\t\tonCancel: () => void,\n\t\tpreselectRole?: FitnessRole,\n\t) {\n\t\tsuper();\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(`Assign a role for ${modelRef}:`, 1, 0));\n\t\tthis.selectList = new SelectList(\n\t\t\t[...FITNESS_ROLE_ITEMS],\n\t\t\t8,\n\t\t\tgetSelectListTheme(),\n\t\t\tFITNESS_ROLE_SELECT_LIST_LAYOUT,\n\t\t);\n\t\tthis.selectList.onSelect = (item) => onSelect(item.value as FitnessRole);\n\t\tthis.selectList.onCancel = () => onCancel();\n\t\tif (preselectRole) {\n\t\t\tconst index = FITNESS_ROLE_ORDER.indexOf(preselectRole);\n\t\t\tif (index >= 0) this.selectList.setSelectedIndex(index);\n\t\t}\n\t\tthis.addChild(this.selectList);\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.selectList.handleInput(data);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Container } from "@caupulican/pi-tui";
|
|
2
|
+
import type { ModelSuggestion } from "../../../core/models/default-model-suggestions.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Picker over the validated local-model roster: the user chooses a suggestion instead of retyping
|
|
5
|
+
* a ref, and the caller then installs it, probes it on THIS host, and lands its shaped role
|
|
6
|
+
* pre-selected. Non-tool-callers are marked so the executor role reads as the footgun it is.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ModelSuggestionSelectorComponent extends Container {
|
|
9
|
+
private selectList;
|
|
10
|
+
constructor(suggestions: readonly ModelSuggestion[], onSelect: (suggestion: ModelSuggestion) => void, onCancel: () => void);
|
|
11
|
+
handleInput(data: string): void;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=model-suggestion-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-suggestion-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-suggestion-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmE,MAAM,oBAAoB,CAAC;AAChH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AASzF;;;;GAIG;AACH,qBAAa,gCAAiC,SAAQ,SAAS;IAC9D,OAAO,CAAC,UAAU,CAAa;IAE/B,YACC,WAAW,EAAE,SAAS,eAAe,EAAE,EACvC,QAAQ,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,EAC/C,QAAQ,EAAE,MAAM,IAAI,EAsBpB;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE9B;CACD","sourcesContent":["import { Container, type SelectItem, SelectList, type SelectListLayoutOptions, Text } from \"@caupulican/pi-tui\";\nimport type { ModelSuggestion } from \"../../../core/models/default-model-suggestions.ts\";\nimport { getSelectListTheme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nconst MODEL_SUGGESTION_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 22,\n\tmaxPrimaryColumnWidth: 40,\n};\n\n/**\n * Picker over the validated local-model roster: the user chooses a suggestion instead of retyping\n * a ref, and the caller then installs it, probes it on THIS host, and lands its shaped role\n * pre-selected. Non-tool-callers are marked so the executor role reads as the footgun it is.\n */\nexport class ModelSuggestionSelectorComponent extends Container {\n\tprivate selectList: SelectList;\n\n\tconstructor(\n\t\tsuggestions: readonly ModelSuggestion[],\n\t\tonSelect: (suggestion: ModelSuggestion) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst items: SelectItem[] = suggestions.map((suggestion, index) => ({\n\t\t\tvalue: String(index),\n\t\t\tlabel: `${suggestion.name}${suggestion.toolCalling ? \"\" : \" [no tool-calling]\"}`,\n\t\t\t// Surface the note (quant/RAM caveats) here so a caveat is seen BEFORE the pull, not as a\n\t\t\t// confusing failure after picking.\n\t\t\tdescription: `${suggestion.role} — ${suggestion.rationale}${suggestion.note ? ` (note: ${suggestion.note})` : \"\"}`,\n\t\t}));\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(\"Pick a validated local model to install and set up (probed on your hardware):\", 1, 0));\n\t\tthis.selectList = new SelectList(items, 8, getSelectListTheme(), MODEL_SUGGESTION_SELECT_LIST_LAYOUT);\n\t\tthis.selectList.onSelect = (item) => {\n\t\t\tconst suggestion = suggestions[Number(item.value)];\n\t\t\tif (suggestion) onSelect(suggestion);\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\thandleInput(data: string): void {\n\t\tthis.selectList.handleInput(data);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Container, SelectList, Text } from "@caupulican/pi-tui";
|
|
2
|
+
import { getSelectListTheme } from "../theme/theme.js";
|
|
3
|
+
import { DynamicBorder } from "./dynamic-border.js";
|
|
4
|
+
const MODEL_SUGGESTION_SELECT_LIST_LAYOUT = {
|
|
5
|
+
minPrimaryColumnWidth: 22,
|
|
6
|
+
maxPrimaryColumnWidth: 40,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Picker over the validated local-model roster: the user chooses a suggestion instead of retyping
|
|
10
|
+
* a ref, and the caller then installs it, probes it on THIS host, and lands its shaped role
|
|
11
|
+
* pre-selected. Non-tool-callers are marked so the executor role reads as the footgun it is.
|
|
12
|
+
*/
|
|
13
|
+
export class ModelSuggestionSelectorComponent extends Container {
|
|
14
|
+
selectList;
|
|
15
|
+
constructor(suggestions, onSelect, onCancel) {
|
|
16
|
+
super();
|
|
17
|
+
const items = suggestions.map((suggestion, index) => ({
|
|
18
|
+
value: String(index),
|
|
19
|
+
label: `${suggestion.name}${suggestion.toolCalling ? "" : " [no tool-calling]"}`,
|
|
20
|
+
// Surface the note (quant/RAM caveats) here so a caveat is seen BEFORE the pull, not as a
|
|
21
|
+
// confusing failure after picking.
|
|
22
|
+
description: `${suggestion.role} — ${suggestion.rationale}${suggestion.note ? ` (note: ${suggestion.note})` : ""}`,
|
|
23
|
+
}));
|
|
24
|
+
this.addChild(new DynamicBorder());
|
|
25
|
+
this.addChild(new Text("Pick a validated local model to install and set up (probed on your hardware):", 1, 0));
|
|
26
|
+
this.selectList = new SelectList(items, 8, getSelectListTheme(), MODEL_SUGGESTION_SELECT_LIST_LAYOUT);
|
|
27
|
+
this.selectList.onSelect = (item) => {
|
|
28
|
+
const suggestion = suggestions[Number(item.value)];
|
|
29
|
+
if (suggestion)
|
|
30
|
+
onSelect(suggestion);
|
|
31
|
+
};
|
|
32
|
+
this.selectList.onCancel = () => onCancel();
|
|
33
|
+
this.addChild(this.selectList);
|
|
34
|
+
this.addChild(new DynamicBorder());
|
|
35
|
+
}
|
|
36
|
+
handleInput(data) {
|
|
37
|
+
this.selectList.handleInput(data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=model-suggestion-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-suggestion-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-suggestion-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,UAAU,EAAgC,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAEhH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,mCAAmC,GAA4B;IACpE,qBAAqB,EAAE,EAAE;IACzB,qBAAqB,EAAE,EAAE;CACzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,gCAAiC,SAAQ,SAAS;IACtD,UAAU,CAAa;IAE/B,YACC,WAAuC,EACvC,QAA+C,EAC/C,QAAoB,EACnB;QACD,KAAK,EAAE,CAAC;QAER,MAAM,KAAK,GAAiB,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACnE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;YACpB,KAAK,EAAE,GAAG,UAAU,CAAC,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,EAAE;YACjF,0FAA0F;YAC1F,mCAAmC;YACnC,WAAW,EAAE,GAAG,UAAU,CAAC,IAAI,QAAM,UAAU,CAAC,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;SACnH,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,+EAA+E,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/G,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,kBAAkB,EAAE,EAAE,mCAAmC,CAAC,CAAC;QACtG,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACnD,IAAI,UAAU;gBAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAAA,CACrC,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;IAAA,CACnC;IAED,WAAW,CAAC,IAAY,EAAQ;QAC/B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAAA,CAClC;CACD","sourcesContent":["import { Container, type SelectItem, SelectList, type SelectListLayoutOptions, Text } from \"@caupulican/pi-tui\";\nimport type { ModelSuggestion } from \"../../../core/models/default-model-suggestions.ts\";\nimport { getSelectListTheme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nconst MODEL_SUGGESTION_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 22,\n\tmaxPrimaryColumnWidth: 40,\n};\n\n/**\n * Picker over the validated local-model roster: the user chooses a suggestion instead of retyping\n * a ref, and the caller then installs it, probes it on THIS host, and lands its shaped role\n * pre-selected. Non-tool-callers are marked so the executor role reads as the footgun it is.\n */\nexport class ModelSuggestionSelectorComponent extends Container {\n\tprivate selectList: SelectList;\n\n\tconstructor(\n\t\tsuggestions: readonly ModelSuggestion[],\n\t\tonSelect: (suggestion: ModelSuggestion) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst items: SelectItem[] = suggestions.map((suggestion, index) => ({\n\t\t\tvalue: String(index),\n\t\t\tlabel: `${suggestion.name}${suggestion.toolCalling ? \"\" : \" [no tool-calling]\"}`,\n\t\t\t// Surface the note (quant/RAM caveats) here so a caveat is seen BEFORE the pull, not as a\n\t\t\t// confusing failure after picking.\n\t\t\tdescription: `${suggestion.role} — ${suggestion.rationale}${suggestion.note ? ` (note: ${suggestion.note})` : \"\"}`,\n\t\t}));\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(\"Pick a validated local model to install and set up (probed on your hardware):\", 1, 0));\n\t\tthis.selectList = new SelectList(items, 8, getSelectListTheme(), MODEL_SUGGESTION_SELECT_LIST_LAYOUT);\n\t\tthis.selectList.onSelect = (item) => {\n\t\t\tconst suggestion = suggestions[Number(item.value)];\n\t\t\tif (suggestion) onSelect(suggestion);\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\thandleInput(data: string): void {\n\t\tthis.selectList.handleInput(data);\n\t}\n}\n"]}
|
|
@@ -32,6 +32,9 @@ export declare class ProfileResourceEditorComponent extends Container implements
|
|
|
32
32
|
private enabledByKind;
|
|
33
33
|
private framingByKind;
|
|
34
34
|
private missingIdsByKind;
|
|
35
|
+
/** The filter each kind was decoded from, so an all-enabled save preserves an enumerated grant
|
|
36
|
+
* (or an already-wildcard grant) instead of widening it to allow:["*"]. */
|
|
37
|
+
private originalFilterByKind;
|
|
35
38
|
private currentKindIndex;
|
|
36
39
|
private cwd;
|
|
37
40
|
private agentDir;
|
|
@@ -1 +1 @@
|
|
|
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\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\n\t\t\t// Decode against the DISCOVERABLE universe plus every id the profile itself mentions:\n\t\t\t// decoding only against the discoverable set silently drops grants for anything the\n\t\t\t// current environment cannot see (offline external root, or a collapsed universe),\n\t\t\t// and the next save would then corrupt the profile.\n\t\t\tconst enabledSet = decodeResourceSelection(filter, [...allIds, ...missingSet]);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\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\t`${theme.fg(\"accent\", \"Ctrl+Q\")}/${keyText(\"app.interrupt\")} cancel`,\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 / Ctrl+Q - cancel (unconditional)\n\t\tif (\n\t\t\tkb.matches(data, \"app.interrupt\") ||\n\t\t\tmatchesKey(data, Key.escape) ||\n\t\t\tmatchesKey(data, Key.esc) ||\n\t\t\tmatchesKey(data, \"ctrl+q\")\n\t\t) {\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\t// Persist against the same widened universe used at decode time: profile-referenced\n\t\t\t// ids that are not currently discoverable must survive a save untouched.\n\t\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) ?? new Set<string>();\n\t\t\tconst allIds = [...new Set([...kind.items.map((item) => item.id), ...missingSet])];\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 {\n\tResourceProfileFilterSettings,\n\tResourceProfileKind,\n\tResourceProfileSettings,\n} 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\t/** The filter each kind was decoded from, so an all-enabled save preserves an enumerated grant\n\t * (or an already-wildcard grant) instead of widening it to allow:[\"*\"]. */\n\tprivate originalFilterByKind: Map<ResourceProfileKind, ResourceProfileFilterSettings | undefined> = 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\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\t// \"*\" is a grant-all framing marker, not a resource id — the block loop below\n\t\t\t\t\t\t// already excludes it; the allow loop must too, or it leaks into missingSet as\n\t\t\t\t\t\t// a fake \"missing item\" and rides along as a phantom enabled member through\n\t\t\t\t\t\t// decode/encode.\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\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\n\t\t\t// Decode against the DISCOVERABLE universe plus every id the profile itself mentions:\n\t\t\t// decoding only against the discoverable set silently drops grants for anything the\n\t\t\t// current environment cannot see (offline external root, or a collapsed universe),\n\t\t\t// and the next save would then corrupt the profile.\n\t\t\tconst enabledSet = decodeResourceSelection(filter, [...allIds, ...missingSet]);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\t\t\tthis.originalFilterByKind.set(kind.kind, filter);\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\t`${theme.fg(\"accent\", \"Ctrl+Q\")}/${keyText(\"app.interrupt\")} cancel`,\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 / Ctrl+Q - cancel (unconditional)\n\t\tif (\n\t\t\tkb.matches(data, \"app.interrupt\") ||\n\t\t\tmatchesKey(data, Key.escape) ||\n\t\t\tmatchesKey(data, Key.esc) ||\n\t\t\tmatchesKey(data, \"ctrl+q\")\n\t\t) {\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\t// Persist against the same widened universe used at decode time: profile-referenced\n\t\t\t// ids that are not currently discoverable must survive a save untouched.\n\t\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) ?? new Set<string>();\n\t\t\tconst allIds = [...new Set([...kind.items.map((item) => item.id), ...missingSet])];\n\t\t\t// Pass the original filter so an all-enabled save preserves an enumerated grant (and only\n\t\t\t// keeps a wildcard when the grant was already a wildcard) — never widening it silently.\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing, {\n\t\t\t\toriginalFilter: this.originalFilterByKind.get(kind.kind),\n\t\t\t});\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"]}
|
|
@@ -103,6 +103,9 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
103
103
|
enabledByKind = new Map();
|
|
104
104
|
framingByKind = new Map();
|
|
105
105
|
missingIdsByKind = new Map();
|
|
106
|
+
/** The filter each kind was decoded from, so an all-enabled save preserves an enumerated grant
|
|
107
|
+
* (or an already-wildcard grant) instead of widening it to allow:["*"]. */
|
|
108
|
+
originalFilterByKind = new Map();
|
|
106
109
|
currentKindIndex = 0;
|
|
107
110
|
cwd;
|
|
108
111
|
agentDir;
|
|
@@ -149,7 +152,13 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
149
152
|
if (filter) {
|
|
150
153
|
if (filter.allow) {
|
|
151
154
|
for (const id of filter.allow) {
|
|
152
|
-
|
|
155
|
+
// "*" is a grant-all framing marker, not a resource id — the block loop below
|
|
156
|
+
// already excludes it; the allow loop must too, or it leaks into missingSet as
|
|
157
|
+
// a fake "missing item" and rides along as a phantom enabled member through
|
|
158
|
+
// decode/encode.
|
|
159
|
+
if (id !== "*") {
|
|
160
|
+
mentionedIds.add(id);
|
|
161
|
+
}
|
|
153
162
|
}
|
|
154
163
|
}
|
|
155
164
|
if (filter.block) {
|
|
@@ -175,6 +184,7 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
175
184
|
const enabledSet = decodeResourceSelection(filter, [...allIds, ...missingSet]);
|
|
176
185
|
this.enabledByKind.set(kind.kind, enabledSet);
|
|
177
186
|
this.framingByKind.set(kind.kind, detectResourceFraming(filter));
|
|
187
|
+
this.originalFilterByKind.set(kind.kind, filter);
|
|
178
188
|
}
|
|
179
189
|
// Header
|
|
180
190
|
this.addChild(new DynamicBorder());
|
|
@@ -457,7 +467,11 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
457
467
|
// ids that are not currently discoverable must survive a save untouched.
|
|
458
468
|
const missingSet = this.missingIdsByKind.get(kind.kind) ?? new Set();
|
|
459
469
|
const allIds = [...new Set([...kind.items.map((item) => item.id), ...missingSet])];
|
|
460
|
-
|
|
470
|
+
// Pass the original filter so an all-enabled save preserves an enumerated grant (and only
|
|
471
|
+
// keeps a wildcard when the grant was already a wildcard) — never widening it silently.
|
|
472
|
+
const encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing, {
|
|
473
|
+
originalFilter: this.originalFilterByKind.get(kind.kind),
|
|
474
|
+
});
|
|
461
475
|
if (encoded !== undefined) {
|
|
462
476
|
resources[kind.kind] = encoded;
|
|
463
477
|
}
|
|
@@ -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 * 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\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\n\t\t\t// Decode against the DISCOVERABLE universe plus every id the profile itself mentions:\n\t\t\t// decoding only against the discoverable set silently drops grants for anything the\n\t\t\t// current environment cannot see (offline external root, or a collapsed universe),\n\t\t\t// and the next save would then corrupt the profile.\n\t\t\tconst enabledSet = decodeResourceSelection(filter, [...allIds, ...missingSet]);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\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\t`${theme.fg(\"accent\", \"Ctrl+Q\")}/${keyText(\"app.interrupt\")} cancel`,\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 / Ctrl+Q - cancel (unconditional)\n\t\tif (\n\t\t\tkb.matches(data, \"app.interrupt\") ||\n\t\t\tmatchesKey(data, Key.escape) ||\n\t\t\tmatchesKey(data, Key.esc) ||\n\t\t\tmatchesKey(data, \"ctrl+q\")\n\t\t) {\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\t// Persist against the same widened universe used at decode time: profile-referenced\n\t\t\t// ids that are not currently discoverable must survive a save untouched.\n\t\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) ?? new Set<string>();\n\t\t\tconst allIds = [...new Set([...kind.items.map((item) => item.id), ...missingSet])];\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 {\n\tResourceProfileFilterSettings,\n\tResourceProfileKind,\n\tResourceProfileSettings,\n} 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\t/** The filter each kind was decoded from, so an all-enabled save preserves an enumerated grant\n\t * (or an already-wildcard grant) instead of widening it to allow:[\"*\"]. */\n\tprivate originalFilterByKind: Map<ResourceProfileKind, ResourceProfileFilterSettings | undefined> = 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\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\t// \"*\" is a grant-all framing marker, not a resource id — the block loop below\n\t\t\t\t\t\t// already excludes it; the allow loop must too, or it leaks into missingSet as\n\t\t\t\t\t\t// a fake \"missing item\" and rides along as a phantom enabled member through\n\t\t\t\t\t\t// decode/encode.\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\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\n\t\t\t// Decode against the DISCOVERABLE universe plus every id the profile itself mentions:\n\t\t\t// decoding only against the discoverable set silently drops grants for anything the\n\t\t\t// current environment cannot see (offline external root, or a collapsed universe),\n\t\t\t// and the next save would then corrupt the profile.\n\t\t\tconst enabledSet = decodeResourceSelection(filter, [...allIds, ...missingSet]);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\t\t\tthis.originalFilterByKind.set(kind.kind, filter);\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\t`${theme.fg(\"accent\", \"Ctrl+Q\")}/${keyText(\"app.interrupt\")} cancel`,\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 / Ctrl+Q - cancel (unconditional)\n\t\tif (\n\t\t\tkb.matches(data, \"app.interrupt\") ||\n\t\t\tmatchesKey(data, Key.escape) ||\n\t\t\tmatchesKey(data, Key.esc) ||\n\t\t\tmatchesKey(data, \"ctrl+q\")\n\t\t) {\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\t// Persist against the same widened universe used at decode time: profile-referenced\n\t\t\t// ids that are not currently discoverable must survive a save untouched.\n\t\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) ?? new Set<string>();\n\t\t\tconst allIds = [...new Set([...kind.items.map((item) => item.id), ...missingSet])];\n\t\t\t// Pass the original filter so an all-enabled save preserves an enumerated grant (and only\n\t\t\t// keeps a wildcard when the grant was already a wildcard) — never widening it silently.\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing, {\n\t\t\t\toriginalFilter: this.originalFilterByKind.get(kind.kind),\n\t\t\t});\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"]}
|