@soederpop/luca 0.0.25 → 0.0.26
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/package.json +1 -1
- package/src/agi/features/assistant.ts +14 -12
- package/src/agi/features/docs-reader.ts +25 -1
- package/src/bootstrap/generated.ts +1 -1
- package/src/cli/build-info.ts +2 -2
- package/src/command.ts +75 -0
- package/src/commands/describe.ts +29 -1089
- package/src/container-describer.ts +1098 -0
- package/src/container.ts +11 -0
- package/src/introspection/generated.agi.ts +513 -429
- package/src/introspection/generated.node.ts +503 -419
- package/src/introspection/generated.web.ts +9 -1
- package/src/node/features/content-db.ts +17 -0
- package/src/node/features/fs.ts +18 -0
- package/src/scaffolds/generated.ts +1 -1
- package/src/server.ts +40 -0
- package/src/servers/express.ts +2 -0
- package/src/servers/mcp.ts +1 -0
- package/src/servers/socket.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soederpop/luca",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"website": "https://luca.soederpop.com",
|
|
5
5
|
"description": "lightweight universal conversational architecture AKA Le Ultimate Component Architecture AKA Last Universal Common Ancestor, part AI part Human",
|
|
6
6
|
"author": "jon soeder aka the people's champ <jon@soederpop.com>",
|
|
@@ -278,28 +278,30 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
|
|
|
278
278
|
* .use(container.feature('git'))
|
|
279
279
|
* ```
|
|
280
280
|
*/
|
|
281
|
-
use(fnOrHelper: ((assistant: this) => void | Promise<void>) | { toTools: () => { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> } }): this {
|
|
281
|
+
use(fnOrHelper: ((assistant: this) => void | Promise<void>) | { toTools: () => { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> } } | { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> }): this {
|
|
282
282
|
if (typeof fnOrHelper === 'function') {
|
|
283
283
|
const result = fnOrHelper(this)
|
|
284
284
|
if (result && typeof (result as any).then === 'function') {
|
|
285
285
|
const pending = this.state.get('pendingPlugins') as Promise<void>[]
|
|
286
286
|
this.state.set('pendingPlugins', [...pending, result as Promise<void>])
|
|
287
287
|
}
|
|
288
|
-
} else if (fnOrHelper && typeof fnOrHelper.toTools === 'function') {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
this.addTool(
|
|
293
|
-
name,
|
|
294
|
-
handlers[name] as any,
|
|
295
|
-
schemas[name],
|
|
296
|
-
)
|
|
297
|
-
}
|
|
298
|
-
}
|
|
288
|
+
} else if (fnOrHelper && typeof (fnOrHelper as any).toTools === 'function') {
|
|
289
|
+
this._registerTools((fnOrHelper as any).toTools())
|
|
290
|
+
} else if (fnOrHelper && 'schemas' in fnOrHelper && 'handlers' in fnOrHelper) {
|
|
291
|
+
this._registerTools(fnOrHelper as { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> })
|
|
299
292
|
}
|
|
300
293
|
return this
|
|
301
294
|
}
|
|
302
295
|
|
|
296
|
+
/** Register tools from a `{ schemas, handlers }` object. */
|
|
297
|
+
private _registerTools({ schemas, handlers }: { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> }) {
|
|
298
|
+
for (const name of Object.keys(schemas)) {
|
|
299
|
+
if (typeof handlers[name] === 'function') {
|
|
300
|
+
this.addTool(name, handlers[name] as any, schemas[name])
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
303
305
|
/**
|
|
304
306
|
* Add a tool to this assistant. The tool name is derived from the
|
|
305
307
|
* handler's function name.
|
|
@@ -108,15 +108,39 @@ export class DocsReader extends Feature<DocsReaderState, DocsReaderOptions> {
|
|
|
108
108
|
|
|
109
109
|
assistant?: Assistant
|
|
110
110
|
|
|
111
|
+
|
|
112
|
+
private generateSpecificCollectionExplainer() {
|
|
113
|
+
const { contentDb } = this
|
|
114
|
+
const fileTree = contentDb.fileTree
|
|
115
|
+
const modelDefinitionTable = contentDb.modelDefinitionTable
|
|
116
|
+
const modelNames = contentDb.modelNames
|
|
117
|
+
|
|
118
|
+
const domainTermSummary = this.container.feature('ui').endent(`
|
|
119
|
+
## Domain Specific Terms
|
|
120
|
+
|
|
121
|
+
When the user is referring to one of the following nouns: ${modelNames.join(', ')} they are likely
|
|
122
|
+
referencing one of the documents defined by the following content models:
|
|
123
|
+
|
|
124
|
+
${Object.entries(modelDefinitionTable).map(([name, { description, glob, routePatterns }]) => `
|
|
125
|
+
- **${name}**: ${description}
|
|
126
|
+
Glob: ${glob}
|
|
127
|
+
Route Patterns: ${routePatterns.join(', ')}
|
|
128
|
+
`).join('\n')}
|
|
129
|
+
`)
|
|
130
|
+
|
|
131
|
+
return domainTermSummary
|
|
132
|
+
}
|
|
133
|
+
|
|
111
134
|
/** Start the docs reader by loading the contentDb and wiring its tools into an assistant. */
|
|
112
135
|
async start(): Promise<DocsReader> {
|
|
113
136
|
if (this.isStarted) return this
|
|
114
137
|
|
|
115
138
|
const contentDb = this.contentDb
|
|
116
139
|
if (!contentDb.isLoaded) await contentDb.load()
|
|
140
|
+
|
|
117
141
|
|
|
118
142
|
this.assistant = this.container.feature('assistant', {
|
|
119
|
-
systemPrompt: CONTENT_DB_SYSTEM_PROMPT,
|
|
143
|
+
systemPrompt: [CONTENT_DB_SYSTEM_PROMPT, this.generateSpecificCollectionExplainer()].filter(Boolean).join('\n\n'),
|
|
120
144
|
model: this.options.model,
|
|
121
145
|
local: this.options.local,
|
|
122
146
|
}).use(contentDb)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Auto-generated bootstrap content
|
|
2
|
-
// Generated at: 2026-03-
|
|
2
|
+
// Generated at: 2026-03-22T20:57:24.866Z
|
|
3
3
|
// Source: docs/bootstrap/*.md, docs/bootstrap/templates/*, docs/examples/*.md, docs/tutorials/*.md
|
|
4
4
|
//
|
|
5
5
|
// Do not edit manually. Run: luca build-bootstrap
|
package/src/cli/build-info.ts
CHANGED
package/src/command.ts
CHANGED
|
@@ -266,6 +266,81 @@ export class CommandsRegistry extends Registry<Command<any>> {
|
|
|
266
266
|
override scope = 'commands'
|
|
267
267
|
override baseClass = Command as any
|
|
268
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Convert all registered commands into a `{ schemas, handlers }` object
|
|
271
|
+
* compatible with `assistant.use()`.
|
|
272
|
+
*
|
|
273
|
+
* Each command becomes a tool whose parameters come from the command's
|
|
274
|
+
* `argsSchema` (with internal CLI fields stripped) and whose handler
|
|
275
|
+
* dispatches the command headlessly, returning `{ exitCode, stdout, stderr }`.
|
|
276
|
+
*
|
|
277
|
+
* @param container - The container used to instantiate and dispatch commands
|
|
278
|
+
* @param options - Optional filter/transform options
|
|
279
|
+
* @param options.include - Only include these command names (default: all)
|
|
280
|
+
* @param options.exclude - Exclude these command names (default: none)
|
|
281
|
+
* @param options.prefix - Prefix tool names (e.g. 'luca_' → 'luca_eval')
|
|
282
|
+
*/
|
|
283
|
+
toTools(
|
|
284
|
+
container: Container<any> & CommandsInterface,
|
|
285
|
+
options?: { include?: string[], exclude?: string[], prefix?: string },
|
|
286
|
+
): { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> } {
|
|
287
|
+
const schemas: Record<string, z.ZodType> = {}
|
|
288
|
+
const handlers: Record<string, Function> = {}
|
|
289
|
+
const prefix = options?.prefix ?? ''
|
|
290
|
+
const includeSet = options?.include ? new Set(options.include) : null
|
|
291
|
+
const excludeSet = new Set(options?.exclude ?? [])
|
|
292
|
+
|
|
293
|
+
// Internal fields inherited from HelperOptionsSchema / CommandOptionsSchema
|
|
294
|
+
const internalFields = ['_', 'dispatchSource', 'name', '_cacheKey']
|
|
295
|
+
|
|
296
|
+
for (const name of this.available) {
|
|
297
|
+
if (excludeSet.has(name)) continue
|
|
298
|
+
if (includeSet && !includeSet.has(name)) continue
|
|
299
|
+
|
|
300
|
+
const Cmd = this.lookup(name) as typeof Command
|
|
301
|
+
const rawSchema = Cmd.argsSchema
|
|
302
|
+
const description = Cmd.commandDescription || Cmd.description || name
|
|
303
|
+
|
|
304
|
+
// Build a clean schema by stripping internal CLI fields from the argsSchema.
|
|
305
|
+
// If the schema is a ZodObject we can use .omit(), otherwise create a
|
|
306
|
+
// virtual passthrough schema so the tool still flows through.
|
|
307
|
+
let toolSchema: z.ZodType
|
|
308
|
+
try {
|
|
309
|
+
const shape = typeof (rawSchema as any)?._def?.shape === 'function'
|
|
310
|
+
? (rawSchema as any)._def.shape()
|
|
311
|
+
: (rawSchema as any)?._def?.shape
|
|
312
|
+
|
|
313
|
+
if (shape) {
|
|
314
|
+
// Build a new object schema omitting internal fields
|
|
315
|
+
const cleanShape: Record<string, z.ZodType> = {}
|
|
316
|
+
for (const [key, val] of Object.entries(shape)) {
|
|
317
|
+
if (internalFields.includes(key)) continue
|
|
318
|
+
cleanShape[key] = val as z.ZodType
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
toolSchema = Object.keys(cleanShape).length > 0
|
|
322
|
+
? z.object(cleanShape).describe(description)
|
|
323
|
+
: z.object({}).describe(description)
|
|
324
|
+
} else {
|
|
325
|
+
// Not a ZodObject — wrap as passthrough
|
|
326
|
+
toolSchema = z.object({}).describe(description)
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
toolSchema = z.object({}).describe(description)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const toolName = `${prefix}${name}`
|
|
333
|
+
schemas[toolName] = toolSchema
|
|
334
|
+
handlers[toolName] = async (args: Record<string, any>) => {
|
|
335
|
+
const cmd = container.command(name as any)
|
|
336
|
+
const result = await cmd.dispatch(args ?? {}, 'headless')
|
|
337
|
+
return result ?? { exitCode: 0, stdout: '', stderr: '' }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return { schemas, handlers }
|
|
342
|
+
}
|
|
343
|
+
|
|
269
344
|
/**
|
|
270
345
|
* Internal: register a command from a handler function.
|
|
271
346
|
* Used by built-in commands. External commands should use the module export pattern.
|