@homer0/pi-config 0.0.0-stage → 1.0.0

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/src/types.ts ADDED
@@ -0,0 +1,240 @@
1
+ import type { z } from 'zod';
2
+
3
+ /** The fallback object shape for the config namespaces, before consumers augment them. */
4
+ export type ConfigNamespace = Record<string, unknown>;
5
+
6
+ /**
7
+ * The settings that affect Pi itself (`pi`). Consumers extend it through module
8
+ * augmentation.
9
+ *
10
+ * @example <caption>Typing a Pi setting</caption>
11
+ * declare module '@homer0/pi-config' {
12
+ * interface PiConfig {
13
+ * dummySetting?: boolean;
14
+ * }
15
+ * }
16
+ */
17
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- It's empty on purpose: consumers fill it through module augmentation.
18
+ export interface PiConfig {}
19
+
20
+ /**
21
+ * The settings of the skills (`skills.<name>`). Consumers extend it through module
22
+ * augmentation.
23
+ *
24
+ * @example <caption>Typing a skill config</caption>
25
+ * declare module '@homer0/pi-config' {
26
+ * interface SkillsConfig {
27
+ * commit?: { runChecks?: boolean };
28
+ * }
29
+ * }
30
+ */
31
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- It's empty on purpose: consumers fill it through module augmentation.
32
+ export interface SkillsConfig {}
33
+
34
+ /**
35
+ * The settings of the tools (`tools.<name>`). Consumers extend it through module
36
+ * augmentation.
37
+ *
38
+ * @example <caption>Typing a tool config</caption>
39
+ * declare module '@homer0/pi-config' {
40
+ * interface ToolsConfig {
41
+ * bash?: { timeout?: number };
42
+ * }
43
+ * }
44
+ */
45
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- It's empty on purpose: consumers fill it through module augmentation.
46
+ export interface ToolsConfig {}
47
+
48
+ /**
49
+ * The settings of the extensions (`extensions.<name>`). Consumers extend it through
50
+ * module augmentation.
51
+ *
52
+ * @example <caption>Typing an extension config</caption>
53
+ * declare module '@homer0/pi-config' {
54
+ * interface ExtensionsConfig {
55
+ * myExtension?: { enabled?: boolean };
56
+ * }
57
+ * }
58
+ */
59
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- It's empty on purpose: consumers fill it through module augmentation.
60
+ export interface ExtensionsConfig {}
61
+
62
+ /**
63
+ * The fully resolved config. Each namespace can be extended through module augmentation
64
+ * of its interface: {@link PiConfig}, {@link SkillsConfig}, {@link ToolsConfig}, and
65
+ * {@link ExtensionsConfig}.
66
+ */
67
+ export interface PiCoreConfig {
68
+ /** The settings that affect Pi itself. */
69
+ pi: PiConfig;
70
+ /** The settings of the skills, by skill name. */
71
+ skills: SkillsConfig;
72
+ /** The settings of the tools, by tool name. */
73
+ tools: ToolsConfig;
74
+ /** The settings of the extensions, by extension name. */
75
+ extensions: ExtensionsConfig;
76
+ }
77
+
78
+ /**
79
+ * Extracts the string keys of a type, to generate dot-paths.
80
+ *
81
+ * @template TValue The type to get the keys from.
82
+ */
83
+ type StringKey<TValue> = Extract<keyof TValue, string>;
84
+
85
+ /**
86
+ * Builds the valid dot-path strings for a nested object type.
87
+ *
88
+ * @template TValue The object type to build the paths for.
89
+ */
90
+ type DotPathFor<TValue> = TValue extends object
91
+ ? {
92
+ [TKey in StringKey<TValue>]: TValue[TKey] extends object
93
+ ? TKey | `${TKey}.${DotPathFor<TValue[TKey]>}`
94
+ : TKey;
95
+ }[StringKey<TValue>]
96
+ : never;
97
+
98
+ /** The typed config dot-paths, inferred from the declaration-merged config shape. */
99
+ export type ConfigPath = DotPathFor<PiCoreConfig>;
100
+
101
+ /**
102
+ * Resolves the type of the value at a dot-path, against the declaration-merged config
103
+ * shape. If the path can't be resolved, the type is `unknown`.
104
+ *
105
+ * @template TPath The dot-path to resolve, like `skills.commit.runChecks`.
106
+ * @template TValue The type to resolve the path against.
107
+ */
108
+ export type ConfigPathValue<
109
+ TPath extends string,
110
+ TValue = PiCoreConfig,
111
+ > = TPath extends `${infer THead}.${infer TRest}`
112
+ ? THead extends keyof TValue
113
+ ? ConfigPathValue<TRest, TValue[THead]>
114
+ : unknown
115
+ : TPath extends keyof TValue
116
+ ? TValue[TPath]
117
+ : unknown;
118
+
119
+ /** The information about one config file candidate in the resolution chain. */
120
+ export type ConfigSource = {
121
+ /** The path of the config file. */
122
+ path: string;
123
+ /** Whether the file was found, and it could be read and parsed. */
124
+ exists: boolean;
125
+ /** Whether the file was used to build the resolved config. */
126
+ used: boolean;
127
+ /** Whether the file is in the global agent directory or in the project. */
128
+ scope: 'global' | 'project';
129
+ /** Whether it's a `config.local` file, meant for overrides that shouldn't be committed. */
130
+ local: boolean;
131
+ };
132
+
133
+ /**
134
+ * The runtime schema accepted by the Node API config readers.
135
+ *
136
+ * @template TValue The type of the value the schema produces.
137
+ */
138
+ export type ConfigSchema<TValue> = z.ZodType<TValue>;
139
+
140
+ /**
141
+ * The options shared by the Node API config readers that can validate with a schema.
142
+ *
143
+ * @template TValue The type of the value the schema produces.
144
+ */
145
+ export type GetConfigOptions<TValue = PiCoreConfig> = {
146
+ /**
147
+ * The project directory, the one that has the `.pi` folder with the project config.
148
+ *
149
+ * @default process.cwd()
150
+ */
151
+ cwd?: string;
152
+ /**
153
+ * A Zod schema to validate and parse the resolved value. If the value exists and
154
+ * doesn't match, the reader throws a {@link ConfigValidationError}.
155
+ */
156
+ schema?: ConfigSchema<TValue>;
157
+ };
158
+
159
+ /** The options for the model-facing config queries. */
160
+ export type QueryConfigOptions = {
161
+ /**
162
+ * The project directory, the one that has the `.pi` folder with the project config.
163
+ *
164
+ * @default process.cwd()
165
+ */
166
+ cwd?: string;
167
+ };
168
+
169
+ /** The result of loading the full resolved config, with the metadata of its sources. */
170
+ export type ReadConfigResult = {
171
+ /** The resolved config: the files merged in order, without the session override. */
172
+ config: PiCoreConfig;
173
+ /** The config file candidates, in merge order, and what happened with each. */
174
+ sources: ConfigSource[];
175
+ };
176
+
177
+ /**
178
+ * The scopes a model-facing config query supports: `all` for everything; a whole
179
+ * namespace (`pi`, `skills`, `tools`, `extensions`); or a single integration (`skill`,
180
+ * `tool`, `extension`), which also needs a name.
181
+ */
182
+ export type ConfigQueryScope =
183
+ | 'all'
184
+ | 'pi'
185
+ | 'skills'
186
+ | 'tools'
187
+ | 'extensions'
188
+ | 'skill'
189
+ | 'tool'
190
+ | 'extension';
191
+
192
+ /** The runtime query shape the `get_config` tool uses. */
193
+ export type ConfigQuery = {
194
+ /**
195
+ * A dot-path to read, like `skills.commit.runChecks`. It can't be used together with
196
+ * `scope` or `name`.
197
+ */
198
+ path?: string;
199
+ /** What to read. */
200
+ scope?: ConfigQueryScope;
201
+ /**
202
+ * The name of the skill, tool, or extension. It's required by the `skill`, `tool`, and
203
+ * `extension` scopes, and it's not valid with any other.
204
+ */
205
+ name?: string;
206
+ };
207
+
208
+ /** The structured result of a runtime config query. */
209
+ export type QueryConfigResult = {
210
+ /**
211
+ * `found` when there's a value, `not_found` when the path doesn't exist, and `error`
212
+ * when the query is not valid.
213
+ */
214
+ status: 'found' | 'not_found' | 'error';
215
+ /** The query that generated the result. */
216
+ query: ConfigQuery;
217
+ /** The value that was found. It's only there when the `status` is `found`. */
218
+ value?: unknown;
219
+ /**
220
+ * Whether secret-looking values were redacted from the `value`. `queryConfig` never
221
+ * redacts, so it's always `false`; the `get_config` tool is the one that redacts.
222
+ */
223
+ redacted: boolean;
224
+ /** The config file candidates that were checked. It's empty if the query is not valid. */
225
+ sources: ConfigSource[];
226
+ /** A human-readable explanation, for the `not_found` and `error` statuses. */
227
+ message?: string;
228
+ };
229
+
230
+ /** Structured result details for the set_config tool. */
231
+ export type SetConfigDetails = {
232
+ /** Whether the override was applied, or the tool failed. */
233
+ status: 'applied' | 'error';
234
+ /** The path the tool tried to set. */
235
+ path: string;
236
+ /** The value that was set. It's only there when the `status` is `applied`. */
237
+ value?: unknown;
238
+ /** The explanation of what failed. It's only there when the `status` is `error`. */
239
+ message?: string;
240
+ };