@mrclrchtr/supi-lsp 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/README.md +9 -8
  2. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -3
  4. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -5
  5. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/scoped-settings-list.ts +372 -0
  6. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-action-menu.ts +101 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +13 -7
  8. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +464 -0
  9. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-submenus.ts +124 -0
  10. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +50 -309
  11. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +2 -5
  12. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings.ts +17 -1
  13. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
  14. package/node_modules/@mrclrchtr/supi-core/README.md +9 -8
  15. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  16. package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -3
  17. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -5
  18. package/node_modules/@mrclrchtr/supi-core/src/settings/scoped-settings-list.ts +372 -0
  19. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-action-menu.ts +101 -0
  20. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +13 -7
  21. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +464 -0
  22. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-submenus.ts +124 -0
  23. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +50 -309
  24. package/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +2 -5
  25. package/node_modules/@mrclrchtr/supi-core/src/settings.ts +17 -1
  26. package/package.json +3 -3
  27. package/src/client/client.ts +49 -12
  28. package/src/config/lsp-settings.ts +1 -1
  29. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +0 -207
  30. package/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +0 -207
@@ -1,207 +0,0 @@
1
- // Config-aware settings contribution helper for SuPi packages.
2
- //
3
- // Registers config-backed settings sections through PI's shared event bus so
4
- // /supi-settings can collect contributions from all loaded extensions without
5
- // relying on a shared supi-core module instance.
6
-
7
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
8
- import type { SettingItem } from "@earendil-works/pi-tui";
9
- import {
10
- isSettingsContributionCollector,
11
- type SettingsScope,
12
- type SettingsSection,
13
- SUPI_SETTINGS_COLLECT_EVENT,
14
- } from "../settings/settings-registry.ts";
15
- import { loadSupiConfigForScope, removeSupiConfigKey, writeSupiConfig } from "./config.ts";
16
-
17
- // ── Types ──────────────────────────────────────────────────────────────────
18
-
19
- /**
20
- * Supported config value types for declarative persistChange.
21
- *
22
- * - `"boolean"`: maps "on" → true, "off" → false
23
- * - `"number"`: parses integer via Number.parseInt, falls back to unset on invalid
24
- * - `"stringList"`: splits on comma, trims whitespace, unsets on empty
25
- */
26
- export type ConfigSettingType = "boolean" | "number" | "stringList";
27
-
28
- /** Extended setting item that can declare its config type for persistence. */
29
- export interface ConfigSettingItem extends SettingItem {
30
- /** Config value type used for auto-generated persistence. */
31
- configType?: ConfigSettingType;
32
- }
33
-
34
- /** Helpers provided to persistChange for scoped SuPi config writes. */
35
- export interface ConfigSettingsHelpers {
36
- /** Write a key to the selected scope's config section. */
37
- set(key: string, value: unknown): void;
38
- /** Remove a key from the selected scope's config section. */
39
- unset(key: string): void;
40
- }
41
-
42
- export interface ConfigSettingsPersistedChange {
43
- scope: SettingsScope;
44
- cwd: string;
45
- settingId: string;
46
- value: string;
47
- }
48
-
49
- export interface ConfigSettingsOptions<T> {
50
- /** Settings contribution identifier — e.g. "lsp", "claude-md". */
51
- id: string;
52
- /** Human-readable label shown in the UI. */
53
- label: string;
54
- /** SuPi config section name — e.g. "lsp", "claude-md". */
55
- section: string;
56
- /** Default config values. */
57
- defaults: T;
58
- /** Build SettingItem[] from scoped config. */
59
- buildItems: (
60
- settings: T,
61
- scope: SettingsScope,
62
- cwd: string,
63
- ctx?: ExtensionContext,
64
- ) => ConfigSettingItem[];
65
- /**
66
- * Convert a UI value into scoped SuPi config writes.
67
- *
68
- * Optional when every item returned by `buildItems` declares `configType`.
69
- * Required when any item lacks `configType`.
70
- */
71
- persistChange?: (
72
- scope: SettingsScope,
73
- cwd: string,
74
- settingId: string,
75
- value: string,
76
- helpers: ConfigSettingsHelpers,
77
- ) => void;
78
- /** Optional live runtime sync after successful persistence. */
79
- afterPersist?: (change: ConfigSettingsPersistedChange) => void;
80
- /** Optional home directory for config resolution (testing). */
81
- homeDir?: string;
82
- }
83
-
84
- // ── Auto-generated persistChange ───────────────────────────────────────────
85
-
86
- function autoPersistChange(
87
- settingId: string,
88
- value: string,
89
- helpers: ConfigSettingsHelpers,
90
- items: ConfigSettingItem[],
91
- ): void {
92
- const item = items.find((i) => i.id === settingId);
93
- if (!item?.configType) return;
94
-
95
- switch (item.configType) {
96
- case "boolean": {
97
- helpers.set(settingId, value === "on");
98
- break;
99
- }
100
- case "number": {
101
- const num = Number.parseInt(value, 10);
102
- if (Number.isFinite(num) && num > 0) {
103
- helpers.set(settingId, num);
104
- } else {
105
- helpers.unset(settingId);
106
- }
107
- break;
108
- }
109
- case "stringList": {
110
- const names = value
111
- .split(",")
112
- .map((s) => s.trim())
113
- .filter((s) => s.length > 0);
114
- if (names.length > 0) {
115
- helpers.set(settingId, names);
116
- } else {
117
- helpers.unset(settingId);
118
- }
119
- break;
120
- }
121
- }
122
- }
123
-
124
- function areAllItemsDeclarative(items: ConfigSettingItem[]): boolean {
125
- return items.length > 0 && items.every((i) => i.configType !== undefined);
126
- }
127
-
128
- function createHelpers<T>(options: ConfigSettingsOptions<T>, scope: SettingsScope, cwd: string) {
129
- return {
130
- set: (key: string, val: unknown) => {
131
- writeSupiConfig(
132
- { section: options.section, scope, cwd },
133
- { [key]: val },
134
- { homeDir: options.homeDir },
135
- );
136
- },
137
- unset: (key: string) => {
138
- removeSupiConfigKey({ section: options.section, scope, cwd }, key, {
139
- homeDir: options.homeDir,
140
- });
141
- },
142
- } satisfies ConfigSettingsHelpers;
143
- }
144
-
145
- function toSettingsSection<T>(options: ConfigSettingsOptions<T>): SettingsSection {
146
- let cachedItems: ConfigSettingItem[] | undefined;
147
-
148
- return {
149
- id: options.id,
150
- label: options.label,
151
- loadValues: (scope, cwd, ctx) => {
152
- const settings = loadSupiConfigForScope(options.section, cwd, options.defaults, {
153
- scope,
154
- homeDir: options.homeDir,
155
- });
156
- const items = options.buildItems(settings, scope, cwd, ctx);
157
- cachedItems = items;
158
- return items;
159
- },
160
- persistChange: (scope, cwd, settingId, value) => {
161
- const helpers = createHelpers(options, scope, cwd);
162
-
163
- if (options.persistChange) {
164
- options.persistChange(scope, cwd, settingId, value, helpers);
165
- } else {
166
- const items = cachedItems ?? options.buildItems(options.defaults, scope, cwd, undefined);
167
- if (!areAllItemsDeclarative(items)) {
168
- throw new Error(
169
- `Settings contribution "${options.id}" needs persistChange or configType on every item.`,
170
- );
171
- }
172
- autoPersistChange(settingId, value, helpers, items);
173
- }
174
-
175
- try {
176
- options.afterPersist?.({ scope, cwd, settingId, value });
177
- } catch (error) {
178
- const message = error instanceof Error ? error.message : String(error);
179
- throw new Error(`Saved setting, but live sync failed: ${message}`, { cause: error });
180
- }
181
- },
182
- };
183
- }
184
-
185
- // ── Registration ───────────────────────────────────────────────────────────
186
-
187
- /**
188
- * Register a config-backed settings contribution for `/supi-settings`.
189
- *
190
- * Contributions are collected through PI's process-local event bus. Call this
191
- * during the extension factory function, not in async session handlers.
192
- */
193
- export function registerConfigSettings<T>(
194
- pi: ExtensionAPI,
195
- options: ConfigSettingsOptions<T>,
196
- ): void {
197
- const section = toSettingsSection(options);
198
- const dispose = pi.events.on(SUPI_SETTINGS_COLLECT_EVENT, (collector) => {
199
- if (isSettingsContributionCollector(collector)) {
200
- collector.add(section);
201
- }
202
- });
203
-
204
- pi.on("session_shutdown", () => {
205
- dispose();
206
- });
207
- }
@@ -1,207 +0,0 @@
1
- // Config-aware settings contribution helper for SuPi packages.
2
- //
3
- // Registers config-backed settings sections through PI's shared event bus so
4
- // /supi-settings can collect contributions from all loaded extensions without
5
- // relying on a shared supi-core module instance.
6
-
7
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
8
- import type { SettingItem } from "@earendil-works/pi-tui";
9
- import {
10
- isSettingsContributionCollector,
11
- type SettingsScope,
12
- type SettingsSection,
13
- SUPI_SETTINGS_COLLECT_EVENT,
14
- } from "../settings/settings-registry.ts";
15
- import { loadSupiConfigForScope, removeSupiConfigKey, writeSupiConfig } from "./config.ts";
16
-
17
- // ── Types ──────────────────────────────────────────────────────────────────
18
-
19
- /**
20
- * Supported config value types for declarative persistChange.
21
- *
22
- * - `"boolean"`: maps "on" → true, "off" → false
23
- * - `"number"`: parses integer via Number.parseInt, falls back to unset on invalid
24
- * - `"stringList"`: splits on comma, trims whitespace, unsets on empty
25
- */
26
- export type ConfigSettingType = "boolean" | "number" | "stringList";
27
-
28
- /** Extended setting item that can declare its config type for persistence. */
29
- export interface ConfigSettingItem extends SettingItem {
30
- /** Config value type used for auto-generated persistence. */
31
- configType?: ConfigSettingType;
32
- }
33
-
34
- /** Helpers provided to persistChange for scoped SuPi config writes. */
35
- export interface ConfigSettingsHelpers {
36
- /** Write a key to the selected scope's config section. */
37
- set(key: string, value: unknown): void;
38
- /** Remove a key from the selected scope's config section. */
39
- unset(key: string): void;
40
- }
41
-
42
- export interface ConfigSettingsPersistedChange {
43
- scope: SettingsScope;
44
- cwd: string;
45
- settingId: string;
46
- value: string;
47
- }
48
-
49
- export interface ConfigSettingsOptions<T> {
50
- /** Settings contribution identifier — e.g. "lsp", "claude-md". */
51
- id: string;
52
- /** Human-readable label shown in the UI. */
53
- label: string;
54
- /** SuPi config section name — e.g. "lsp", "claude-md". */
55
- section: string;
56
- /** Default config values. */
57
- defaults: T;
58
- /** Build SettingItem[] from scoped config. */
59
- buildItems: (
60
- settings: T,
61
- scope: SettingsScope,
62
- cwd: string,
63
- ctx?: ExtensionContext,
64
- ) => ConfigSettingItem[];
65
- /**
66
- * Convert a UI value into scoped SuPi config writes.
67
- *
68
- * Optional when every item returned by `buildItems` declares `configType`.
69
- * Required when any item lacks `configType`.
70
- */
71
- persistChange?: (
72
- scope: SettingsScope,
73
- cwd: string,
74
- settingId: string,
75
- value: string,
76
- helpers: ConfigSettingsHelpers,
77
- ) => void;
78
- /** Optional live runtime sync after successful persistence. */
79
- afterPersist?: (change: ConfigSettingsPersistedChange) => void;
80
- /** Optional home directory for config resolution (testing). */
81
- homeDir?: string;
82
- }
83
-
84
- // ── Auto-generated persistChange ───────────────────────────────────────────
85
-
86
- function autoPersistChange(
87
- settingId: string,
88
- value: string,
89
- helpers: ConfigSettingsHelpers,
90
- items: ConfigSettingItem[],
91
- ): void {
92
- const item = items.find((i) => i.id === settingId);
93
- if (!item?.configType) return;
94
-
95
- switch (item.configType) {
96
- case "boolean": {
97
- helpers.set(settingId, value === "on");
98
- break;
99
- }
100
- case "number": {
101
- const num = Number.parseInt(value, 10);
102
- if (Number.isFinite(num) && num > 0) {
103
- helpers.set(settingId, num);
104
- } else {
105
- helpers.unset(settingId);
106
- }
107
- break;
108
- }
109
- case "stringList": {
110
- const names = value
111
- .split(",")
112
- .map((s) => s.trim())
113
- .filter((s) => s.length > 0);
114
- if (names.length > 0) {
115
- helpers.set(settingId, names);
116
- } else {
117
- helpers.unset(settingId);
118
- }
119
- break;
120
- }
121
- }
122
- }
123
-
124
- function areAllItemsDeclarative(items: ConfigSettingItem[]): boolean {
125
- return items.length > 0 && items.every((i) => i.configType !== undefined);
126
- }
127
-
128
- function createHelpers<T>(options: ConfigSettingsOptions<T>, scope: SettingsScope, cwd: string) {
129
- return {
130
- set: (key: string, val: unknown) => {
131
- writeSupiConfig(
132
- { section: options.section, scope, cwd },
133
- { [key]: val },
134
- { homeDir: options.homeDir },
135
- );
136
- },
137
- unset: (key: string) => {
138
- removeSupiConfigKey({ section: options.section, scope, cwd }, key, {
139
- homeDir: options.homeDir,
140
- });
141
- },
142
- } satisfies ConfigSettingsHelpers;
143
- }
144
-
145
- function toSettingsSection<T>(options: ConfigSettingsOptions<T>): SettingsSection {
146
- let cachedItems: ConfigSettingItem[] | undefined;
147
-
148
- return {
149
- id: options.id,
150
- label: options.label,
151
- loadValues: (scope, cwd, ctx) => {
152
- const settings = loadSupiConfigForScope(options.section, cwd, options.defaults, {
153
- scope,
154
- homeDir: options.homeDir,
155
- });
156
- const items = options.buildItems(settings, scope, cwd, ctx);
157
- cachedItems = items;
158
- return items;
159
- },
160
- persistChange: (scope, cwd, settingId, value) => {
161
- const helpers = createHelpers(options, scope, cwd);
162
-
163
- if (options.persistChange) {
164
- options.persistChange(scope, cwd, settingId, value, helpers);
165
- } else {
166
- const items = cachedItems ?? options.buildItems(options.defaults, scope, cwd, undefined);
167
- if (!areAllItemsDeclarative(items)) {
168
- throw new Error(
169
- `Settings contribution "${options.id}" needs persistChange or configType on every item.`,
170
- );
171
- }
172
- autoPersistChange(settingId, value, helpers, items);
173
- }
174
-
175
- try {
176
- options.afterPersist?.({ scope, cwd, settingId, value });
177
- } catch (error) {
178
- const message = error instanceof Error ? error.message : String(error);
179
- throw new Error(`Saved setting, but live sync failed: ${message}`, { cause: error });
180
- }
181
- },
182
- };
183
- }
184
-
185
- // ── Registration ───────────────────────────────────────────────────────────
186
-
187
- /**
188
- * Register a config-backed settings contribution for `/supi-settings`.
189
- *
190
- * Contributions are collected through PI's process-local event bus. Call this
191
- * during the extension factory function, not in async session handlers.
192
- */
193
- export function registerConfigSettings<T>(
194
- pi: ExtensionAPI,
195
- options: ConfigSettingsOptions<T>,
196
- ): void {
197
- const section = toSettingsSection(options);
198
- const dispose = pi.events.on(SUPI_SETTINGS_COLLECT_EVENT, (collector) => {
199
- if (isSettingsContributionCollector(collector)) {
200
- collector.add(section);
201
- }
202
- });
203
-
204
- pi.on("session_shutdown", () => {
205
- dispose();
206
- });
207
- }