@soederpop/luca 0.0.3 → 0.0.4

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 (113) hide show
  1. package/AGENTS.md +98 -0
  2. package/CLAUDE.md +27 -0
  3. package/SPEC.md +304 -0
  4. package/bun.lock +110 -265
  5. package/docs/CLI.md +1 -1
  6. package/docs/apis/features/node/content-db.md +16 -0
  7. package/docs/apis/features/node/fs.md +24 -0
  8. package/docs/apis/features/node/ipc-socket.md +0 -1
  9. package/docs/apis/features/node/package-finder.md +1 -11
  10. package/docs/apis/features/node/proc.md +0 -41
  11. package/docs/apis/features/node/ui.md +0 -2
  12. package/package.json +12 -8
  13. package/src/agi/container.server.ts +16 -3
  14. package/src/agi/features/assistant.ts +3 -7
  15. package/src/agi/features/assistants-manager.ts +3 -7
  16. package/src/agi/features/claude-code.ts +3 -7
  17. package/src/agi/features/conversation-history.ts +3 -7
  18. package/src/agi/features/conversation.ts +4 -8
  19. package/src/agi/features/openai-codex.ts +3 -7
  20. package/src/agi/features/openapi.ts +4 -2
  21. package/src/agi/features/skills-library.ts +4 -8
  22. package/src/cli/cli.ts +22 -0
  23. package/src/client.ts +69 -26
  24. package/src/clients/civitai/index.ts +3 -7
  25. package/src/clients/comfyui/index.ts +5 -9
  26. package/src/clients/elevenlabs/index.ts +39 -19
  27. package/src/clients/openai/index.ts +3 -7
  28. package/src/clients/supabase/index.ts +4 -13
  29. package/src/commands/console.ts +0 -3
  30. package/src/commands/eval.ts +1 -1
  31. package/src/commands/index.ts +1 -0
  32. package/src/commands/introspect.ts +128 -0
  33. package/src/commands/prompt.ts +1 -4
  34. package/src/commands/run.ts +6 -13
  35. package/src/commands/sandbox-mcp.ts +1 -13
  36. package/src/feature.ts +45 -2
  37. package/src/introspection/generated.agi.ts +175 -101
  38. package/src/introspection/generated.node.ts +175 -101
  39. package/src/introspection/generated.web.ts +113 -29
  40. package/src/introspection/index.ts +1 -1
  41. package/src/introspection/scan.ts +3 -1
  42. package/src/node/features/container-link.ts +3 -2
  43. package/src/node/features/content-db.ts +10 -2
  44. package/src/node/features/disk-cache.ts +3 -4
  45. package/src/node/features/dns.ts +3 -2
  46. package/src/node/features/docker.ts +3 -2
  47. package/src/node/features/downloader.ts +3 -16
  48. package/src/node/features/esbuild.ts +3 -12
  49. package/src/node/features/file-manager.ts +3 -2
  50. package/src/node/features/fs.ts +12 -3
  51. package/src/node/features/git.ts +3 -2
  52. package/src/node/features/google-auth.ts +3 -2
  53. package/src/node/features/google-calendar.ts +3 -2
  54. package/src/node/features/google-docs.ts +3 -2
  55. package/src/node/features/google-drive.ts +3 -2
  56. package/src/node/features/google-sheets.ts +3 -2
  57. package/src/node/features/grep.ts +3 -2
  58. package/src/node/features/helpers.ts +13 -2
  59. package/src/node/features/ink.ts +3 -3
  60. package/src/node/features/ipc-socket.ts +3 -3
  61. package/src/node/features/json-tree.ts +3 -21
  62. package/src/node/features/launcher-app-command-listener.ts +3 -2
  63. package/src/node/features/networking.ts +3 -2
  64. package/src/node/features/nlp.ts +3 -2
  65. package/src/node/features/opener.ts +8 -7
  66. package/src/node/features/os.ts +3 -2
  67. package/src/node/features/package-finder.ts +3 -2
  68. package/src/node/features/port-exposer.ts +3 -4
  69. package/src/node/features/postgres.ts +3 -3
  70. package/src/node/features/proc.ts +37 -64
  71. package/src/node/features/process-manager.ts +3 -2
  72. package/src/node/features/python.ts +3 -3
  73. package/src/node/features/repl.ts +3 -2
  74. package/src/node/features/runpod.ts +3 -3
  75. package/src/node/features/secure-shell.ts +3 -2
  76. package/src/node/features/semantic-search.ts +4 -6
  77. package/src/node/features/sqlite.ts +3 -3
  78. package/src/node/features/telegram.ts +3 -2
  79. package/src/node/features/tts.ts +3 -2
  80. package/src/node/features/ui.ts +3 -3
  81. package/src/node/features/vault.ts +3 -14
  82. package/src/node/features/vm.ts +41 -3
  83. package/src/node/features/window-manager.ts +165 -22
  84. package/src/node/features/yaml-tree.ts +3 -4
  85. package/src/node/features/yaml.ts +3 -2
  86. package/src/registry.ts +1 -1
  87. package/src/scaffolds/generated.ts +1 -1
  88. package/src/server.ts +43 -0
  89. package/src/servers/express.ts +24 -8
  90. package/src/servers/mcp.ts +2 -6
  91. package/src/servers/socket.ts +22 -7
  92. package/src/web/clients/socket.ts +3 -5
  93. package/src/web/features/asset-loader.ts +20 -12
  94. package/src/web/features/container-link.ts +3 -6
  95. package/src/web/features/esbuild.ts +21 -7
  96. package/src/web/features/helpers.ts +4 -2
  97. package/src/web/features/network.ts +24 -7
  98. package/src/web/features/speech.ts +24 -7
  99. package/src/web/features/vault.ts +21 -3
  100. package/src/web/features/vm.ts +20 -13
  101. package/src/web/features/voice-recognition.ts +26 -9
  102. package/commands/update-introspection.ts +0 -67
  103. package/docs/ideas/class-registration-refactor-possibilities.md +0 -197
  104. package/docs/ideas/container-use-api.md +0 -9
  105. package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
  106. package/docs/ideas/feature-stacks.md +0 -22
  107. package/docs/ideas/luca-cli-self-sufficiency-demo.md +0 -23
  108. package/docs/ideas/mcp-design.md +0 -9
  109. package/docs/ideas/web-container-debugging-feature.md +0 -13
  110. package/scripts/animations/chrome-glitch.ts +0 -55
  111. package/scripts/animations/index.ts +0 -16
  112. package/scripts/animations/neon-pulse.ts +0 -64
  113. package/scripts/animations/types.ts +0 -6
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { google, type calendar_v3 } from 'googleapis'
5
5
  import type { GoogleAuth } from './google-auth.js'
6
6
 
@@ -104,6 +104,7 @@ export class GoogleCalendar extends Feature<GoogleCalendarState, GoogleCalendarO
104
104
  static override stateSchema = GoogleCalendarStateSchema
105
105
  static override optionsSchema = GoogleCalendarOptionsSchema
106
106
  static override eventsSchema = GoogleCalendarEventsSchema
107
+ static { Feature.register(this, 'googleCalendar') }
107
108
 
108
109
  private _calendar?: calendar_v3.Calendar
109
110
 
@@ -297,4 +298,4 @@ declare module '../../feature' {
297
298
  }
298
299
  }
299
300
 
300
- export default features.register('googleCalendar', GoogleCalendar)
301
+ export default GoogleCalendar
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { google, type docs_v1 } from 'googleapis'
5
5
  import type { GoogleAuth } from './google-auth.js'
6
6
  import type { GoogleDrive, DriveFile } from './google-drive.js'
@@ -55,6 +55,7 @@ export class GoogleDocs extends Feature<GoogleDocsState, GoogleDocsOptions> {
55
55
  static override stateSchema = GoogleDocsStateSchema
56
56
  static override optionsSchema = GoogleDocsOptionsSchema
57
57
  static override eventsSchema = GoogleDocsEventsSchema
58
+ static { Feature.register(this, 'googleDocs') }
58
59
 
59
60
  private _docs?: docs_v1.Docs
60
61
 
@@ -401,4 +402,4 @@ declare module '../../feature' {
401
402
  }
402
403
  }
403
404
 
404
- export default features.register('googleDocs', GoogleDocs)
405
+ export default GoogleDocs
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { google, type drive_v3 } from 'googleapis'
5
5
  import type { GoogleAuth } from './google-auth.js'
6
6
 
@@ -104,6 +104,7 @@ export class GoogleDrive extends Feature<GoogleDriveState, GoogleDriveOptions> {
104
104
  static override stateSchema = GoogleDriveStateSchema
105
105
  static override optionsSchema = GoogleDriveOptionsSchema
106
106
  static override eventsSchema = GoogleDriveEventsSchema
107
+ static { Feature.register(this, 'googleDrive') }
107
108
 
108
109
  private _drive?: drive_v3.Drive
109
110
 
@@ -336,4 +337,4 @@ declare module '../../feature' {
336
337
  }
337
338
  }
338
339
 
339
- export default features.register('googleDrive', GoogleDrive)
340
+ export default GoogleDrive
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { google, type sheets_v4 } from 'googleapis'
5
5
  import type { GoogleAuth } from './google-auth.js'
6
6
 
@@ -74,6 +74,7 @@ export class GoogleSheets extends Feature<GoogleSheetsState, GoogleSheetsOptions
74
74
  static override stateSchema = GoogleSheetsStateSchema
75
75
  static override optionsSchema = GoogleSheetsOptionsSchema
76
76
  static override eventsSchema = GoogleSheetsEventsSchema
77
+ static { Feature.register(this, 'googleSheets') }
77
78
 
78
79
  private _sheets?: sheets_v4.Sheets
79
80
 
@@ -276,4 +277,4 @@ declare module '../../feature' {
276
277
  }
277
278
  }
278
279
 
279
- export default features.register('googleSheets', GoogleSheets)
280
+ export default GoogleSheets
@@ -1,4 +1,4 @@
1
- import { features, Feature } from '../feature.js'
1
+ import { Feature } from '../feature.js'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
 
4
4
  /** Shell-escape a string using single quotes (safe for all characters) */
@@ -77,6 +77,7 @@ export class Grep extends Feature {
77
77
  static override shortcut = 'features.grep' as const
78
78
  static override stateSchema = FeatureStateSchema
79
79
  static override optionsSchema = FeatureOptionsSchema
80
+ static { Feature.register(this, 'grep') }
80
81
 
81
82
  private _hasRipgrep: boolean | null = null
82
83
 
@@ -403,4 +404,4 @@ export class Grep extends Feature {
403
404
  }
404
405
  }
405
406
 
406
- export default features.register('grep', Grep)
407
+ export default Grep
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { Feature as UniversalFeature } from '../../feature.js'
5
5
  import { Client, clients } from '../../client.js'
6
6
  import { Server, servers } from '../../server.js'
@@ -69,6 +69,7 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
69
69
  static override stateSchema = HelpersStateSchema
70
70
  static override optionsSchema = HelpersOptionsSchema
71
71
  static override eventsSchema = HelpersEventsSchema
72
+ static { Feature.register(this, 'helpers') }
72
73
 
73
74
  /**
74
75
  * Returns a mapping from registry type name to its registry singleton, base class, and conventional folder candidates.
@@ -251,6 +252,16 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
251
252
  const fm = await this.ensureFileManager()
252
253
  const discovered: string[] = []
253
254
 
255
+ // Load build-time introspection data before importing helpers so that
256
+ // interceptRegistration() can merge JSDoc descriptions from the generated file.
257
+ const introspectionFile = resolve(dir, 'introspection.generated.ts')
258
+ try {
259
+ const { existsSync } = await import('fs')
260
+ if (existsSync(introspectionFile)) {
261
+ await import(introspectionFile)
262
+ }
263
+ } catch {}
264
+
254
265
  const tests = [`${type}/*/*.ts`, `${type}/*.ts`]
255
266
  const files = fm.match(tests)
256
267
 
@@ -371,4 +382,4 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
371
382
  }
372
383
  }
373
384
 
374
- export default features.register('helpers', Helpers)
385
+ export default Helpers
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { features, Feature } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
 
5
5
  // ─── Schemas ────────────────────────────────────────────────────────────────
6
6
 
@@ -84,6 +84,7 @@ export class Ink extends Feature<InkState, InkOptions> {
84
84
  static override stateSchema = InkStateSchema
85
85
  static override optionsSchema = InkOptionsSchema
86
86
  static override eventsSchema = InkEventsSchema
87
+ static { Feature.register(this, 'ink') }
87
88
 
88
89
  private _instance: any | null = null
89
90
  private _inkModule: typeof import('ink') | null = null
@@ -481,8 +482,7 @@ export class Ink extends Feature<InkState, InkOptions> {
481
482
  }
482
483
  }
483
484
 
484
- export default features.register('ink', Ink)
485
-
485
+ export default Ink
486
486
  declare module '../../feature' {
487
487
  interface AvailableFeatures {
488
488
  ink: typeof Ink
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from "../feature.js";
3
+ import { Feature } from "../feature.js";
4
4
  import { NodeContainer } from "../container.js";
5
5
  import { Server, Socket } from "net";
6
6
 
@@ -84,6 +84,7 @@ export const IpcEventsSchema = FeatureEventsSchema.extend({
84
84
  * @extends {Feature<T>}
85
85
  */
86
86
  export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
87
+ static { Feature.register(this, 'ipcSocket') }
87
88
  /** The shortcut path for accessing this feature */
88
89
  static override shortcut = "features.ipcSocket" as const
89
90
  static override stateSchema = IpcStateSchema
@@ -106,7 +107,6 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
106
107
  * @returns The container for method chaining
107
108
  */
108
109
  static attach(container: NodeContainer & { ipcSocket?: IpcSocket }) {
109
- container.features.register("ipcSocket", IpcSocket);
110
110
  container.ipcSocket = container.feature("ipcSocket", { enable: true });
111
111
  }
112
112
 
@@ -456,4 +456,4 @@ export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
456
456
  }
457
457
  }
458
458
 
459
- export default features.register("ipcSocket", IpcSocket);
459
+ export default IpcSocket
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from "../feature.js";
4
- import { NodeContainer } from "../container.js";
3
+ import { Feature } from "../feature.js";
5
4
  import { camelCase, omit, set } from 'lodash-es';
6
5
 
7
6
  /**
@@ -52,29 +51,12 @@ export type JsonTreeState = z.infer<typeof JsonTreeStateSchema>
52
51
  * @extends {Feature<T>}
53
52
  */
54
53
  export class JsonTree<T extends JsonTreeState = JsonTreeState> extends Feature<T> {
54
+ static { Feature.register(this, 'jsonTree') }
55
55
  /** The shortcut path for accessing this feature */
56
56
  static override shortcut = "features.jsonTree" as const
57
57
  static override stateSchema = JsonTreeStateSchema
58
58
  static override optionsSchema = FeatureOptionsSchema
59
59
 
60
- /**
61
- * Attaches the JsonTree feature to a NodeContainer instance.
62
- * Registers the feature in the container's feature registry for later use.
63
- *
64
- * @param container - The NodeContainer to attach to
65
- * @returns The container for method chaining
66
- *
67
- * @example
68
- * ```typescript
69
- * // Typically called during container setup:
70
- * JsonTree.attach(container)
71
- * const jsonTree = container.feature('jsonTree')
72
- * ```
73
- */
74
- static attach(container: NodeContainer & { jsonTree?: JsonTree }) {
75
- container.features.register("jsonTree", JsonTree);
76
- }
77
-
78
60
  /**
79
61
  * Loads a tree of JSON files from the specified base path and stores them in state.
80
62
  *
@@ -185,4 +167,4 @@ export class JsonTree<T extends JsonTreeState = JsonTreeState> extends Feature<T
185
167
  }
186
168
  }
187
169
 
188
- export default features.register("jsonTree", JsonTree);
170
+ export default JsonTree
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { Server as NetServer, Socket } from 'net'
5
5
  import { homedir } from 'os'
6
6
  import { join, dirname } from 'path'
@@ -201,6 +201,7 @@ export class LauncherAppCommandListener extends Feature<LauncherAppCommandListen
201
201
  static override stateSchema = LauncherAppCommandListenerStateSchema
202
202
  static override optionsSchema = LauncherAppCommandListenerOptionsSchema
203
203
  static override eventsSchema = LauncherAppCommandListenerEventsSchema
204
+ static { Feature.register(this, 'launcherAppCommandListener') }
204
205
 
205
206
  private _server?: NetServer
206
207
  private _client?: ClientConnection
@@ -385,4 +386,4 @@ export class LauncherAppCommandListener extends Feature<LauncherAppCommandListen
385
386
  }
386
387
  }
387
388
 
388
- export default features.register('launcherAppCommandListener', LauncherAppCommandListener)
389
+ export default LauncherAppCommandListener
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import net from 'net'
3
3
  import detectPort from 'detect-port'
4
- import { Feature, features } from '../feature.js'
4
+ import { Feature } from '../feature.js'
5
5
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
6
6
 
7
7
  const MAX_CIDR_HOSTS = 65536
@@ -204,6 +204,7 @@ export class Networking extends Feature<NetworkingState, NetworkingOptions> {
204
204
  static override shortcut = 'features.networking' as const
205
205
  static override stateSchema = NetworkingStateSchema
206
206
  static override optionsSchema = NetworkingOptionsSchema
207
+ static { Feature.register(this, 'networking') }
207
208
 
208
209
  override get initialState(): NetworkingState {
209
210
  return {
@@ -922,4 +923,4 @@ export class Networking extends Feature<NetworkingState, NetworkingOptions> {
922
923
  }
923
924
  }
924
925
 
925
- export default features.register('networking', Networking)
926
+ export default Networking
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod'
2
- import { Feature, features } from '../feature.js'
2
+ import { Feature } from '../feature.js'
3
3
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
4
4
  import compromise from 'compromise'
5
5
  import winkNLP from 'wink-nlp'
@@ -65,6 +65,7 @@ export class NLP extends Feature<z.infer<typeof NLPStateSchema>, z.infer<typeof
65
65
  static override shortcut = 'features.nlp' as const
66
66
  static override stateSchema = NLPStateSchema
67
67
  static override optionsSchema = NLPOptionsSchema
68
+ static { Feature.register(this, 'nlp') }
68
69
 
69
70
  private _wink?: ReturnType<typeof winkNLP>
70
71
 
@@ -208,4 +209,4 @@ export class NLP extends Feature<z.infer<typeof NLPStateSchema>, z.infer<typeof
208
209
  }
209
210
  }
210
211
 
211
- export default features.register('nlp', NLP)
212
+ export default NLP
@@ -1,4 +1,4 @@
1
- import { features, Feature } from "../feature.js";
1
+ import { Feature } from "../feature.js";
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
 
4
4
  /**
@@ -35,6 +35,7 @@ export class Opener extends Feature {
35
35
  static override description = "Opens files, URLs, desktop apps, and code editors"
36
36
  static override stateSchema = FeatureStateSchema
37
37
  static override optionsSchema = FeatureOptionsSchema
38
+ static { Feature.register(this, 'opener') }
38
39
 
39
40
  /**
40
41
  * Opens a path or URL with the appropriate application.
@@ -47,7 +48,7 @@ export class Opener extends Feature {
47
48
  */
48
49
  async open(target: string): Promise<void> {
49
50
  const isUrl = /^https?:\/\//i.test(target)
50
- const platform = process.platform
51
+ const platform = this.container.os.platform
51
52
 
52
53
  if (isUrl) {
53
54
  console.log(`Opening ${target} in Chrome`)
@@ -57,7 +58,7 @@ export class Opener extends Feature {
57
58
  }
58
59
  }
59
60
 
60
- private async openInChrome(url: string, platform: string): Promise<void> {
61
+ private async openInChrome(url: string, platform: string = this.container.os.platform): Promise<void> {
61
62
  const proc = this.container.proc
62
63
 
63
64
  if (platform === 'darwin') {
@@ -88,7 +89,7 @@ export class Opener extends Feature {
88
89
  * @returns {Promise<void>}
89
90
  */
90
91
  async app(name: string): Promise<void> {
91
- const platform = process.platform
92
+ const platform = this.container.os.platform
92
93
  const proc = this.container.proc
93
94
 
94
95
  if (platform === 'darwin') {
@@ -114,7 +115,7 @@ export class Opener extends Feature {
114
115
  try {
115
116
  await proc.spawnAndCapture('code', [path])
116
117
  } catch {
117
- if (process.platform === 'darwin') {
118
+ if (this.container.os.platform === 'darwin') {
118
119
  await proc.spawnAndCapture('open', ['-a', 'Visual Studio Code', path])
119
120
  } else {
120
121
  throw new Error('VS Code CLI (code) not found. Install it from VS Code: Command Palette > "Shell Command: Install code command in PATH"')
@@ -136,7 +137,7 @@ export class Opener extends Feature {
136
137
  try {
137
138
  await proc.spawnAndCapture('cursor', [path])
138
139
  } catch {
139
- if (process.platform === 'darwin') {
140
+ if (this.container.os.platform === 'darwin') {
140
141
  await proc.spawnAndCapture('open', ['-a', 'Cursor', path])
141
142
  } else {
142
143
  throw new Error('Cursor CLI (cursor) not found. Install it from Cursor: Command Palette > "Shell Command: Install cursor command in PATH"')
@@ -163,4 +164,4 @@ declare module '../../feature' {
163
164
  }
164
165
  }
165
166
 
166
- export default features.register("opener", Opener)
167
+ export default Opener
@@ -1,4 +1,4 @@
1
- import { Feature, features } from '../feature.js'
1
+ import { Feature } from '../feature.js'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import os from 'os'
4
4
 
@@ -25,6 +25,7 @@ export class OS extends Feature {
25
25
  static override shortcut = 'features.os' as const
26
26
  static override stateSchema = FeatureStateSchema
27
27
  static override optionsSchema = FeatureOptionsSchema
28
+ static { Feature.register(this, 'os') }
28
29
 
29
30
  /**
30
31
  * Gets the operating system CPU architecture.
@@ -154,4 +155,4 @@ export class OS extends Feature {
154
155
  }
155
156
  }
156
157
 
157
- export default features.register('os', OS)
158
+ export default OS
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from "../feature.js";
3
+ import { Feature } from "../feature.js";
4
4
  import { readdir, readFile } from 'fs/promises'
5
5
  import { resolve, join, basename } from 'path'
6
6
  import { mapValues, pickBy, uniqBy } from 'lodash-es'
@@ -111,6 +111,7 @@ export class PackageFinder<
111
111
  T extends PackageFinderState = PackageFinderState,
112
112
  K extends PackageFinderOptions = PackageFinderOptions
113
113
  > extends Feature<T, K> {
114
+ static { Feature.register(this, 'packageFinder') }
114
115
 
115
116
  /** The shortcut path for accessing this feature */
116
117
  static override shortcut = "features.packageFinder" as const
@@ -536,4 +537,4 @@ export class PackageFinder<
536
537
 
537
538
  }
538
539
 
539
- export default features.register("packageFinder", PackageFinder);
540
+ export default PackageFinder
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import * as ngrok from '@ngrok/ngrok'
4
- import { Feature, features } from '../../feature.js'
4
+ import { Feature } from '../../feature.js'
5
5
 
6
6
  /**
7
7
  * Zod schema for the Port Exposer feature state
@@ -81,6 +81,7 @@ export class PortExposer extends Feature<PortExposerState, PortExposerOptions> {
81
81
  static override shortcut = 'portExposer' as const
82
82
  static override stateSchema = PortExposerStateSchema
83
83
  static override optionsSchema = PortExposerOptionsSchema
84
+ static { Feature.register(this, 'portExposer') }
84
85
 
85
86
  private ngrokListener?: ngrok.Listener
86
87
 
@@ -330,9 +331,7 @@ export class PortExposer extends Feature<PortExposerState, PortExposerOptions> {
330
331
  }
331
332
  }
332
333
 
333
- // Register the feature with the features registry
334
- export default features.register('portExposer', PortExposer)
335
-
334
+ export default PortExposer
336
335
  // Module augmentation for type safety
337
336
  declare module '../../feature.js' {
338
337
  interface AvailableFeatures {
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { SQL } from 'bun'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
5
5
  import type { ContainerContext } from '../../container.js'
6
6
 
@@ -46,6 +46,7 @@ export class Postgres extends Feature<PostgresState, PostgresOptions> {
46
46
  static override shortcut = 'features.postgres' as const
47
47
  static override stateSchema = PostgresStateSchema
48
48
  static override optionsSchema = PostgresOptionsSchema
49
+ static { Feature.register(this, 'postgres') }
49
50
 
50
51
  private _client: SQL
51
52
 
@@ -224,8 +225,7 @@ export class Postgres extends Feature<PostgresState, PostgresOptions> {
224
225
  }
225
226
  }
226
227
 
227
- export default features.register('postgres', Postgres)
228
-
228
+ export default Postgres
229
229
  declare module '../../feature.js' {
230
230
  interface AvailableFeatures {
231
231
  postgres: typeof Postgres
@@ -1,4 +1,4 @@
1
- import { features, Feature } from "../feature.js";
1
+ import { Feature } from "../feature.js";
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import { execSync, spawn as nodeSpawn } from "child_process";
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
@@ -69,6 +69,7 @@ export class ChildProcess extends Feature {
69
69
  static override shortcut = "features.proc" as const
70
70
  static override stateSchema = FeatureStateSchema
71
71
  static override optionsSchema = FeatureOptionsSchema
72
+ static { Feature.register(this, 'proc') }
72
73
 
73
74
  /**
74
75
  * Executes a command string and captures its output asynchronously.
@@ -182,6 +183,10 @@ export class ChildProcess extends Feature {
182
183
 
183
184
  const childProcess = proc.childProcess!;
184
185
 
186
+ if (typeof options?.onStart === 'function') {
187
+ options.onStart(childProcess)
188
+ }
189
+
185
190
  if (childProcess.stdout && childProcess.stderr) {
186
191
  childProcess.stdout.on("data", (buf: Buffer) => {
187
192
  stdout = stdout + buf.toString();
@@ -243,69 +248,7 @@ export class ChildProcess extends Feature {
243
248
  return child
244
249
  }
245
250
 
246
- /**
247
- * Runs a script file with Bun, inheriting stdout for full TTY passthrough
248
- * (animations, colors, cursor movement) while capturing stderr in a rolling buffer.
249
- *
250
- * @param {string} scriptPath - Absolute path to the script file
251
- * @param {object} [options] - Options
252
- * @param {string} [options.cwd] - Working directory
253
- * @param {number} [options.maxLines=100] - Max stderr lines to keep
254
- * @param {Record<string, string>} [options.env] - Extra environment variables
255
- * @returns {Promise<{ exitCode: number, stderr: string[] }>}
256
- *
257
- * @example
258
- * ```typescript
259
- * const { exitCode, stderr } = await proc.runScript('/path/to/script.ts')
260
- * if (exitCode !== 0) {
261
- * console.log('Error:', stderr.join('\n'))
262
- * }
263
- * ```
264
- */
265
- async runScript(
266
- scriptPath: string,
267
- options?: { cwd?: string; maxLines?: number; env?: Record<string, string> }
268
- ): Promise<{ exitCode: number; stderr: string[] }> {
269
- const cwd = options?.cwd ?? this.container.cwd
270
- const maxLines = options?.maxLines ?? 100
271
-
272
- const proc = Bun.spawn(['bun', 'run', scriptPath], {
273
- cwd,
274
- stdout: 'inherit',
275
- stderr: 'pipe',
276
- env: { ...process.env, ...options?.env },
277
- })
278
-
279
- const stderrLines: string[] = []
280
-
281
- const reader = proc.stderr.getReader()
282
- const decoder = new TextDecoder()
283
- let partial = ''
284
-
285
- while (true) {
286
- const { done, value } = await reader.read()
287
- if (done) break
288
- const text = partial + decoder.decode(value, { stream: true })
289
- const lines = text.split('\n')
290
- partial = lines.pop() || ''
291
-
292
- for (const line of lines) {
293
- process.stderr.write(line + '\n')
294
- stderrLines.push(line)
295
- if (stderrLines.length > maxLines) stderrLines.shift()
296
- }
297
- }
298
-
299
- if (partial) {
300
- process.stderr.write(partial + '\n')
301
- stderrLines.push(partial)
302
- if (stderrLines.length > maxLines) stderrLines.shift()
303
- }
304
-
305
- const exitCode = await proc.exited
306
251
 
307
- return { exitCode, stderr: stderrLines }
308
- }
309
252
 
310
253
  /**
311
254
  * Execute a command synchronously and return its output.
@@ -333,6 +276,10 @@ export class ChildProcess extends Feature {
333
276
  .trim();
334
277
  }
335
278
 
279
+ execSync(command: string, options?: any): string {
280
+ return this.exec(command,options)
281
+ }
282
+
336
283
  /**
337
284
  * Establishes a PID-file lock to prevent duplicate process instances.
338
285
  *
@@ -493,10 +440,36 @@ export class ChildProcess extends Feature {
493
440
  * off()
494
441
  * ```
495
442
  */
443
+ /**
444
+ * Checks whether any process matching a given name is currently running.
445
+ *
446
+ * Uses `pgrep -x` for an exact match against process names.
447
+ *
448
+ * @param {string} name - The process name to look for (e.g. 'afplay', 'node', 'nginx')
449
+ * @returns {boolean} True if at least one matching process is running
450
+ *
451
+ * @example
452
+ * ```typescript
453
+ * if (proc.isProcessRunning('afplay')) {
454
+ * console.log('Audio is currently playing')
455
+ * }
456
+ * ```
457
+ */
458
+ isProcessRunning(name: string): boolean {
459
+ try {
460
+ const output = execSync(`pgrep -x ${name}`, { stdio: ['pipe', 'pipe', 'pipe'] })
461
+ .toString()
462
+ .trim()
463
+ return output.length > 0
464
+ } catch {
465
+ return false
466
+ }
467
+ }
468
+
496
469
  onSignal(signal: NodeJS.Signals, handler: () => void): () => void {
497
470
  process.on(signal, handler)
498
471
  return () => process.removeListener(signal, handler)
499
472
  }
500
473
  }
501
474
 
502
- export default features.register("proc", ChildProcess);
475
+ export default ChildProcess
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { State } from '../../state.js'
5
5
  import { Bus, type EventMap } from '../../bus.js'
6
6
 
@@ -335,6 +335,7 @@ export class ProcessManager extends Feature {
335
335
  static override stateSchema = ProcessManagerStateSchema
336
336
  static override optionsSchema = ProcessManagerOptionsSchema
337
337
  static override eventsSchema = ProcessManagerEventsSchema
338
+ static { Feature.register(this, 'processManager') }
338
339
 
339
340
  private _handlers = new Map<string, SpawnHandler>()
340
341
  private _cleanupRegistered = false
@@ -539,4 +540,4 @@ export class ProcessManager extends Feature {
539
540
  }
540
541
  }
541
542
 
542
- export default features.register('processManager', ProcessManager)
543
+ export default ProcessManager
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { features, Feature } from "../feature.js";
3
+ import { Feature } from "../feature.js";
4
4
  import { existsSync } from 'fs';
5
5
  import { join, resolve } from 'path';
6
6
 
@@ -64,6 +64,7 @@ export class Python<
64
64
  static override shortcut = "features.python" as const
65
65
  static override stateSchema = PythonStateSchema
66
66
  static override optionsSchema = PythonOptionsSchema
67
+ static { Feature.register(this, 'python') }
67
68
 
68
69
  override get initialState(): T {
69
70
  return {
@@ -434,8 +435,7 @@ export class Python<
434
435
  }
435
436
  }
436
437
 
437
- export default features.register("python", Python);
438
-
438
+ export default Python
439
439
  // Module augmentation for type safety
440
440
  declare module '../feature.js' {
441
441
  interface AvailableFeatures {