@tyvm/knowhow 0.0.83 → 0.0.84
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 +4 -2
- package/src/agents/base/base.ts +72 -62
- package/src/agents/index.ts +30 -14
- package/src/agents/tools/startAgentTask.ts +3 -1
- package/src/chat/CliChatService.ts +20 -4
- package/src/chat/modules/AgentModule.ts +399 -357
- package/src/chat/modules/CustomCommandsModule.ts +0 -1
- package/src/chat/modules/InternalChatModule.ts +18 -2
- package/src/chat/modules/RendererModule.ts +109 -0
- package/src/chat/modules/SessionsModule.ts +854 -0
- package/src/chat/modules/SetupModule.ts +6 -8
- package/src/chat/modules/index.ts +1 -0
- package/src/chat/renderer/CompactRenderer.ts +209 -0
- package/src/chat/renderer/ConsoleRenderer.ts +141 -0
- package/src/chat/renderer/FancyRenderer.ts +421 -0
- package/src/chat/renderer/index.ts +5 -0
- package/src/chat/renderer/loadRenderer.ts +314 -0
- package/src/chat/renderer/messagesToRenderEvents.ts +96 -0
- package/src/chat/renderer/types.ts +88 -0
- package/src/chat/types.ts +5 -0
- package/src/chat.ts +69 -5
- package/src/cli.ts +24 -5
- package/src/config.ts +15 -0
- package/src/plugins/AgentsMdPlugin.ts +1 -1
- package/src/plugins/GitPlugin.ts +20 -20
- package/src/plugins/PluginBase.ts +11 -0
- package/src/plugins/SkillsPlugin.ts +150 -0
- package/src/plugins/asana.ts +4 -4
- package/src/plugins/embedding.ts +3 -5
- package/src/plugins/exec.ts +3 -3
- package/src/plugins/figma.ts +3 -7
- package/src/plugins/github.ts +18 -29
- package/src/plugins/jira.ts +2 -2
- package/src/plugins/language.ts +4 -4
- package/src/plugins/linear.ts +4 -4
- package/src/plugins/notion.ts +6 -8
- package/src/plugins/plugins.ts +29 -3
- package/src/plugins/url.ts +2 -2
- package/src/plugins/vim.ts +4 -3
- package/src/services/AgentService.ts +17 -0
- package/src/services/AgentSyncFs.ts +3 -0
- package/src/services/EventService.ts +168 -27
- package/src/services/KnowhowClient.ts +1 -0
- package/src/services/SessionManager.ts +51 -1
- package/src/services/SyncedAgentWatcher.ts +397 -0
- package/src/services/SyncerService.ts +147 -0
- package/src/services/index.ts +2 -0
- package/src/services/modules/index.ts +14 -3
- package/src/types.ts +25 -0
- package/src/worker.ts +80 -2
- package/src/workers/auth/PasskeySetup.ts +185 -0
- package/src/workers/auth/WorkerPasskeyAuth.ts +190 -0
- package/src/workers/auth/types.ts +58 -0
- package/src/workers/tools/getChallenge.ts +33 -0
- package/src/workers/tools/index.ts +8 -0
- package/src/workers/tools/lock.ts +31 -0
- package/src/workers/tools/unlock.ts +116 -0
- package/tests/unit/modules/moduleLoading.test.ts +226 -0
- package/tests/unit/plugins/pluginLoading.test.ts +151 -0
- package/ts_build/package.json +4 -2
- package/ts_build/src/agents/base/base.d.ts +4 -3
- package/ts_build/src/agents/base/base.js +54 -30
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/index.d.ts +3 -0
- package/ts_build/src/agents/index.js +21 -11
- package/ts_build/src/agents/index.js.map +1 -1
- package/ts_build/src/agents/tools/startAgentTask.js +2 -1
- package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
- package/ts_build/src/chat/CliChatService.js +16 -5
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +34 -17
- package/ts_build/src/chat/modules/AgentModule.js +248 -258
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/CustomCommandsModule.js.map +1 -1
- package/ts_build/src/chat/modules/InternalChatModule.d.ts +3 -0
- package/ts_build/src/chat/modules/InternalChatModule.js +16 -1
- package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
- package/ts_build/src/chat/modules/RendererModule.d.ts +16 -0
- package/ts_build/src/chat/modules/RendererModule.js +76 -0
- package/ts_build/src/chat/modules/RendererModule.js.map +1 -0
- package/ts_build/src/chat/modules/SessionsModule.d.ts +33 -0
- package/ts_build/src/chat/modules/SessionsModule.js +582 -0
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -0
- package/ts_build/src/chat/modules/SetupModule.d.ts +3 -3
- package/ts_build/src/chat/modules/SetupModule.js +4 -6
- package/ts_build/src/chat/modules/SetupModule.js.map +1 -1
- package/ts_build/src/chat/modules/index.d.ts +1 -0
- package/ts_build/src/chat/modules/index.js +3 -1
- package/ts_build/src/chat/modules/index.js.map +1 -1
- package/ts_build/src/chat/renderer/CompactRenderer.d.ts +23 -0
- package/ts_build/src/chat/renderer/CompactRenderer.js +167 -0
- package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -0
- package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +22 -0
- package/ts_build/src/chat/renderer/ConsoleRenderer.js +110 -0
- package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -0
- package/ts_build/src/chat/renderer/FancyRenderer.d.ts +23 -0
- package/ts_build/src/chat/renderer/FancyRenderer.js +328 -0
- package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -0
- package/ts_build/src/chat/renderer/index.d.ts +5 -0
- package/ts_build/src/chat/renderer/index.js +29 -0
- package/ts_build/src/chat/renderer/index.js.map +1 -0
- package/ts_build/src/chat/renderer/loadRenderer.d.ts +4 -0
- package/ts_build/src/chat/renderer/loadRenderer.js +246 -0
- package/ts_build/src/chat/renderer/loadRenderer.js.map +1 -0
- package/ts_build/src/chat/renderer/messagesToRenderEvents.d.ts +15 -0
- package/ts_build/src/chat/renderer/messagesToRenderEvents.js +72 -0
- package/ts_build/src/chat/renderer/messagesToRenderEvents.js.map +1 -0
- package/ts_build/src/chat/renderer/types.d.ts +75 -0
- package/ts_build/src/chat/renderer/types.js +3 -0
- package/ts_build/src/chat/renderer/types.js.map +1 -0
- package/ts_build/src/chat/types.d.ts +5 -0
- package/ts_build/src/chat.js +46 -4
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.js +18 -5
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/config.d.ts +1 -0
- package/ts_build/src/config.js +17 -1
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/plugins/AgentsMdPlugin.js +1 -1
- package/ts_build/src/plugins/AgentsMdPlugin.js.map +1 -1
- package/ts_build/src/plugins/GitPlugin.js +20 -20
- package/ts_build/src/plugins/GitPlugin.js.map +1 -1
- package/ts_build/src/plugins/PluginBase.d.ts +1 -0
- package/ts_build/src/plugins/PluginBase.js +13 -0
- package/ts_build/src/plugins/PluginBase.js.map +1 -1
- package/ts_build/src/plugins/SkillsPlugin.d.ts +13 -0
- package/ts_build/src/plugins/SkillsPlugin.js +149 -0
- package/ts_build/src/plugins/SkillsPlugin.js.map +1 -0
- package/ts_build/src/plugins/asana.js +4 -4
- package/ts_build/src/plugins/asana.js.map +1 -1
- package/ts_build/src/plugins/embedding.js +3 -3
- package/ts_build/src/plugins/embedding.js.map +1 -1
- package/ts_build/src/plugins/exec.js +3 -3
- package/ts_build/src/plugins/exec.js.map +1 -1
- package/ts_build/src/plugins/figma.js +3 -3
- package/ts_build/src/plugins/figma.js.map +1 -1
- package/ts_build/src/plugins/github.js +18 -18
- package/ts_build/src/plugins/github.js.map +1 -1
- package/ts_build/src/plugins/jira.js +2 -2
- package/ts_build/src/plugins/jira.js.map +1 -1
- package/ts_build/src/plugins/language.js +4 -4
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/plugins/linear.js +4 -4
- package/ts_build/src/plugins/linear.js.map +1 -1
- package/ts_build/src/plugins/notion.js +6 -6
- package/ts_build/src/plugins/notion.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +3 -0
- package/ts_build/src/plugins/plugins.js +18 -3
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/url.js +2 -2
- package/ts_build/src/plugins/url.js.map +1 -1
- package/ts_build/src/plugins/vim.js +2 -2
- package/ts_build/src/plugins/vim.js.map +1 -1
- package/ts_build/src/services/AgentService.d.ts +3 -0
- package/ts_build/src/services/AgentService.js +7 -0
- package/ts_build/src/services/AgentService.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.d.ts +1 -0
- package/ts_build/src/services/AgentSyncFs.js +2 -0
- package/ts_build/src/services/AgentSyncFs.js.map +1 -1
- package/ts_build/src/services/EventService.d.ts +25 -2
- package/ts_build/src/services/EventService.js +92 -14
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.d.ts +1 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/SessionManager.d.ts +6 -0
- package/ts_build/src/services/SessionManager.js +39 -1
- package/ts_build/src/services/SessionManager.js.map +1 -1
- package/ts_build/src/services/SyncedAgentWatcher.d.ts +101 -0
- package/ts_build/src/services/SyncedAgentWatcher.js +312 -0
- package/ts_build/src/services/SyncedAgentWatcher.js.map +1 -0
- package/ts_build/src/services/SyncerService.d.ts +30 -0
- package/ts_build/src/services/SyncerService.js +72 -0
- package/ts_build/src/services/SyncerService.js.map +1 -0
- package/ts_build/src/services/index.d.ts +2 -0
- package/ts_build/src/services/index.js +2 -0
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/modules/index.js +10 -2
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/types.d.ts +19 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/worker.d.ts +2 -0
- package/ts_build/src/worker.js +59 -4
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/src/workers/auth/PasskeySetup.d.ts +10 -0
- package/ts_build/src/workers/auth/PasskeySetup.js +131 -0
- package/ts_build/src/workers/auth/PasskeySetup.js.map +1 -0
- package/ts_build/src/workers/auth/WorkerPasskeyAuth.d.ts +35 -0
- package/ts_build/src/workers/auth/WorkerPasskeyAuth.js +129 -0
- package/ts_build/src/workers/auth/WorkerPasskeyAuth.js.map +1 -0
- package/ts_build/src/workers/auth/types.d.ts +36 -0
- package/ts_build/src/workers/auth/types.js +3 -0
- package/ts_build/src/workers/auth/types.js.map +1 -0
- package/ts_build/src/workers/tools/getChallenge.d.ts +9 -0
- package/ts_build/src/workers/tools/getChallenge.js +27 -0
- package/ts_build/src/workers/tools/getChallenge.js.map +1 -0
- package/ts_build/src/workers/tools/index.d.ts +6 -0
- package/ts_build/src/workers/tools/index.js +10 -0
- package/ts_build/src/workers/tools/index.js.map +1 -1
- package/ts_build/src/workers/tools/lock.d.ts +9 -0
- package/ts_build/src/workers/tools/lock.js +27 -0
- package/ts_build/src/workers/tools/lock.js.map +1 -0
- package/ts_build/src/workers/tools/unlock.d.ts +18 -0
- package/ts_build/src/workers/tools/unlock.js +78 -0
- package/ts_build/src/workers/tools/unlock.js.map +1 -0
- package/ts_build/tests/unit/modules/moduleLoading.test.d.ts +1 -0
- package/ts_build/tests/unit/modules/moduleLoading.test.js +187 -0
- package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -0
- package/ts_build/tests/unit/plugins/pluginLoading.test.d.ts +1 -0
- package/ts_build/tests/unit/plugins/pluginLoading.test.js +123 -0
- package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderer Loader - Dynamically loads a custom renderer from an npm package or local file.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - built-in names: "basic" | "fancy" | "compact"
|
|
6
|
+
* - npm packages: "@scope/package" or "my-renderer-package"
|
|
7
|
+
* - local JS files: "./my-renderer.js" or "/absolute/path/renderer.js"
|
|
8
|
+
* - local TS files: "./my-renderer.ts" (compiled on-the-fly via tsx/ts-node if available)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from "path";
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import { AgentRenderer } from "./types";
|
|
14
|
+
import { ConsoleRenderer } from "./ConsoleRenderer";
|
|
15
|
+
import { FancyRenderer } from "./FancyRenderer";
|
|
16
|
+
import { CompactRenderer } from "./CompactRenderer";
|
|
17
|
+
|
|
18
|
+
/** Built-in renderer names that ship with knowhow */
|
|
19
|
+
const BUILTIN_RENDERERS: Record<string, () => AgentRenderer> = {
|
|
20
|
+
basic: () => new ConsoleRenderer(),
|
|
21
|
+
fancy: () => new FancyRenderer(),
|
|
22
|
+
compact: () => new CompactRenderer(),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Attempt to transpile + load a TypeScript file as a module.
|
|
27
|
+
* Falls back gracefully if tsx / ts-node are unavailable.
|
|
28
|
+
*/
|
|
29
|
+
async function loadTsFile(filePath: string): Promise<any> {
|
|
30
|
+
// Try using jiti (another popular TS loader)
|
|
31
|
+
try {
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
33
|
+
const jiti = require("jiti");
|
|
34
|
+
const loader = typeof jiti === "function" ? jiti : jiti.default;
|
|
35
|
+
return loader(filePath, { interopDefault: true })(filePath);
|
|
36
|
+
} catch (_) {}
|
|
37
|
+
|
|
38
|
+
// Try ts-node
|
|
39
|
+
try {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
41
|
+
require("ts-node/register");
|
|
42
|
+
return require(filePath); // eslint-disable-line @typescript-eslint/no-var-requires
|
|
43
|
+
} catch (_) {}
|
|
44
|
+
|
|
45
|
+
// Last resort: try esbuild-register
|
|
46
|
+
try {
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
48
|
+
const { register } = require("esbuild-register/dist/node");
|
|
49
|
+
const { unregister } = register();
|
|
50
|
+
const mod = require(filePath);
|
|
51
|
+
unregister();
|
|
52
|
+
return mod;
|
|
53
|
+
} catch (_) {}
|
|
54
|
+
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Cannot load TypeScript file "${filePath}". Install tsx, jiti, ts-node, or esbuild-register:\n` +
|
|
57
|
+
` npm install -g tsx\n or npm install -g ts-node`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load a renderer from a specifier string.
|
|
63
|
+
*
|
|
64
|
+
* The loaded module must export either:
|
|
65
|
+
* - A default export that is an AgentRenderer instance, OR
|
|
66
|
+
* - A named export `createRenderer` function that returns an AgentRenderer, OR
|
|
67
|
+
* - A named export `renderer` that is an AgentRenderer instance
|
|
68
|
+
*
|
|
69
|
+
* @param specifier - npm package name or file path (relative paths resolved from cwd)
|
|
70
|
+
* @returns An AgentRenderer instance
|
|
71
|
+
*/
|
|
72
|
+
export async function loadRenderer(specifier: string): Promise<AgentRenderer> {
|
|
73
|
+
// Check for built-in renderer names first
|
|
74
|
+
const builtin = BUILTIN_RENDERERS[specifier.toLowerCase()];
|
|
75
|
+
if (builtin) {
|
|
76
|
+
return builtin();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let mod: any;
|
|
80
|
+
|
|
81
|
+
const isLocalPath =
|
|
82
|
+
specifier.startsWith("./") ||
|
|
83
|
+
specifier.startsWith("../") ||
|
|
84
|
+
specifier.startsWith("/");
|
|
85
|
+
|
|
86
|
+
if (isLocalPath) {
|
|
87
|
+
const resolved = path.resolve(process.cwd(), specifier);
|
|
88
|
+
|
|
89
|
+
if (!fs.existsSync(resolved)) {
|
|
90
|
+
throw new Error(`Renderer file not found: ${resolved}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (resolved.endsWith(".ts")) {
|
|
94
|
+
mod = await loadTsFile(resolved);
|
|
95
|
+
} else {
|
|
96
|
+
// JS or JSON
|
|
97
|
+
mod = await import(resolved);
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
// npm package
|
|
101
|
+
try {
|
|
102
|
+
mod = await import(specifier);
|
|
103
|
+
} catch (err: any) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Failed to load renderer package "${specifier}": ${err.message}\n` +
|
|
106
|
+
`Make sure it is installed: npm install -g ${specifier}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Resolve the renderer from the module exports
|
|
112
|
+
if (mod?.default && typeof mod.default.render === "function") {
|
|
113
|
+
return mod.default as AgentRenderer;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (typeof mod?.createRenderer === "function") {
|
|
117
|
+
return mod.createRenderer() as AgentRenderer;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (mod?.renderer && typeof mod.renderer.render === "function") {
|
|
121
|
+
return mod.renderer as AgentRenderer;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check the default export might be a class
|
|
125
|
+
if (mod?.default && typeof mod.default === "function") {
|
|
126
|
+
try {
|
|
127
|
+
const instance = new mod.default();
|
|
128
|
+
if (typeof instance.render === "function") {
|
|
129
|
+
return instance as AgentRenderer;
|
|
130
|
+
}
|
|
131
|
+
} catch (_) {}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Check named exports for a class with "Renderer" in name
|
|
135
|
+
for (const key of Object.keys(mod || {})) {
|
|
136
|
+
if (key.toLowerCase().includes("renderer") && typeof mod[key] === "function") {
|
|
137
|
+
try {
|
|
138
|
+
const instance = new mod[key]();
|
|
139
|
+
if (typeof instance.render === "function") {
|
|
140
|
+
return instance as AgentRenderer;
|
|
141
|
+
}
|
|
142
|
+
} catch (_) {}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error(
|
|
147
|
+
`Renderer module "${specifier}" does not export a valid AgentRenderer.\n` +
|
|
148
|
+
`Expected one of:\n` +
|
|
149
|
+
` - default export: AgentRenderer instance\n` +
|
|
150
|
+
` - default export: AgentRenderer class\n` +
|
|
151
|
+
` - named export "createRenderer": () => AgentRenderer\n` +
|
|
152
|
+
` - named export "renderer": AgentRenderer instance\n` +
|
|
153
|
+
` - named export "SomethingRenderer": AgentRenderer class`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Load a root chat module from a specifier string.
|
|
159
|
+
*
|
|
160
|
+
* The module must export either:
|
|
161
|
+
* - A default export that is a ChatModule class or instance
|
|
162
|
+
* - A named export "createModule" function
|
|
163
|
+
* - A named export with "Module" in the name
|
|
164
|
+
*/
|
|
165
|
+
export async function loadRootModule(specifier: string): Promise<any> {
|
|
166
|
+
let mod: any;
|
|
167
|
+
|
|
168
|
+
const isLocalPath =
|
|
169
|
+
specifier.startsWith("./") ||
|
|
170
|
+
specifier.startsWith("../") ||
|
|
171
|
+
specifier.startsWith("/");
|
|
172
|
+
|
|
173
|
+
if (isLocalPath) {
|
|
174
|
+
const resolved = path.resolve(process.cwd(), specifier);
|
|
175
|
+
|
|
176
|
+
if (!fs.existsSync(resolved)) {
|
|
177
|
+
throw new Error(`Root module file not found: ${resolved}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (resolved.endsWith(".ts")) {
|
|
181
|
+
mod = await loadTsFile(resolved);
|
|
182
|
+
} else {
|
|
183
|
+
mod = await import(resolved);
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
try {
|
|
187
|
+
mod = await import(specifier);
|
|
188
|
+
} catch (err: any) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Failed to load root module package "${specifier}": ${err.message}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Try default export as instance
|
|
196
|
+
if (mod?.default && typeof mod.default.initialize === "function") {
|
|
197
|
+
return mod.default;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Try default export as class
|
|
201
|
+
if (mod?.default && typeof mod.default === "function") {
|
|
202
|
+
try {
|
|
203
|
+
const instance = new mod.default();
|
|
204
|
+
if (typeof instance.initialize === "function") {
|
|
205
|
+
return instance;
|
|
206
|
+
}
|
|
207
|
+
} catch (_) {}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Try createModule factory
|
|
211
|
+
if (typeof mod?.createModule === "function") {
|
|
212
|
+
return mod.createModule();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Try named exports with "Module" in name
|
|
216
|
+
for (const key of Object.keys(mod || {})) {
|
|
217
|
+
if (key.toLowerCase().includes("module") && typeof mod[key] === "function") {
|
|
218
|
+
try {
|
|
219
|
+
const instance = new mod[key]();
|
|
220
|
+
if (typeof instance.initialize === "function") {
|
|
221
|
+
return instance;
|
|
222
|
+
}
|
|
223
|
+
} catch (_) {}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Root module "${specifier}" does not export a valid ChatModule.\n` +
|
|
229
|
+
`Expected a class or instance with an "initialize(chatService)" method.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Load an additional chat module (not the root module) from a specifier string.
|
|
235
|
+
*
|
|
236
|
+
* The module must export either:
|
|
237
|
+
* - A default export that is a ChatModule class or instance
|
|
238
|
+
* - A named export "createModule" function that returns a ChatModule
|
|
239
|
+
* - A named export with "Module" in the name (class or instance)
|
|
240
|
+
*
|
|
241
|
+
* @param specifier - npm package name or file path (relative paths resolved from cwd)
|
|
242
|
+
* @returns An instantiated ChatModule (not yet initialized)
|
|
243
|
+
*/
|
|
244
|
+
export async function loadChatModule(specifier: string): Promise<any> {
|
|
245
|
+
let mod: any;
|
|
246
|
+
|
|
247
|
+
const isLocalPath =
|
|
248
|
+
specifier.startsWith("./") ||
|
|
249
|
+
specifier.startsWith("../") ||
|
|
250
|
+
specifier.startsWith("/");
|
|
251
|
+
|
|
252
|
+
if (isLocalPath) {
|
|
253
|
+
const resolved = path.resolve(process.cwd(), specifier);
|
|
254
|
+
|
|
255
|
+
if (!fs.existsSync(resolved)) {
|
|
256
|
+
throw new Error(`Chat module file not found: ${resolved}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (resolved.endsWith(".ts")) {
|
|
260
|
+
mod = await loadTsFile(resolved);
|
|
261
|
+
} else {
|
|
262
|
+
mod = await import(resolved);
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
try {
|
|
266
|
+
mod = await import(specifier);
|
|
267
|
+
} catch (err: any) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Failed to load chat module package "${specifier}": ${err.message}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Try default export as instance (already has initialize)
|
|
275
|
+
if (mod?.default && typeof mod.default.initialize === "function") {
|
|
276
|
+
return mod.default;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Try default export as class
|
|
280
|
+
if (mod?.default && typeof mod.default === "function") {
|
|
281
|
+
try {
|
|
282
|
+
const instance = new mod.default();
|
|
283
|
+
if (typeof instance.initialize === "function") {
|
|
284
|
+
return instance;
|
|
285
|
+
}
|
|
286
|
+
} catch (_) {}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Try createModule factory
|
|
290
|
+
if (typeof mod?.createModule === "function") {
|
|
291
|
+
return mod.createModule();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Try named exports with "Module" in name
|
|
295
|
+
for (const key of Object.keys(mod || {})) {
|
|
296
|
+
if (key.toLowerCase().includes("module") && typeof mod[key] === "function") {
|
|
297
|
+
try {
|
|
298
|
+
const instance = new mod[key]();
|
|
299
|
+
if (typeof instance.initialize === "function") {
|
|
300
|
+
return instance;
|
|
301
|
+
}
|
|
302
|
+
} catch (_) {}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
throw new Error(
|
|
307
|
+
`Chat module "${specifier}" does not export a valid ChatModule.\n` +
|
|
308
|
+
`Expected a class or instance with an "initialize(chatService)" method.\n` +
|
|
309
|
+
`Supported export patterns:\n` +
|
|
310
|
+
` - default export: ChatModule instance or class\n` +
|
|
311
|
+
` - named export "createModule": () => ChatModule\n` +
|
|
312
|
+
` - named export "SomethingModule": ChatModule class`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility: Convert OpenAI-format Message[] threads into RenderEvent[] for the renderer.
|
|
3
|
+
* Shared by SyncedAgentWatcher implementations and the /logs command.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { RenderEvent } from "../../chat/renderer/types";
|
|
7
|
+
|
|
8
|
+
export interface Message {
|
|
9
|
+
role: "assistant" | "user" | "tool" | "system";
|
|
10
|
+
content?: any;
|
|
11
|
+
tool_calls?: Array<{
|
|
12
|
+
id: string;
|
|
13
|
+
function: {
|
|
14
|
+
name: string;
|
|
15
|
+
arguments: string;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
name?: string;
|
|
19
|
+
tool_call_id?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function messagesToRenderEvents(
|
|
23
|
+
messages: Message[],
|
|
24
|
+
taskId: string,
|
|
25
|
+
agentName: string
|
|
26
|
+
): RenderEvent[] {
|
|
27
|
+
const events: RenderEvent[] = [];
|
|
28
|
+
|
|
29
|
+
for (const msg of messages) {
|
|
30
|
+
if (msg.role === "assistant") {
|
|
31
|
+
if (typeof msg.content === "string" && msg.content) {
|
|
32
|
+
events.push({
|
|
33
|
+
type: "agentMessage",
|
|
34
|
+
taskId,
|
|
35
|
+
agentName,
|
|
36
|
+
message: msg.content,
|
|
37
|
+
role: "assistant",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (msg.tool_calls) {
|
|
41
|
+
for (const tc of msg.tool_calls) {
|
|
42
|
+
events.push({
|
|
43
|
+
type: "toolCall",
|
|
44
|
+
taskId,
|
|
45
|
+
agentName,
|
|
46
|
+
toolCall: {
|
|
47
|
+
id: tc.id,
|
|
48
|
+
function: {
|
|
49
|
+
name: tc.function.name,
|
|
50
|
+
arguments: tc.function.arguments,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} else if (msg.role === "tool") {
|
|
57
|
+
const content =
|
|
58
|
+
typeof msg.content === "string"
|
|
59
|
+
? msg.content
|
|
60
|
+
: JSON.stringify(msg.content);
|
|
61
|
+
events.push({
|
|
62
|
+
type: "toolResult",
|
|
63
|
+
taskId,
|
|
64
|
+
agentName,
|
|
65
|
+
toolCall: { function: { name: msg.name || "unknown", arguments: "" } },
|
|
66
|
+
result: content,
|
|
67
|
+
});
|
|
68
|
+
} else if (msg.role === "user") {
|
|
69
|
+
const content =
|
|
70
|
+
typeof msg.content === "string"
|
|
71
|
+
? msg.content
|
|
72
|
+
: Array.isArray(msg.content)
|
|
73
|
+
? msg.content
|
|
74
|
+
.filter((c: any) => c.type === "text")
|
|
75
|
+
.map((c: any) => c.text)
|
|
76
|
+
.join("\n")
|
|
77
|
+
: String(msg.content ?? "");
|
|
78
|
+
// Skip workflow messages — these are internal agent control messages
|
|
79
|
+
// injected as user-role messages and should not be rendered to the user
|
|
80
|
+
if (content.trim().startsWith("<Workflow>") || /<Workflow>/i.test(content)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (content) {
|
|
84
|
+
events.push({
|
|
85
|
+
type: "agentMessage",
|
|
86
|
+
taskId,
|
|
87
|
+
agentName,
|
|
88
|
+
message: content,
|
|
89
|
+
role: "user",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return events;
|
|
96
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderer interfaces for CLI agent output
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface LogEvent {
|
|
6
|
+
taskId: string;
|
|
7
|
+
agentName: string;
|
|
8
|
+
message: string;
|
|
9
|
+
level: "info" | "warn" | "error";
|
|
10
|
+
timestamp: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ToolCall {
|
|
14
|
+
id?: string;
|
|
15
|
+
function: {
|
|
16
|
+
name: string;
|
|
17
|
+
arguments: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ToolCallEvent {
|
|
22
|
+
taskId: string;
|
|
23
|
+
agentName: string;
|
|
24
|
+
toolCall: ToolCall;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ToolResultEvent {
|
|
28
|
+
taskId: string;
|
|
29
|
+
agentName: string;
|
|
30
|
+
toolCall: ToolCall;
|
|
31
|
+
result: any;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AgentMessageEvent {
|
|
35
|
+
taskId: string;
|
|
36
|
+
agentName: string;
|
|
37
|
+
message: string;
|
|
38
|
+
role: "assistant" | "user";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AgentDoneEvent {
|
|
42
|
+
taskId: string;
|
|
43
|
+
agentName: string;
|
|
44
|
+
output: string;
|
|
45
|
+
totalCost: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface AgentStatusEvent {
|
|
49
|
+
taskId: string;
|
|
50
|
+
agentName: string;
|
|
51
|
+
statusMessage: string;
|
|
52
|
+
details: {
|
|
53
|
+
totalCostUsd: number;
|
|
54
|
+
elapsedMs: number;
|
|
55
|
+
remainingTimeMs?: number;
|
|
56
|
+
remainingTurns?: number;
|
|
57
|
+
remainingBudget?: number;
|
|
58
|
+
};
|
|
59
|
+
timestamp: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type RenderEvent =
|
|
63
|
+
| ({ type: "log" } & LogEvent)
|
|
64
|
+
| ({ type: "agentStatus" } & AgentStatusEvent)
|
|
65
|
+
| ({ type: "toolCall" } & ToolCallEvent)
|
|
66
|
+
| ({ type: "toolResult" } & ToolResultEvent)
|
|
67
|
+
| ({ type: "agentMessage" } & AgentMessageEvent)
|
|
68
|
+
| ({ type: "agentDone" } & AgentDoneEvent);
|
|
69
|
+
|
|
70
|
+
export interface AgentRenderer {
|
|
71
|
+
onLog(handler: (event: LogEvent) => void): void;
|
|
72
|
+
onAgentStatus(handler: (event: AgentStatusEvent) => void): void;
|
|
73
|
+
onToolCall(handler: (event: ToolCallEvent) => void): void;
|
|
74
|
+
onToolResult(handler: (event: ToolResultEvent) => void): void;
|
|
75
|
+
onAgentMessage(handler: (event: AgentMessageEvent) => void): void;
|
|
76
|
+
onAgentDone(handler: (event: AgentDoneEvent) => void): void;
|
|
77
|
+
render(event: RenderEvent): void;
|
|
78
|
+
|
|
79
|
+
/** Set the currently active agent task ID - only events for this task are shown */
|
|
80
|
+
setActiveTaskId(taskId: string | undefined): void;
|
|
81
|
+
getActiveTaskId(): string | undefined;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Replay the last N render events (used by /logs command).
|
|
85
|
+
* If count is not provided, shows last 10.
|
|
86
|
+
*/
|
|
87
|
+
logMessages(events: RenderEvent[], count?: number): void;
|
|
88
|
+
}
|
package/src/chat/types.ts
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import { ChatInteraction, Config } from "../types";
|
|
5
5
|
import { BaseAgent } from "../agents/base/base";
|
|
6
6
|
import { ToolsService } from "src/services";
|
|
7
|
+
import { AgentRenderer } from "./renderer/types";
|
|
7
8
|
|
|
8
9
|
export interface ChatContext {
|
|
9
10
|
debugMode?: boolean;
|
|
10
11
|
agentMode?: boolean;
|
|
11
12
|
currentAgent?: string;
|
|
13
|
+
promptText?: string;
|
|
12
14
|
searchMode?: boolean;
|
|
13
15
|
voiceMode?: boolean;
|
|
14
16
|
multilineMode?: boolean;
|
|
@@ -17,6 +19,8 @@ export interface ChatContext {
|
|
|
17
19
|
inputMethod?: InputMethod;
|
|
18
20
|
selectedAgent?: BaseAgent;
|
|
19
21
|
plugins: string[];
|
|
22
|
+
activeAgentTaskId?: string;
|
|
23
|
+
renderer?: AgentRenderer;
|
|
20
24
|
|
|
21
25
|
[key: string]: any;
|
|
22
26
|
}
|
|
@@ -25,6 +29,7 @@ export interface ChatMode {
|
|
|
25
29
|
name: string;
|
|
26
30
|
description: string;
|
|
27
31
|
active: boolean;
|
|
32
|
+
promptText?: string | (() => string);
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
export interface CommandResult {
|
package/src/chat.ts
CHANGED
|
@@ -2,11 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* New Modular Chat Interface - Simplified and cleaner than original chat.ts
|
|
5
|
+
*
|
|
6
|
+
* Supports custom renderers and root modules via .knowhow/knowhow.json:
|
|
7
|
+
*
|
|
8
|
+
* ```json
|
|
9
|
+
* {
|
|
10
|
+
* "chat": {
|
|
11
|
+
* "renderer": "./my-renderer.ts", // local TS/JS file or npm package
|
|
12
|
+
* "rootModule": "./my-module.ts" // local TS/JS file or npm package
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
5
16
|
*/
|
|
6
17
|
|
|
7
18
|
import { CliChatService } from "./chat/CliChatService.js";
|
|
8
19
|
import { InternalChatModule } from "./chat/modules/InternalChatModule.js";
|
|
9
20
|
import { getConfig } from "./config.js";
|
|
21
|
+
import { loadRenderer, loadRootModule, loadChatModule } from "./chat/renderer/loadRenderer.js";
|
|
22
|
+
import { AgentModule } from "./chat/modules/AgentModule.js";
|
|
10
23
|
|
|
11
24
|
async function main() {
|
|
12
25
|
try {
|
|
@@ -39,11 +52,60 @@ async function main() {
|
|
|
39
52
|
}
|
|
40
53
|
|
|
41
54
|
// Create chat service with plugins
|
|
42
|
-
const chatService = new CliChatService(config.plugins
|
|
55
|
+
const chatService = new CliChatService(config.plugins?.enabled ?? []);
|
|
43
56
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
// ── Renderer ──────────────────────────────────────────────────────────────
|
|
58
|
+
// Load a custom renderer if configured, otherwise use the default
|
|
59
|
+
// ConsoleRenderer (already the default inside AgentModule).
|
|
60
|
+
const rendererSpecifier = config.chat?.renderer;
|
|
61
|
+
if (rendererSpecifier) {
|
|
62
|
+
try {
|
|
63
|
+
const customRenderer = await loadRenderer(rendererSpecifier);
|
|
64
|
+
// Inject the renderer into AgentModule via the chat service context
|
|
65
|
+
// AgentModule picks it up from context.renderer
|
|
66
|
+
chatService.setContext({ renderer: customRenderer });
|
|
67
|
+
console.log(`✓ Loaded custom renderer: ${rendererSpecifier}`);
|
|
68
|
+
} catch (err: any) {
|
|
69
|
+
console.warn(`⚠ Could not load renderer "${rendererSpecifier}": ${err.message}`);
|
|
70
|
+
console.warn(" Falling back to default ConsoleRenderer.");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Root Module ───────────────────────────────────────────────────────────
|
|
75
|
+
// Allow swapping the root chat module entirely via config.
|
|
76
|
+
// The root module drives the whole CLI chat experience.
|
|
77
|
+
const rootModuleSpecifier = config.chat?.rootModule;
|
|
78
|
+
if (rootModuleSpecifier) {
|
|
79
|
+
try {
|
|
80
|
+
const customModule = await loadRootModule(rootModuleSpecifier);
|
|
81
|
+
await customModule.initialize(chatService);
|
|
82
|
+
console.log(`✓ Loaded custom root module: ${rootModuleSpecifier}`);
|
|
83
|
+
} catch (err: any) {
|
|
84
|
+
console.warn(`⚠ Could not load root module "${rootModuleSpecifier}": ${err.message}`);
|
|
85
|
+
console.warn(" Falling back to InternalChatModule.");
|
|
86
|
+
const internalModule = new InternalChatModule();
|
|
87
|
+
await internalModule.initialize(chatService);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
// Default: use the internal module
|
|
91
|
+
const internalModule = new InternalChatModule();
|
|
92
|
+
await internalModule.initialize(chatService);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Additional Chat Modules ────────────────────────────────────────────────
|
|
96
|
+
// Load any additional chat modules specified in config.chat.modules.
|
|
97
|
+
// These are loaded AFTER the root module so they can add commands/modes
|
|
98
|
+
// on top of whatever the root module provides.
|
|
99
|
+
const moduleSpecifiers = config.chat?.modules ?? [];
|
|
100
|
+
for (const specifier of moduleSpecifiers) {
|
|
101
|
+
try {
|
|
102
|
+
const chatModule = await loadChatModule(specifier);
|
|
103
|
+
await chatModule.initialize(chatService);
|
|
104
|
+
console.log(`✓ Loaded chat module: ${specifier}`);
|
|
105
|
+
} catch (err: any) {
|
|
106
|
+
console.warn(`⚠ Could not load chat module "${specifier}": ${err.message}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
47
109
|
|
|
48
110
|
// Start the chat loop
|
|
49
111
|
await chatService.startChatLoop();
|
|
@@ -56,7 +118,9 @@ async function main() {
|
|
|
56
118
|
// Check if this file is being run directly
|
|
57
119
|
const isMainModule =
|
|
58
120
|
(process.argv[1] && process.argv[1].endsWith("chat2.ts")) ||
|
|
59
|
-
(process.argv[1] && process.argv[1].endsWith("chat2.js"))
|
|
121
|
+
(process.argv[1] && process.argv[1].endsWith("chat2.js")) ||
|
|
122
|
+
(process.argv[1] && process.argv[1].endsWith("chat.ts")) ||
|
|
123
|
+
(process.argv[1] && process.argv[1].endsWith("chat.js"));
|
|
60
124
|
|
|
61
125
|
if (isMainModule) {
|
|
62
126
|
main().catch(console.error);
|