@orxataguy/tyr 1.0.30 → 1.0.32
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/README.md +49 -10
- package/package.json +1 -1
- package/src/core/Container.ts +4 -1
- package/src/core/Kernel.ts +2 -0
- package/src/core/sys/chat.ts +73 -0
- package/src/core/sys/help.ts +1 -0
- package/src/index.ts +1 -0
- package/src/lib/AIContextManager.ts +1004 -27
- package/src/lib/AIVendorManager.ts +474 -25
- package/src/lib/ChatManager.ts +880 -0
- package/src/lib/PromptTemplateManager.ts +112 -1
- package/src/lib/TokenManager.ts +30 -2
package/README.md
CHANGED
|
@@ -173,6 +173,35 @@ tyr doc
|
|
|
173
173
|
# → Open http://localhost:3000
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
+
### `tyr chat [directory] [--port <n>] [--split <0-1>]`
|
|
177
|
+
|
|
178
|
+
Opens a local web app with a chat pane and a file browser for `directory` (defaults to the current directory). You can attach images to a message, drag the divider between the two panes to resize them, and click any file in the browser to preview it. Messages are answered by the configured AI vendor (see `AIVendorManager`, below) by default.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
tyr chat ./my-project --split 0.35
|
|
182
|
+
# → Chat ready at: http://localhost:4646
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Under the hood this is powered by `ChatManager`, injected into every command as `chat`. Write your own command to customise it:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
export default ({ chat, aiVendor, logger }: TyrContext) => {
|
|
189
|
+
return async (args: string[]) => {
|
|
190
|
+
chat.onMessage(async ({ message, history, dir }) => {
|
|
191
|
+
// Decide how to answer — call AIVendorManager, a local model, a script, etc.
|
|
192
|
+
const result = await aiVendor.complete([{ role: 'user', content: message.text }]);
|
|
193
|
+
return result.content;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
chat.on('message:send', ({ message }) => logger.info(`User: ${message.text}`));
|
|
197
|
+
chat.on('message:response', ({ message }) => logger.info(`Assistant: ${message.text}`));
|
|
198
|
+
|
|
199
|
+
const session = await chat.open(args[0] ?? process.cwd(), { splitRatio: 0.4 });
|
|
200
|
+
logger.success(`Chat ready at: ${session.url}`);
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
```
|
|
204
|
+
|
|
176
205
|
---
|
|
177
206
|
|
|
178
207
|
## Context API Reference
|
|
@@ -252,6 +281,8 @@ tyr deploy --debug
|
|
|
252
281
|
│ │ ├── Kernel.ts # Command router and execution engine
|
|
253
282
|
│ │ ├── Container.ts # Dependency injection container
|
|
254
283
|
│ │ ├── TyrError.ts # Structured error type
|
|
284
|
+
│ │ ├── util
|
|
285
|
+
│ │ │ └── getenv.ts # Helper: get environment variables
|
|
255
286
|
│ │ └── sys/
|
|
256
287
|
│ │ ├── gen.ts # Built-in: scaffold a command
|
|
257
288
|
│ │ ├── rem.ts # Built-in: remove a command
|
|
@@ -259,16 +290,24 @@ tyr deploy --debug
|
|
|
259
290
|
│ ├── commands/
|
|
260
291
|
│ │ └── *.tyr.ts # Your custom commands go here
|
|
261
292
|
│ └── lib/
|
|
262
|
-
│
|
|
263
|
-
│
|
|
264
|
-
│
|
|
265
|
-
│
|
|
266
|
-
│
|
|
267
|
-
│
|
|
268
|
-
│
|
|
269
|
-
│
|
|
270
|
-
├──
|
|
271
|
-
│
|
|
293
|
+
│ │ ├── AIContextManager.ts
|
|
294
|
+
│ │ ├── AIVendorManager.ts
|
|
295
|
+
│ │ ├── ChatManager.ts
|
|
296
|
+
│ │ ├── DockerManager.ts
|
|
297
|
+
│ │ ├── FileSystemManager.ts
|
|
298
|
+
│ │ ├── GitManager.ts
|
|
299
|
+
│ │ ├── JiraManager.ts
|
|
300
|
+
│ │ ├── MongoManager.ts
|
|
301
|
+
│ │ ├── PackageManager.ts
|
|
302
|
+
│ │ ├── PromptTemplateManager.ts
|
|
303
|
+
│ │ ├── SetupManager.ts
|
|
304
|
+
│ │ ├── ShellManager.ts
|
|
305
|
+
│ │ ├── SQLManager.ts
|
|
306
|
+
│ │ ├── SystemManager.ts
|
|
307
|
+
│ │ ├── TokenManager.ts
|
|
308
|
+
│ │ ├── WebManager.ts
|
|
309
|
+
│ │ └── WorkspaceManager.ts
|
|
310
|
+
| └── index.ts # Exports context
|
|
272
311
|
└── tests/
|
|
273
312
|
├── setup.ts # Mock context factory
|
|
274
313
|
└── test-runner.ts # Smoke test runner
|
package/package.json
CHANGED
package/src/core/Container.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { AIVendorManager } from '../lib/AIVendorManager.js';
|
|
|
14
14
|
import { AIContextManager } from '../lib/AIContextManager.js';
|
|
15
15
|
import { PromptTemplateManager } from '../lib/PromptTemplateManager.js';
|
|
16
16
|
import { TokenManager } from '../lib/TokenManager.js';
|
|
17
|
+
import { ChatManager } from '../lib/ChatManager.js';
|
|
17
18
|
import { Logger, createLogger } from './Logger.js';
|
|
18
19
|
|
|
19
20
|
import path from 'path';
|
|
@@ -39,6 +40,7 @@ export interface ServiceContainer {
|
|
|
39
40
|
aiContext: AIContextManager;
|
|
40
41
|
prompts: PromptTemplateManager;
|
|
41
42
|
tokens: TokenManager;
|
|
43
|
+
chat: ChatManager;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export class Container {
|
|
@@ -56,7 +58,7 @@ export class Container {
|
|
|
56
58
|
const web = new WebManager(logger);
|
|
57
59
|
const fs = new FileSystemManager(logger);
|
|
58
60
|
const aiVendor = new AIVendorManager(logger);
|
|
59
|
-
const aiContext = new AIContextManager(fs, aiVendor, logger);
|
|
61
|
+
const aiContext = new AIContextManager(fs, shell, aiVendor, logger);
|
|
60
62
|
|
|
61
63
|
this.services = {
|
|
62
64
|
logger,
|
|
@@ -77,6 +79,7 @@ export class Container {
|
|
|
77
79
|
aiContext,
|
|
78
80
|
prompts: new PromptTemplateManager(aiContext, logger),
|
|
79
81
|
tokens: new TokenManager(logger),
|
|
82
|
+
chat: new ChatManager(fs, logger),
|
|
80
83
|
};
|
|
81
84
|
}
|
|
82
85
|
|
package/src/core/Kernel.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { Container } from './Container';
|
|
|
9
9
|
import gen from './sys/gen';
|
|
10
10
|
import rem from './sys/rem';
|
|
11
11
|
import doc from './sys/doc';
|
|
12
|
+
import chat from './sys/chat';
|
|
12
13
|
import config from './sys/config';
|
|
13
14
|
import help from './sys/help';
|
|
14
15
|
|
|
@@ -166,6 +167,7 @@ export class Kernel {
|
|
|
166
167
|
gen,
|
|
167
168
|
rem,
|
|
168
169
|
doc,
|
|
170
|
+
chat,
|
|
169
171
|
};
|
|
170
172
|
|
|
171
173
|
if (systemCommands[commandName]) {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { TyrContext } from '../Kernel';
|
|
4
|
+
import type { AIContentBlock, AIMessage } from '../../lib/AIVendorManager';
|
|
5
|
+
import type { ChatMessageContext } from '../../lib/ChatManager';
|
|
6
|
+
|
|
7
|
+
function parseFlag(args: string[], name: string): string | undefined {
|
|
8
|
+
const index = args.indexOf(name);
|
|
9
|
+
return index !== -1 ? args[index + 1] : undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function chat({ logger, chat: chatManager, aiVendor, fail }: TyrContext) {
|
|
13
|
+
return async (args: string[]) => {
|
|
14
|
+
const positional = args.filter((a) => !a.startsWith('--'));
|
|
15
|
+
const dir = path.resolve(positional[0] ?? process.cwd());
|
|
16
|
+
|
|
17
|
+
const portArg = parseFlag(args, '--port');
|
|
18
|
+
const splitArg = parseFlag(args, '--split');
|
|
19
|
+
const port = portArg ? parseInt(portArg, 10) : undefined;
|
|
20
|
+
const splitRatio = splitArg ? parseFloat(splitArg) : undefined;
|
|
21
|
+
|
|
22
|
+
// Default responder: forwards the conversation (plus any attached images) to the
|
|
23
|
+
// configured AI vendor. Replace with your own chatManager.onMessage(...) in a custom
|
|
24
|
+
// command if you want different behaviour.
|
|
25
|
+
chatManager.onMessage(async ({ message, history, dir: chatDir }: ChatMessageContext) => {
|
|
26
|
+
const priorTurns: AIMessage[] = history.slice(0, -1).map((m) => ({
|
|
27
|
+
role: m.role === 'user' ? 'user' : 'assistant',
|
|
28
|
+
content: m.text,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const contentBlocks: AIContentBlock[] = [];
|
|
32
|
+
if (message.text) contentBlocks.push({ type: 'text', text: message.text });
|
|
33
|
+
|
|
34
|
+
for (const attachment of message.attachments) {
|
|
35
|
+
try {
|
|
36
|
+
const fileBuffer = await readFile(attachment.path);
|
|
37
|
+
contentBlocks.push({ type: 'image', mediaType: attachment.mimeType, data: fileBuffer.toString('base64') });
|
|
38
|
+
} catch (e) {
|
|
39
|
+
logger.warn(`Could not read attachment '${attachment.filename}': ${(e as Error).message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const messages: AIMessage[] = [
|
|
44
|
+
{
|
|
45
|
+
role: 'system',
|
|
46
|
+
content: `You are an assistant embedded in a chat UI browsing the directory: ${chatDir}. Answer helpfully and concisely, referencing its files when relevant.`,
|
|
47
|
+
},
|
|
48
|
+
...priorTurns,
|
|
49
|
+
{ role: 'user', content: contentBlocks.length > 0 ? contentBlocks : message.text },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const result = await aiVendor.complete(messages);
|
|
53
|
+
return result.content;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Example hooks — side effects around the conversation, independent from onMessage.
|
|
57
|
+
chatManager.on('message:send', ({ message }: { message: { text: string } }) => {
|
|
58
|
+
logger.info(`[chat] user: ${message.text}`);
|
|
59
|
+
});
|
|
60
|
+
chatManager.on('message:error', ({ error }: { error: Error }) => {
|
|
61
|
+
logger.warn(`[chat] handler failed: ${error.message}`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const session = await chatManager.open(dir, { port, splitRatio });
|
|
66
|
+
logger.success(`Chat ready at: ${session.url}`);
|
|
67
|
+
logger.info(`Browsing: ${session.dir}`);
|
|
68
|
+
logger.info('Press Ctrl+C to stop.');
|
|
69
|
+
} catch (e: any) {
|
|
70
|
+
fail(`Could not start chat: ${e.message}`, 'Check that the directory exists and the port is free.');
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
package/src/core/sys/help.ts
CHANGED
|
@@ -76,6 +76,7 @@ export default function help({ userRoot }: TyrContext) {
|
|
|
76
76
|
{ name: '--upgrade', description: 'Upgrades the tyr npm package.', usage: 'tyr --upgrade' },
|
|
77
77
|
{ name: 'gen', description: 'Generates a new command from a description using AI.', usage: 'tyr gen <name> "<description>"' },
|
|
78
78
|
{ name: 'doc', description: 'Opens the framework documentation in the browser.', usage: 'tyr doc' },
|
|
79
|
+
{ name: 'chat', description: 'Opens an AI chat + file browser for a directory.', usage: 'tyr chat [directory] [--port <n>] [--split <0-1>]' },
|
|
79
80
|
];
|
|
80
81
|
|
|
81
82
|
console.log(` ${bold}${yellow}Framework${reset}`);
|
package/src/index.ts
CHANGED