@soederpop/luca 0.0.23 → 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/AGENTS.md +1 -1
- package/CLAUDE.md +6 -1
- package/assistants/codingAssistant/hooks.ts +0 -1
- package/assistants/lucaExpert/CORE.md +37 -0
- package/assistants/lucaExpert/hooks.ts +9 -0
- package/assistants/lucaExpert/tools.ts +177 -0
- package/commands/build-bootstrap.ts +41 -1
- package/docs/TABLE-OF-CONTENTS.md +0 -1
- package/docs/apis/clients/rest.md +5 -5
- package/docs/apis/features/agi/assistant.md +1 -1
- package/docs/apis/features/agi/conversation-history.md +6 -7
- package/docs/apis/features/agi/conversation.md +1 -1
- package/docs/apis/features/agi/semantic-search.md +1 -1
- package/docs/bootstrap/CLAUDE.md +1 -1
- package/docs/bootstrap/SKILL.md +7 -3
- package/docs/bootstrap/templates/luca-cli.ts +5 -0
- package/docs/mcp/readme.md +1 -1
- package/docs/tutorials/00-bootstrap.md +18 -0
- package/package.json +2 -2
- package/scripts/stamp-build.sh +12 -0
- package/scripts/test-docs-reader.ts +10 -0
- package/src/agi/container.server.ts +8 -5
- package/src/agi/features/assistant.ts +210 -55
- package/src/agi/features/assistants-manager.ts +138 -66
- package/src/agi/features/conversation.ts +46 -14
- package/src/agi/features/docs-reader.ts +166 -0
- package/src/agi/features/openapi.ts +1 -1
- package/src/agi/features/skills-library.ts +257 -313
- package/src/bootstrap/generated.ts +8163 -6
- package/src/cli/build-info.ts +4 -0
- package/src/cli/cli.ts +2 -1
- package/src/command.ts +75 -0
- package/src/commands/bootstrap.ts +16 -1
- package/src/commands/describe.ts +29 -1089
- package/src/commands/eval.ts +6 -1
- package/src/commands/sandbox-mcp.ts +17 -7
- package/src/container-describer.ts +1098 -0
- package/src/container.ts +11 -0
- package/src/helper.ts +56 -2
- package/src/introspection/generated.agi.ts +1684 -799
- package/src/introspection/generated.node.ts +964 -572
- package/src/introspection/generated.web.ts +9 -1
- package/src/node/container.ts +1 -1
- package/src/node/features/content-db.ts +268 -13
- package/src/node/features/fs.ts +18 -0
- package/src/node/features/git.ts +90 -0
- package/src/node/features/grep.ts +1 -1
- package/src/node/features/proc.ts +1 -0
- package/src/node/features/tts.ts +1 -1
- package/src/node/features/vm.ts +48 -0
- package/src/scaffolds/generated.ts +2 -2
- 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/assistants/architect/CORE.md +0 -3
- package/assistants/architect/hooks.ts +0 -3
- package/assistants/architect/tools.ts +0 -10
- package/docs/apis/features/agi/skills-library.md +0 -234
- package/docs/reports/assistant-bugs.md +0 -38
- package/docs/reports/attach-pattern-usage.md +0 -18
- package/docs/reports/code-audit-results.md +0 -391
- package/docs/reports/console-hmr-design.md +0 -170
- package/docs/reports/helper-semantic-search.md +0 -72
- package/docs/reports/introspection-audit-tasks.md +0 -378
- package/docs/reports/luca-mcp-improvements.md +0 -128
- package/test-integration/skills-library.test.ts +0 -157
package/src/server.ts
CHANGED
|
@@ -79,6 +79,44 @@ export class Server<T extends ServerState = ServerState, K extends ServerOptions
|
|
|
79
79
|
return super.container as NodeContainer
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/** Async functions passed to `.use()` before `start()` — drained in `start()`. */
|
|
83
|
+
_pendingPlugins: Promise<void>[] = []
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Register a setup function against this server. The function receives the
|
|
87
|
+
* server instance and can configure it however it likes.
|
|
88
|
+
*
|
|
89
|
+
* - If the server hasn't started yet, async return values are queued and
|
|
90
|
+
* awaited when `start()` is called.
|
|
91
|
+
* - If the server is already listening, the function is fire-and-forget.
|
|
92
|
+
* - Always returns `this` synchronously for chaining.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* server
|
|
97
|
+
* .use((s) => s.app.use(cors()))
|
|
98
|
+
* .use(async (s) => { await loadRoutes(s) })
|
|
99
|
+
* await server.start()
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
use(fn: (server: this) => void | Promise<void>): this {
|
|
103
|
+
const result = fn(this)
|
|
104
|
+
if (result && typeof (result as any).then === 'function') {
|
|
105
|
+
if (!this.isListening) {
|
|
106
|
+
this._pendingPlugins.push(result as Promise<void>)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return this
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Drain all pending `.use()` promises. Subclasses should call this at the top of `start()`. */
|
|
113
|
+
protected async _drainPendingPlugins() {
|
|
114
|
+
if (this._pendingPlugins.length) {
|
|
115
|
+
await Promise.all(this._pendingPlugins)
|
|
116
|
+
this._pendingPlugins = []
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
82
120
|
get isListening() {
|
|
83
121
|
return !!this.state.get('listening')
|
|
84
122
|
}
|
|
@@ -117,6 +155,8 @@ export class Server<T extends ServerState = ServerState, K extends ServerOptions
|
|
|
117
155
|
return this
|
|
118
156
|
}
|
|
119
157
|
|
|
158
|
+
await this._drainPendingPlugins()
|
|
159
|
+
|
|
120
160
|
if (options?.port) {
|
|
121
161
|
this.state.set('port', options.port)
|
|
122
162
|
}
|
package/src/servers/express.ts
CHANGED
|
@@ -104,6 +104,8 @@ export class ExpressServer<T extends ServerState = ServerState, K extends Expres
|
|
|
104
104
|
return this
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
await this._drainPendingPlugins()
|
|
108
|
+
|
|
107
109
|
// Apply runtime port to state so this.port reflects the override
|
|
108
110
|
if (options?.port) {
|
|
109
111
|
this.state.set('port', options.port)
|
package/src/servers/mcp.ts
CHANGED
|
@@ -472,6 +472,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
|
|
|
472
472
|
stdioCompat?: StdioCompatMode
|
|
473
473
|
}) {
|
|
474
474
|
if (this.isListening) return this
|
|
475
|
+
await this._drainPendingPlugins()
|
|
475
476
|
if (!this.isConfigured) await this.configure()
|
|
476
477
|
|
|
477
478
|
const transport = options?.transport || this.options.transport || 'stdio'
|
package/src/servers/socket.ts
CHANGED
|
@@ -89,6 +89,8 @@ export class WebsocketServer<T extends ServerState = ServerState, K extends Sock
|
|
|
89
89
|
return this
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
await this._drainPendingPlugins()
|
|
93
|
+
|
|
92
94
|
// Apply runtime port to state before configure/wss touches it
|
|
93
95
|
if (options?.port) {
|
|
94
96
|
this.state.set('port', options.port)
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
# SkillsLibrary (features.skillsLibrary)
|
|
2
|
-
|
|
3
|
-
Manages two contentbase collections of skills following the Claude Code SKILL.md format. Project-level skills live in .claude/skills/ and user-level skills live in ~/.luca/skills/. Skills can be discovered, searched, created, updated, and removed at runtime.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
container.feature('skillsLibrary', {
|
|
9
|
-
// Path to project-level skills directory
|
|
10
|
-
projectSkillsPath,
|
|
11
|
-
// Path to user-level global skills directory
|
|
12
|
-
userSkillsPath,
|
|
13
|
-
})
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Options (Zod v4 schema)
|
|
17
|
-
|
|
18
|
-
| Property | Type | Description |
|
|
19
|
-
|----------|------|-------------|
|
|
20
|
-
| `projectSkillsPath` | `string` | Path to project-level skills directory |
|
|
21
|
-
| `userSkillsPath` | `string` | Path to user-level global skills directory |
|
|
22
|
-
|
|
23
|
-
## Methods
|
|
24
|
-
|
|
25
|
-
### load
|
|
26
|
-
|
|
27
|
-
Loads both project and user skill collections from disk. Gracefully handles missing directories.
|
|
28
|
-
|
|
29
|
-
**Returns:** `Promise<SkillsLibrary>`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### list
|
|
34
|
-
|
|
35
|
-
Lists all skills from both collections. Project skills come first.
|
|
36
|
-
|
|
37
|
-
**Returns:** `SkillEntry[]`
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
### find
|
|
42
|
-
|
|
43
|
-
Finds a skill by name. Project skills take precedence over user skills.
|
|
44
|
-
|
|
45
|
-
**Parameters:**
|
|
46
|
-
|
|
47
|
-
| Name | Type | Required | Description |
|
|
48
|
-
|------|------|----------|-------------|
|
|
49
|
-
| `name` | `string` | ✓ | The skill name to find (case-insensitive) |
|
|
50
|
-
|
|
51
|
-
**Returns:** `SkillEntry | undefined`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
### search
|
|
56
|
-
|
|
57
|
-
Searches skills by substring match against name and description.
|
|
58
|
-
|
|
59
|
-
**Parameters:**
|
|
60
|
-
|
|
61
|
-
| Name | Type | Required | Description |
|
|
62
|
-
|------|------|----------|-------------|
|
|
63
|
-
| `query` | `string` | ✓ | The search query |
|
|
64
|
-
|
|
65
|
-
**Returns:** `SkillEntry[]`
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
### getSkill
|
|
70
|
-
|
|
71
|
-
Gets a skill by name. Alias for find().
|
|
72
|
-
|
|
73
|
-
**Parameters:**
|
|
74
|
-
|
|
75
|
-
| Name | Type | Required | Description |
|
|
76
|
-
|------|------|----------|-------------|
|
|
77
|
-
| `name` | `string` | ✓ | The skill name |
|
|
78
|
-
|
|
79
|
-
**Returns:** `SkillEntry | undefined`
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
### create
|
|
84
|
-
|
|
85
|
-
Creates a new SKILL.md file in the specified collection. Maintains the directory-per-skill structure (skill-name/SKILL.md).
|
|
86
|
-
|
|
87
|
-
**Parameters:**
|
|
88
|
-
|
|
89
|
-
| Name | Type | Required | Description |
|
|
90
|
-
|------|------|----------|-------------|
|
|
91
|
-
| `skill` | `{
|
|
92
|
-
name: string
|
|
93
|
-
description: string
|
|
94
|
-
body: string
|
|
95
|
-
meta?: Record<string, unknown>
|
|
96
|
-
}` | ✓ | The skill to create |
|
|
97
|
-
| `target` | `'project' | 'user'` | | Which collection to write to (default: 'project') |
|
|
98
|
-
|
|
99
|
-
**Returns:** `Promise<SkillEntry>`
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
### update
|
|
104
|
-
|
|
105
|
-
Updates an existing skill's content or metadata.
|
|
106
|
-
|
|
107
|
-
**Parameters:**
|
|
108
|
-
|
|
109
|
-
| Name | Type | Required | Description |
|
|
110
|
-
|------|------|----------|-------------|
|
|
111
|
-
| `name` | `string` | ✓ | The skill name to update |
|
|
112
|
-
| `updates` | `{
|
|
113
|
-
description?: string
|
|
114
|
-
body?: string
|
|
115
|
-
meta?: Record<string, unknown>
|
|
116
|
-
}` | ✓ | Fields to update |
|
|
117
|
-
|
|
118
|
-
**Returns:** `Promise<SkillEntry>`
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
### remove
|
|
123
|
-
|
|
124
|
-
Removes a skill by name, deleting its SKILL.md and cleaning up the directory.
|
|
125
|
-
|
|
126
|
-
**Parameters:**
|
|
127
|
-
|
|
128
|
-
| Name | Type | Required | Description |
|
|
129
|
-
|------|------|----------|-------------|
|
|
130
|
-
| `name` | `string` | ✓ | The skill name to remove |
|
|
131
|
-
|
|
132
|
-
**Returns:** `Promise<boolean>`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
### toConversationTools
|
|
137
|
-
|
|
138
|
-
Converts all skills into ConversationTool format for use with Conversation. Each skill becomes a tool that returns its instruction body when invoked.
|
|
139
|
-
|
|
140
|
-
**Returns:** `Record<string, ConversationTool>`
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
### toSystemPromptBlock
|
|
145
|
-
|
|
146
|
-
Generates a markdown block listing all available skills with names and descriptions. Suitable for injecting into a system prompt.
|
|
147
|
-
|
|
148
|
-
**Returns:** `string`
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
## Getters
|
|
153
|
-
|
|
154
|
-
| Property | Type | Description |
|
|
155
|
-
|----------|------|-------------|
|
|
156
|
-
| `projectCollection` | `Collection` | Returns the project-level contentbase Collection, lazily initialized. |
|
|
157
|
-
| `userCollection` | `Collection` | Returns the user-level contentbase Collection, lazily initialized. |
|
|
158
|
-
| `isLoaded` | `boolean` | Whether the skills library has been loaded. |
|
|
159
|
-
| `skillNames` | `string[]` | Array of all skill names across both collections. |
|
|
160
|
-
|
|
161
|
-
## Events (Zod v4 schema)
|
|
162
|
-
|
|
163
|
-
### loaded
|
|
164
|
-
|
|
165
|
-
Fired after both project and user skill collections are loaded
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
### skillCreated
|
|
170
|
-
|
|
171
|
-
Fired after a new skill is written to disk
|
|
172
|
-
|
|
173
|
-
**Event Arguments:**
|
|
174
|
-
|
|
175
|
-
| Name | Type | Description |
|
|
176
|
-
|------|------|-------------|
|
|
177
|
-
| `arg0` | `any` | The created SkillEntry object |
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
### skillUpdated
|
|
182
|
-
|
|
183
|
-
Fired after an existing skill is updated
|
|
184
|
-
|
|
185
|
-
**Event Arguments:**
|
|
186
|
-
|
|
187
|
-
| Name | Type | Description |
|
|
188
|
-
|------|------|-------------|
|
|
189
|
-
| `arg0` | `any` | The updated SkillEntry object |
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
### skillRemoved
|
|
194
|
-
|
|
195
|
-
Fired after a skill is deleted
|
|
196
|
-
|
|
197
|
-
**Event Arguments:**
|
|
198
|
-
|
|
199
|
-
| Name | Type | Description |
|
|
200
|
-
|------|------|-------------|
|
|
201
|
-
| `arg0` | `string` | The name of the removed skill |
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
## State (Zod v4 schema)
|
|
206
|
-
|
|
207
|
-
| Property | Type | Description |
|
|
208
|
-
|----------|------|-------------|
|
|
209
|
-
| `enabled` | `boolean` | Whether this feature is currently enabled |
|
|
210
|
-
| `loaded` | `boolean` | Whether both collections have been loaded |
|
|
211
|
-
| `projectSkillCount` | `number` | Number of skills in the project collection |
|
|
212
|
-
| `userSkillCount` | `number` | Number of skills in the user-level collection |
|
|
213
|
-
| `totalSkillCount` | `number` | Total number of skills across both collections |
|
|
214
|
-
|
|
215
|
-
## Examples
|
|
216
|
-
|
|
217
|
-
**features.skillsLibrary**
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
const skills = container.feature('skillsLibrary')
|
|
221
|
-
await skills.load()
|
|
222
|
-
|
|
223
|
-
// List and search
|
|
224
|
-
const allSkills = skills.list()
|
|
225
|
-
const matches = skills.search('code review')
|
|
226
|
-
|
|
227
|
-
// Create a new skill
|
|
228
|
-
await skills.create({
|
|
229
|
-
name: 'summarize',
|
|
230
|
-
description: 'Summarize a document',
|
|
231
|
-
body: '## Instructions\nRead the document and produce a concise summary.'
|
|
232
|
-
})
|
|
233
|
-
```
|
|
234
|
-
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Assistant systemPrompt Bug Fix Verification
|
|
2
|
-
|
|
3
|
-
The bug: class field initializers (`private _systemPrompt: string = ''`) on Feature subclasses
|
|
4
|
-
overwrite values set during `afterInitialize()` because ES2022 field initializers run AFTER `super()` returns.
|
|
5
|
-
|
|
6
|
-
Fix: use `declare private _systemPrompt: string` which emits no JavaScript, so afterInitialize values persist.
|
|
7
|
-
|
|
8
|
-
## Test
|
|
9
|
-
|
|
10
|
-
```ts
|
|
11
|
-
const assistant = container.feature('assistant', {
|
|
12
|
-
folder: '/Users/jon/@soederpop/commands/voice/assistant'
|
|
13
|
-
})
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
systemPrompt should contain the CORE.md content (not be empty):
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
console.log("systemPrompt length:", assistant.systemPrompt.length)
|
|
20
|
-
console.log("contains omar:", assistant.systemPrompt.toLowerCase().includes("omar"))
|
|
21
|
-
console.log("first 80 chars:", assistant.systemPrompt.slice(0, 80))
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Tools should have been loaded from tools.ts:
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
console.log("tools loaded:", Object.keys(assistant.tools))
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Starting the assistant should pass the systemPrompt into the conversation:
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
await assistant.start()
|
|
34
|
-
const systemMsg = assistant.conversation.messages[0]
|
|
35
|
-
console.log("conversation has system message:", systemMsg.role === "system")
|
|
36
|
-
console.log("system message has content:", systemMsg.content.length > 0)
|
|
37
|
-
console.log("system message contains omar:", systemMsg.content.toLowerCase().includes("omar"))
|
|
38
|
-
```
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Attach Pattern
|
|
2
|
-
|
|
3
|
-
Here is some problematic code:
|
|
4
|
-
|
|
5
|
-
```ts skip
|
|
6
|
-
const { container } = context
|
|
7
|
-
const { VoiceRouter } = await import(resolve(import.meta.dir, 'voice/router.ts'))
|
|
8
|
-
container.features.register('voiceRouter', VoiceRouter as any)
|
|
9
|
-
const router = container.feature('voiceRouter' as any, { enable: true }) as InstanceType<typeof VoiceRouter>
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
I'd rather see
|
|
13
|
-
|
|
14
|
-
```ts skip
|
|
15
|
-
// we know since we're inside a command this has to be our path anyway, relative to the container's cwd is how we're discovered
|
|
16
|
-
await import(container.paths.resolve('commands','voice','router.ts')).then(({ VoiceRouter }) => container.use(VoiceRouter))
|
|
17
|
-
```
|
|
18
|
-
|