@soederpop/luca 0.0.28 → 0.0.30

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 (51) hide show
  1. package/commands/try-all-challenges.ts +1 -1
  2. package/docs/TABLE-OF-CONTENTS.md +0 -3
  3. package/docs/examples/structured-output-with-assistants.md +144 -0
  4. package/docs/tutorials/20-browser-esm.md +234 -0
  5. package/package.json +1 -1
  6. package/src/agi/container.server.ts +4 -0
  7. package/src/agi/features/assistant.ts +132 -2
  8. package/src/agi/features/browser-use.ts +623 -0
  9. package/src/agi/features/conversation.ts +135 -45
  10. package/src/agi/lib/interceptor-chain.ts +79 -0
  11. package/src/bootstrap/generated.ts +381 -308
  12. package/src/cli/build-info.ts +2 -2
  13. package/src/clients/rest.ts +7 -7
  14. package/src/commands/chat.ts +22 -0
  15. package/src/commands/describe.ts +67 -2
  16. package/src/commands/prompt.ts +23 -3
  17. package/src/container.ts +411 -113
  18. package/src/helper.ts +189 -5
  19. package/src/introspection/generated.agi.ts +17664 -11568
  20. package/src/introspection/generated.node.ts +4891 -1860
  21. package/src/introspection/generated.web.ts +379 -291
  22. package/src/introspection/index.ts +7 -0
  23. package/src/introspection/scan.ts +224 -7
  24. package/src/node/container.ts +31 -10
  25. package/src/node/features/content-db.ts +7 -7
  26. package/src/node/features/disk-cache.ts +11 -11
  27. package/src/node/features/esbuild.ts +3 -3
  28. package/src/node/features/file-manager.ts +37 -16
  29. package/src/node/features/fs.ts +64 -25
  30. package/src/node/features/git.ts +10 -10
  31. package/src/node/features/helpers.ts +25 -18
  32. package/src/node/features/ink.ts +13 -13
  33. package/src/node/features/ipc-socket.ts +8 -8
  34. package/src/node/features/networking.ts +3 -3
  35. package/src/node/features/os.ts +7 -7
  36. package/src/node/features/package-finder.ts +15 -15
  37. package/src/node/features/proc.ts +1 -1
  38. package/src/node/features/ui.ts +13 -13
  39. package/src/node/features/vm.ts +4 -4
  40. package/src/scaffolds/generated.ts +1 -1
  41. package/src/servers/express.ts +6 -6
  42. package/src/servers/mcp.ts +4 -4
  43. package/src/servers/socket.ts +6 -6
  44. package/test/interceptor-chain.test.ts +61 -0
  45. package/docs/apis/features/node/window-manager.md +0 -445
  46. package/docs/examples/window-manager-layouts.md +0 -180
  47. package/docs/examples/window-manager.md +0 -125
  48. package/docs/window-manager-fix.md +0 -249
  49. package/scripts/test-window-manager-lifecycle.ts +0 -86
  50. package/scripts/test-window-manager.ts +0 -43
  51. package/src/node/features/window-manager.ts +0 -1603
@@ -128,7 +128,7 @@ export class Ink extends Feature<InkState, InkOptions> {
128
128
  * Exposed so consumers don't need a separate react import.
129
129
  * Lazy-loaded — first access triggers the import.
130
130
  */
131
- get React() {
131
+ get React(): typeof import('react') {
132
132
  // return a promise the first time, but for ergonomics we also
133
133
  // expose a sync getter that throws if react hasn't loaded yet.
134
134
  if (this._reactModule) return this._reactModule
@@ -151,7 +151,7 @@ export class Ink extends Feature<InkState, InkOptions> {
151
151
  * const { Box, Text } = ink.components
152
152
  * ```
153
153
  */
154
- async loadModules() {
154
+ async loadModules(): Promise<this> {
155
155
  await Promise.all([this._getInk(), this._getReact()])
156
156
  return this
157
157
  }
@@ -163,7 +163,7 @@ export class Ink extends Feature<InkState, InkOptions> {
163
163
  * const { Box, Text, Static, Spacer } = ink.components
164
164
  * ```
165
165
  */
166
- get components() {
166
+ get components(): { Box: typeof import('ink').Box; Text: typeof import('ink').Text; Static: typeof import('ink').Static; Transform: typeof import('ink').Transform; Newline: typeof import('ink').Newline; Spacer: typeof import('ink').Spacer } {
167
167
  const ink = this._inkModule
168
168
  if (!ink) {
169
169
  throw new Error(
@@ -188,7 +188,7 @@ export class Ink extends Feature<InkState, InkOptions> {
188
188
  * const { useInput, useApp, useFocus } = ink.hooks
189
189
  * ```
190
190
  */
191
- get hooks() {
191
+ get hooks(): { useInput: typeof import('ink').useInput; useApp: typeof import('ink').useApp; useStdin: typeof import('ink').useStdin; useStdout: typeof import('ink').useStdout; useStderr: typeof import('ink').useStderr; useFocus: typeof import('ink').useFocus; useFocusManager: typeof import('ink').useFocusManager; useCursor: typeof import('ink').useCursor } {
192
192
  const ink = this._inkModule
193
193
  if (!ink) {
194
194
  throw new Error(
@@ -211,7 +211,7 @@ export class Ink extends Feature<InkState, InkOptions> {
211
211
  /**
212
212
  * The Ink measureElement utility.
213
213
  */
214
- get measureElement() {
214
+ get measureElement(): typeof import('ink').measureElement {
215
215
  const ink = this._inkModule
216
216
  if (!ink) {
217
217
  throw new Error('Ink not loaded yet.')
@@ -229,7 +229,7 @@ export class Ink extends Feature<InkState, InkOptions> {
229
229
  * @param options - Ink render options (stdout, stdin, debug, etc.)
230
230
  * @returns The Ink instance with rerender, unmount, waitUntilExit, clear
231
231
  */
232
- async render(node: any, options: Record<string, any> = {}) {
232
+ async render(node: any, options: Record<string, any> = {}): Promise<any> {
233
233
  const ink = await this._getInk()
234
234
  await this._getReact()
235
235
 
@@ -280,7 +280,7 @@ export class Ink extends Feature<InkState, InkOptions> {
280
280
  * ink.rerender(React.createElement(Text, null, 'Updated!'))
281
281
  * ```
282
282
  */
283
- rerender(node: any) {
283
+ rerender(node: any): void {
284
284
  if (!this._instance) {
285
285
  throw new Error('No mounted ink app. Call render() first.')
286
286
  }
@@ -304,7 +304,7 @@ export class Ink extends Feature<InkState, InkOptions> {
304
304
  * console.log(ink.isMounted) // false
305
305
  * ```
306
306
  */
307
- unmount() {
307
+ unmount(): void {
308
308
  if (this._instance) {
309
309
  this._instance.unmount()
310
310
  this.setState({ mounted: false })
@@ -351,7 +351,7 @@ export class Ink extends Feature<InkState, InkOptions> {
351
351
  * ink.clear()
352
352
  * ```
353
353
  */
354
- clear() {
354
+ clear(): void {
355
355
  if (this._instance) {
356
356
  this._instance.clear()
357
357
  }
@@ -367,7 +367,7 @@ export class Ink extends Feature<InkState, InkOptions> {
367
367
  /**
368
368
  * The raw ink render instance if you need low-level access.
369
369
  */
370
- get instance() {
370
+ get instance(): any | null {
371
371
  return this._instance
372
372
  }
373
373
 
@@ -387,7 +387,7 @@ export class Ink extends Feature<InkState, InkOptions> {
387
387
  * )
388
388
  * ```
389
389
  */
390
- registerBlock(name: string, component: Function) {
390
+ registerBlock(name: string, component: Function): this {
391
391
  this._blocks.set(name, component)
392
392
  return this
393
393
  }
@@ -407,7 +407,7 @@ export class Ink extends Feature<InkState, InkOptions> {
407
407
  * await ink.renderBlock('Greeting', { name: 'Jon' })
408
408
  * ```
409
409
  */
410
- async renderBlock(name: string, data?: Record<string, any>) {
410
+ async renderBlock(name: string, data?: Record<string, any>): Promise<void> {
411
411
  const component = this._blocks.get(name)
412
412
  if (!component) {
413
413
  throw new Error(`No block registered with name "${name}". Available: ${[...this._blocks.keys()].join(', ') || '(none)'}`)
@@ -450,7 +450,7 @@ export class Ink extends Feature<InkState, InkOptions> {
450
450
  * await renderAsync('AsyncChart', { url: 'https://api.example.com/data' })
451
451
  * ```
452
452
  */
453
- async renderBlockAsync(name: string, data?: Record<string, any>, options?: { timeout?: number }) {
453
+ async renderBlockAsync(name: string, data?: Record<string, any>, options?: { timeout?: number }): Promise<void> {
454
454
  const component = this._blocks.get(name)
455
455
  if (!component) {
456
456
  throw new Error(`No block registered with name "${name}". Available: ${[...this._blocks.keys()].join(', ') || '(none)'}`)
@@ -139,7 +139,7 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
139
139
  *
140
140
  * @returns True if the socket is configured as a client
141
141
  */
142
- get isClient() {
142
+ get isClient(): boolean {
143
143
  return this.state.get('mode') === 'client'
144
144
  }
145
145
 
@@ -148,14 +148,14 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
148
148
  *
149
149
  * @returns True if the socket is configured as a server
150
150
  */
151
- get isServer() {
151
+ get isServer(): boolean {
152
152
  return this.state.get('mode') === 'server'
153
153
  }
154
154
 
155
155
  /**
156
156
  * Returns the number of currently connected clients (server mode).
157
157
  */
158
- get clientCount() {
158
+ get clientCount(): number {
159
159
  return this.clients.size
160
160
  }
161
161
 
@@ -326,7 +326,7 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
326
326
  *
327
327
  * @returns The active Socket connection, or undefined if not connected
328
328
  */
329
- get connection() {
329
+ get connection(): Socket | undefined {
330
330
  return this._connection
331
331
  }
332
332
 
@@ -337,7 +337,7 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
337
337
  * @param exclude - Optional client ID to exclude from broadcast
338
338
  * @returns This instance for method chaining
339
339
  */
340
- broadcast(message: any, exclude?: string) {
340
+ broadcast(message: any, exclude?: string): this {
341
341
  const envelope = JSON.stringify({
342
342
  data: message,
343
343
  id: this.container.utils.uuid()
@@ -377,7 +377,7 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
377
377
  *
378
378
  * @param message - The message to send
379
379
  */
380
- async send(message: any) {
380
+ async send(message: any): Promise<void> {
381
381
  if(!this._connection) {
382
382
  throw new Error("No connection.")
383
383
  }
@@ -449,7 +449,7 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
449
449
  * @param data - The reply payload
450
450
  * @param clientId - Target client (server mode; for client mode, omit)
451
451
  */
452
- reply(requestId: string, data: any, clientId?: string) {
452
+ reply(requestId: string, data: any, clientId?: string): void {
453
453
  const envelope = JSON.stringify({
454
454
  id: this.container.utils.uuid(),
455
455
  data,
@@ -557,7 +557,7 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
557
557
  /**
558
558
  * Disconnects the client and stops any reconnection attempts.
559
559
  */
560
- disconnect() {
560
+ disconnect(): void {
561
561
  this._reconnect.enabled = false
562
562
  if (this._reconnect.timer) {
563
563
  clearTimeout(this._reconnect.timer)
@@ -249,11 +249,11 @@ export class Networking extends Feature<NetworkingState, NetworkingOptions> {
249
249
  return this._binCache[name]
250
250
  }
251
251
 
252
- get proc() {
252
+ get proc(): ReturnType<typeof this.container.feature<'proc'>> {
253
253
  return this.container.feature('proc')
254
254
  }
255
255
 
256
- get os() {
256
+ get os(): ReturnType<typeof this.container.feature<'os'>> {
257
257
  return this.container.feature('os')
258
258
  }
259
259
 
@@ -630,7 +630,7 @@ export class Networking extends Feature<NetworkingState, NetworkingOptions> {
630
630
  /**
631
631
  * Optional nmap wrapper for users that already have nmap installed.
632
632
  */
633
- get nmap() {
633
+ get nmap(): { isAvailable: () => Promise<boolean>; scan: (target: string, args?: string[]) => Promise<{ hosts: NmapHost[]; raw: string }>; quickScan: (cidr: string) => Promise<{ hosts: NmapHost[]; raw: string }>; fullScan: (target: string) => Promise<{ hosts: NmapHost[]; raw: string }> } {
634
634
  return {
635
635
  isAvailable: async () => this.isNmapAvailable(),
636
636
  scan: async (target: string, args: string[] = []) => this.runNmapScan(target, args),
@@ -48,7 +48,7 @@ export class OS extends Feature {
48
48
  * console.log(`Running on ${arch} architecture`)
49
49
  * ```
50
50
  */
51
- get arch() {
51
+ get arch(): string {
52
52
  return os.arch()
53
53
  }
54
54
 
@@ -63,7 +63,7 @@ export class OS extends Feature {
63
63
  * console.log(`Temp directory: ${tempDir}`)
64
64
  * ```
65
65
  */
66
- get tmpdir() {
66
+ get tmpdir(): string {
67
67
  return os.tmpdir()
68
68
  }
69
69
 
@@ -78,7 +78,7 @@ export class OS extends Feature {
78
78
  * console.log(`User home: ${home}`)
79
79
  * ```
80
80
  */
81
- get homedir() {
81
+ get homedir(): string {
82
82
  return os.homedir()
83
83
  }
84
84
 
@@ -93,7 +93,7 @@ export class OS extends Feature {
93
93
  * console.log(`System has ${cores} CPU cores`)
94
94
  * ```
95
95
  */
96
- get cpuCount() {
96
+ get cpuCount(): number {
97
97
  return os.cpus().length
98
98
  }
99
99
 
@@ -108,7 +108,7 @@ export class OS extends Feature {
108
108
  * console.log(`Hostname: ${hostname}`)
109
109
  * ```
110
110
  */
111
- get hostname() {
111
+ get hostname(): string {
112
112
  return os.hostname()
113
113
  }
114
114
 
@@ -125,7 +125,7 @@ export class OS extends Feature {
125
125
  * }
126
126
  * ```
127
127
  */
128
- get platform() {
128
+ get platform(): string {
129
129
  return os.platform()
130
130
  }
131
131
 
@@ -142,7 +142,7 @@ export class OS extends Feature {
142
142
  * })
143
143
  * ```
144
144
  */
145
- get networkInterfaces() {
145
+ get networkInterfaces(): NodeJS.Dict<os.NetworkInterfaceInfo[]> {
146
146
  return os.networkInterfaces()
147
147
  }
148
148
 
@@ -148,7 +148,7 @@ export class PackageFinder<
148
148
  * }, '/project/node_modules/lodash/package.json');
149
149
  * ```
150
150
  */
151
- addPackage(manifest: PartialManifest, path: string) {
151
+ addPackage(manifest: PartialManifest, path: string): void {
152
152
  const { name } = manifest
153
153
 
154
154
  if (!this.packages[name]) {
@@ -179,7 +179,7 @@ export class PackageFinder<
179
179
  * });
180
180
  * ```
181
181
  */
182
- get duplicates() {
182
+ get duplicates(): string[] {
183
183
  return Object.keys(
184
184
  pickBy(this.packages, (packages) => packages.length > 1)
185
185
  )
@@ -199,7 +199,7 @@ export class PackageFinder<
199
199
  * console.log(`Found ${finder.packageNames.length} unique packages`);
200
200
  * ```
201
201
  */
202
- async start() {
202
+ async start(): Promise<this | undefined> {
203
203
  if (this.isStarted) {
204
204
  return this;
205
205
  }
@@ -234,7 +234,7 @@ export class PackageFinder<
234
234
  * console.log(`Scanned ${finder.manifests.length} packages`);
235
235
  * ```
236
236
  */
237
- async scan(options: { exclude?: string | string[] } = {}) {
237
+ async scan(options: { exclude?: string | string[] } = {}): Promise<this> {
238
238
  const packageFolders = await this.findAllPackageFolders()
239
239
  const manifestPaths = packageFolders.map((folder) => `${folder}/package.json`)
240
240
 
@@ -256,7 +256,7 @@ export class PackageFinder<
256
256
  *
257
257
  * @returns True if the finder has been started and scanning is complete
258
258
  */
259
- get isStarted() {
259
+ get isStarted(): boolean {
260
260
  return !!this.state.get("started");
261
261
  }
262
262
 
@@ -271,7 +271,7 @@ export class PackageFinder<
271
271
  * console.log(`Found ${names.length} unique packages`);
272
272
  * ```
273
273
  */
274
- get packageNames() {
274
+ get packageNames(): string[] {
275
275
  return Object.keys(this.packages)
276
276
  }
277
277
 
@@ -294,7 +294,7 @@ export class PackageFinder<
294
294
  * });
295
295
  * ```
296
296
  */
297
- get scopes() {
297
+ get scopes(): string[] {
298
298
  return Array.from(
299
299
  new Set(this.packageNames.filter(p => p.startsWith('@')).map(p => p.split('/')[0]))
300
300
  )
@@ -317,7 +317,7 @@ export class PackageFinder<
317
317
  * }
318
318
  * ```
319
319
  */
320
- findByName(name: string) {
320
+ findByName(name: string): PartialManifest | undefined {
321
321
  return this.find((i) => i.name === name)
322
322
  }
323
323
 
@@ -340,7 +340,7 @@ export class PackageFinder<
340
340
  * });
341
341
  * ```
342
342
  */
343
- findDependentsOf(packageName: string) {
343
+ findDependentsOf(packageName: string): PartialManifest[] {
344
344
  return this.filter(({ dependencies = {}, devDependencies = {} }) => {
345
345
  return packageName in dependencies || packageName in devDependencies
346
346
  })
@@ -361,7 +361,7 @@ export class PackageFinder<
361
361
  * const utility = finder.find(pkg => pkg.description?.includes('utility'));
362
362
  * ```
363
363
  */
364
- find(filter: (manifest: PartialManifest) => boolean) {
364
+ find(filter: (manifest: PartialManifest) => boolean): PartialManifest | undefined {
365
365
  return this.manifests.find(filter)
366
366
  }
367
367
 
@@ -383,7 +383,7 @@ export class PackageFinder<
383
383
  * const scoped = finder.filter(pkg => pkg.name.startsWith('@'));
384
384
  * ```
385
385
  */
386
- filter(filter: (manifest: PartialManifest) => boolean) {
386
+ filter(filter: (manifest: PartialManifest) => boolean): PartialManifest[] {
387
387
  return this.manifests.filter(filter)
388
388
  }
389
389
 
@@ -404,7 +404,7 @@ export class PackageFinder<
404
404
  * const unscoped = finder.exclude(pkg => pkg.name.startsWith('@'));
405
405
  * ```
406
406
  */
407
- exclude(filter: (manifest: PartialManifest) => boolean) {
407
+ exclude(filter: (manifest: PartialManifest) => boolean): PartialManifest[] {
408
408
  return this.manifests.filter((m) => !filter(m))
409
409
  }
410
410
 
@@ -428,7 +428,7 @@ export class PackageFinder<
428
428
  * }, {});
429
429
  * ```
430
430
  */
431
- get manifests() {
431
+ get manifests(): (PartialManifest & { __path: string })[] {
432
432
  return Object.values(this.packages).flat()
433
433
  }
434
434
 
@@ -452,7 +452,7 @@ export class PackageFinder<
452
452
  * });
453
453
  * ```
454
454
  */
455
- get counts() {
455
+ get counts(): Record<string, number> {
456
456
  return mapValues(this.packages, (packages) => packages.length)
457
457
  }
458
458
 
@@ -489,7 +489,7 @@ export class PackageFinder<
489
489
  return results;
490
490
  }
491
491
 
492
- async findLocalPackageFolders() {
492
+ async findLocalPackageFolders(): Promise<string[]> {
493
493
  const cmd = "find . -name package.json"
494
494
  const result = this.container.proc.exec(cmd)
495
495
  const all = result.split("\n").filter(Boolean).map((path: string) => this.container.paths.dirname(path))
@@ -233,7 +233,7 @@ export class ChildProcess extends Feature {
233
233
  * Useful when callers need streaming access to stdout/stderr and
234
234
  * direct lifecycle control (for example, cancellation via kill()).
235
235
  */
236
- spawn(command: string, args: string[] = [], options: RawSpawnOptions = {}) {
236
+ spawn(command: string, args: string[] = [], options: RawSpawnOptions = {}): import('child_process').ChildProcess {
237
237
  const cwd = options.cwd ?? this.container.cwd
238
238
  const stdout = options.stdout ?? 'pipe'
239
239
  const stderr = options.stderr ?? 'pipe'
@@ -104,7 +104,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
104
104
  * @param text - The markdown string to parse and render
105
105
  * @returns The rendered terminal-formatted string
106
106
  */
107
- markdown(text: string) {
107
+ markdown(text: string): string | Promise<string> {
108
108
  return marked.parse(text)
109
109
  }
110
110
 
@@ -220,7 +220,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
220
220
  * });
221
221
  * ```
222
222
  */
223
- get randomColor() {
223
+ get randomColor(): string | undefined {
224
224
  const colors = Object.keys(this.colors);
225
225
  const index = Math.floor(Math.random() * colors.length);
226
226
 
@@ -321,7 +321,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
321
321
  * ], { version: '1.0.0' });
322
322
  * ```
323
323
  */
324
- wizard(questions: any[], initialAnswers: any = {}) {
324
+ wizard(questions: any[], initialAnswers: any = {}): Promise<any> {
325
325
  return inquirer.createPromptModule()(questions, initialAnswers);
326
326
  }
327
327
 
@@ -331,7 +331,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
331
331
  * @param question - The question message to display
332
332
  * @returns Promise resolving to the answers object with a `question` key
333
333
  */
334
- askQuestion(question: string) {
334
+ askQuestion(question: string): Promise<any> {
335
335
  return inquirer.createPromptModule()([{
336
336
  type: 'input',
337
337
  name: 'question',
@@ -371,7 +371,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
371
371
  * const editedMarkdown = await ui.openInEditor(markdown, '.md');
372
372
  * ```
373
373
  */
374
- async openInEditor(text: string, extension = ".ts") {
374
+ async openInEditor(text: string, extension = ".ts"): Promise<unknown> {
375
375
  const results = await new Promise((resolve, reject) => {
376
376
  /*
377
377
  editAsync(
@@ -429,7 +429,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
429
429
  * console.log('Available fonts:', ui.fonts.slice(0, 10).join(', '));
430
430
  * ```
431
431
  */
432
- asciiArt(text: string, font: Fonts) {
432
+ asciiArt(text: string, font: Fonts): string {
433
433
  if (!figlet) return text;
434
434
  return figlet.textSync(text, font);
435
435
  }
@@ -481,7 +481,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
481
481
  * // Available colors: any chalk color names
482
482
  * ```
483
483
  */
484
- banner(text: string, options: { font: Fonts; colors: Color[] } = { font: 'Star Wars', colors: ['red', 'white', 'blue'] }) {
484
+ banner(text: string, options: { font: Fonts; colors: Color[] } = { font: 'Star Wars', colors: ['red', 'white', 'blue'] }): string {
485
485
  const art = this.asciiArt(text, options.font || 'Star Wars');
486
486
  const colored = this.applyGradient(art, options.colors || ['red', 'white', 'blue']);
487
487
 
@@ -495,7 +495,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
495
495
  * @param args - Tagged template literal arguments
496
496
  * @returns The dedented string
497
497
  */
498
- endent(...args: any[]) {
498
+ endent(...args: any[]): string {
499
499
  // @ts-ignore
500
500
  return endent(...args)
501
501
  }
@@ -544,7 +544,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
544
544
  text: string,
545
545
  lineColors: Color[] = ["red", "white", "blue"],
546
546
  direction: "horizontal" | "vertical" = "horizontal"
547
- ) {
547
+ ): string {
548
548
  if (direction === "horizontal") {
549
549
  return this.applyHorizontalGradient(text, lineColors);
550
550
  }
@@ -587,7 +587,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
587
587
  applyHorizontalGradient(
588
588
  text: string,
589
589
  lineColors: Color[] = ["red", "white", "blue"]
590
- ) {
590
+ ): string {
591
591
  const gColors = Object.fromEntries(
592
592
  lineColors.map((color) => [color, this.colors[color]])
593
593
  );
@@ -639,7 +639,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
639
639
  applyVerticalGradient(
640
640
  text: string,
641
641
  lineColors: Color[] = ["red", "white", "blue"]
642
- ) {
642
+ ): string {
643
643
  const gColors = Object.fromEntries(
644
644
  lineColors.map((color) => [color, this.colors[color]])
645
645
  );
@@ -689,7 +689,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
689
689
  * const title = ui.padLeft('TITLE', 20, '-'); // '---------------TITLE'
690
690
  * ```
691
691
  */
692
- padLeft(str: string, length: number, padChar = " ") {
692
+ padLeft(str: string, length: number, padChar = " "): string {
693
693
  if (str.length >= length) {
694
694
  return str;
695
695
  }
@@ -744,7 +744,7 @@ export class UI<T extends UIState = UIState> extends Feature<T> {
744
744
  * const menuItem = ui.padRight('Coffee', 20, '.') + '$3.50';
745
745
  * ```
746
746
  */
747
- padRight(str: string, length: number, padChar = " ") {
747
+ padRight(str: string, length: number, padChar = " "): string {
748
748
  if (str.length >= length) {
749
749
  return str;
750
750
  }
@@ -67,7 +67,7 @@ export class VM<
67
67
  * // import { Container } from '@soederpop/luca' → works
68
68
  * ```
69
69
  */
70
- defineModule(id: string, exports: any) {
70
+ defineModule(id: string, exports: any): void {
71
71
  this.modules.set(id, exports)
72
72
  }
73
73
 
@@ -78,7 +78,7 @@ export class VM<
78
78
  * @param filePath - The file path to scope native require resolution to
79
79
  * @returns A require function with `.resolve` preserved from the native require
80
80
  */
81
- createRequireFor(filePath: string) {
81
+ createRequireFor(filePath: string): ((id: string) => any) & { resolve: RequireResolve } {
82
82
  const nodeRequire = createRequire(filePath)
83
83
  const modules = this.modules
84
84
 
@@ -111,7 +111,7 @@ export class VM<
111
111
  * const result2 = script.runInContext(vm.createContext({ a: 10, b: 20 }))
112
112
  * ```
113
113
  */
114
- createScript(code: string, options?: vm.ScriptOptions) {
114
+ createScript(code: string, options?: vm.ScriptOptions): vm.Script {
115
115
  return new vm.Script(code, {
116
116
  ...options
117
117
  })
@@ -152,7 +152,7 @@ export class VM<
152
152
  * const result = vm.runSync('user.name', context)
153
153
  * ```
154
154
  */
155
- createContext(ctx: any = {}) {
155
+ createContext(ctx: any = {}): vm.Context {
156
156
  if (this.isContext(ctx)) return ctx
157
157
  return vm.createContext({
158
158
  console,
@@ -1,5 +1,5 @@
1
1
  // Auto-generated scaffold and MCP readme content
2
- // Generated at: 2026-03-23T07:45:57.906Z
2
+ // Generated at: 2026-03-24T06:38:36.374Z
3
3
  // Source: docs/scaffolds/*.md, docs/examples/assistant/, and docs/mcp/readme.md
4
4
  //
5
5
  // Do not edit manually. Run: luca build-scaffolds
@@ -55,11 +55,11 @@ export class ExpressServer<T extends ServerState = ServerState, K extends Expres
55
55
  _listener?: any
56
56
  _mountedEndpoints: Endpoint[] = []
57
57
 
58
- get express() {
58
+ get express(): typeof express {
59
59
  return express
60
60
  }
61
61
 
62
- get hooks() {
62
+ get hooks(): { create: (app: Express, server: Server) => Express; beforeStart: (options: any, server: Server) => any } {
63
63
  const { create = defaultCreate, beforeStart = () => {} } = this.options
64
64
 
65
65
  return {
@@ -68,7 +68,7 @@ export class ExpressServer<T extends ServerState = ServerState, K extends Expres
68
68
  }
69
69
  }
70
70
 
71
- get app() {
71
+ get app(): Express {
72
72
  if(this._app) {
73
73
  return this._app
74
74
  }
@@ -99,7 +99,7 @@ export class ExpressServer<T extends ServerState = ServerState, K extends Expres
99
99
  *
100
100
  * @param options - Optional runtime overrides for port and host
101
101
  */
102
- override async start(options?: StartOptions) {
102
+ override async start(options?: StartOptions): Promise<this> {
103
103
  if (this.isListening) {
104
104
  return this
105
105
  }
@@ -137,7 +137,7 @@ export class ExpressServer<T extends ServerState = ServerState, K extends Expres
137
137
  return this
138
138
  }
139
139
 
140
- override async stop() {
140
+ override async stop(): Promise<this> {
141
141
  if (this.isStopped) {
142
142
  return this
143
143
  }
@@ -167,7 +167,7 @@ export class ExpressServer<T extends ServerState = ServerState, K extends Expres
167
167
  return this
168
168
  }
169
169
 
170
- override async configure() {
170
+ override async configure(): Promise<this> {
171
171
  this.state.set('configured', true)
172
172
  return this
173
173
  }
@@ -428,7 +428,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
428
428
  * Configure the MCP protocol server and register all protocol handlers.
429
429
  * Called automatically before start() if not already configured.
430
430
  */
431
- override async configure() {
431
+ override async configure(): Promise<this> {
432
432
  if (this.isConfigured) return this
433
433
 
434
434
  const serverName = this.options.serverName || this.container.manifest?.name || 'luca-mcp'
@@ -470,7 +470,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
470
470
  host?: string
471
471
  mcpCompat?: MCPCompatMode
472
472
  stdioCompat?: StdioCompatMode
473
- }) {
473
+ }): Promise<this> {
474
474
  if (this.isListening) return this
475
475
  await this._drainPendingPlugins()
476
476
  if (!this.isConfigured) await this.configure()
@@ -499,7 +499,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
499
499
  /**
500
500
  * Stop the MCP server and close all connections.
501
501
  */
502
- override async stop() {
502
+ override async stop(): Promise<this> {
503
503
  if (this.isStopped) return this
504
504
 
505
505
  if (this._mcpServer) {
@@ -631,7 +631,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
631
631
  }
632
632
 
633
633
  /** Start an HTTP transport using StreamableHTTPServerTransport. */
634
- private async _startHTTPTransport(port: number, compat: MCPCompatMode) {
634
+ private async _startHTTPTransport(port: number, compat: MCPCompatMode): Promise<void> {
635
635
  const { createServer } = await import('node:http')
636
636
  const { StreamableHTTPServerTransport } = await import(
637
637
  '@modelcontextprotocol/sdk/server/streamableHttp.js'