@tenex-chat/backend 0.9.5 → 0.9.6
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 +5 -1
- package/dist/daemon-wrapper.cjs +47 -0
- package/dist/index.js +59268 -0
- package/dist/wrapper.js +171 -0
- package/package.json +19 -27
- package/src/agents/AgentRegistry.ts +9 -7
- package/src/agents/AgentStorage.ts +24 -1
- package/src/agents/agent-installer.ts +6 -0
- package/src/agents/agent-loader.ts +7 -2
- package/src/agents/constants.ts +10 -2
- package/src/agents/execution/AgentExecutor.ts +35 -6
- package/src/agents/execution/StreamCallbacks.ts +53 -13
- package/src/agents/execution/StreamExecutionHandler.ts +110 -16
- package/src/agents/execution/StreamSetup.ts +19 -9
- package/src/agents/execution/ToolEventHandlers.ts +112 -0
- package/src/agents/role-categories.ts +53 -0
- package/src/agents/types/runtime.ts +7 -0
- package/src/agents/types/storage.ts +7 -0
- package/src/commands/agent/import/openclaw-distiller.ts +63 -7
- package/src/commands/agent/import/openclaw-reader.ts +54 -0
- package/src/commands/agent/import/openclaw.ts +120 -29
- package/src/commands/agent/index.ts +83 -2
- package/src/commands/setup/display.ts +123 -0
- package/src/commands/setup/embed.ts +13 -13
- package/src/commands/setup/global-system-prompt.ts +15 -17
- package/src/commands/setup/image.ts +17 -20
- package/src/commands/setup/interactive.ts +37 -20
- package/src/commands/setup/llm.ts +12 -7
- package/src/commands/setup/onboarding.ts +1580 -248
- package/src/commands/setup/providers.ts +3 -3
- package/src/conversations/ConversationStore.ts +23 -2
- package/src/conversations/MessageBuilder.ts +51 -73
- package/src/conversations/formatters/utils/conversation-transcript-formatter.ts +425 -0
- package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +40 -98
- package/src/conversations/search/embeddings/ConversationIndexingJob.ts +40 -52
- package/src/conversations/services/ConversationSummarizer.ts +1 -2
- package/src/conversations/types.ts +11 -0
- package/src/daemon/Daemon.ts +78 -57
- package/src/daemon/ProjectRuntime.ts +6 -12
- package/src/daemon/SubscriptionManager.ts +13 -0
- package/src/daemon/index.ts +0 -1
- package/src/event-handler/index.ts +1 -0
- package/src/index.ts +20 -1
- package/src/llm/ChunkHandler.ts +1 -1
- package/src/llm/FinishHandler.ts +28 -4
- package/src/llm/LLMConfigEditor.ts +218 -106
- package/src/llm/index.ts +0 -4
- package/src/llm/meta/MetaModelResolver.ts +3 -18
- package/src/llm/middleware/message-sanitizer.ts +153 -0
- package/src/llm/providers/ollama-models.ts +0 -38
- package/src/llm/service.ts +50 -15
- package/src/llm/types.ts +0 -12
- package/src/llm/utils/ConfigurationManager.ts +88 -465
- package/src/llm/utils/ConfigurationTester.ts +42 -185
- package/src/llm/utils/ModelSelector.ts +156 -92
- package/src/llm/utils/ProviderConfigUI.ts +10 -141
- package/src/llm/utils/models-dev-cache.ts +102 -23
- package/src/llm/utils/provider-select-prompt.ts +284 -0
- package/src/llm/utils/provider-setup.ts +81 -34
- package/src/llm/utils/variant-list-prompt.ts +361 -0
- package/src/nostr/AgentEventDecoder.ts +1 -0
- package/src/nostr/AgentEventEncoder.ts +37 -0
- package/src/nostr/AgentProfilePublisher.ts +13 -0
- package/src/nostr/AgentPublisher.ts +26 -0
- package/src/nostr/kinds.ts +1 -0
- package/src/nostr/ndkClient.ts +4 -1
- package/src/nostr/types.ts +12 -0
- package/src/prompts/fragments/25-rag-instructions.ts +22 -21
- package/src/prompts/fragments/31-agents-md-guidance.ts +7 -21
- package/src/prompts/fragments/index.ts +2 -0
- package/src/prompts/utils/systemPromptBuilder.ts +18 -28
- package/src/services/AgentDefinitionMonitor.ts +8 -0
- package/src/services/ConfigService.ts +34 -0
- package/src/services/PubkeyService.ts +7 -1
- package/src/services/compression/CompressionService.ts +133 -74
- package/src/services/compression/compression-utils.ts +110 -19
- package/src/services/config/types.ts +0 -6
- package/src/services/dispatch/AgentDispatchService.ts +79 -0
- package/src/services/intervention/InterventionService.ts +78 -5
- package/src/services/nip46/Nip46SigningService.ts +30 -1
- package/src/services/projects/ProjectContext.ts +8 -6
- package/src/services/rag/RAGCollectionRegistry.ts +199 -0
- package/src/services/rag/RAGDatabaseService.ts +2 -7
- package/src/services/rag/RAGOperations.ts +25 -45
- package/src/services/rag/RAGService.ts +0 -31
- package/src/services/rag/RagSubscriptionService.ts +71 -122
- package/src/services/rag/rag-utils.ts +13 -0
- package/src/services/ral/RALRegistry.ts +25 -184
- package/src/services/reports/ReportEmbeddingService.ts +63 -113
- package/src/services/search/UnifiedSearchService.ts +115 -4
- package/src/services/search/index.ts +1 -0
- package/src/services/search/projectFilter.ts +20 -4
- package/src/services/search/providers/ConversationSearchProvider.ts +1 -0
- package/src/services/search/providers/GenericCollectionSearchProvider.ts +81 -0
- package/src/services/search/providers/LessonSearchProvider.ts +1 -8
- package/src/services/search/providers/ReportSearchProvider.ts +1 -0
- package/src/services/search/types.ts +24 -3
- package/src/services/trust-pubkeys/SystemPubkeyListService.ts +148 -0
- package/src/services/trust-pubkeys/TrustPubkeyService.ts +70 -9
- package/src/telemetry/setup.ts +2 -13
- package/src/tools/implementations/ask.ts +3 -3
- package/src/tools/implementations/conversation_get.ts +28 -268
- package/src/tools/implementations/fs_grep.ts +6 -6
- package/src/tools/implementations/fs_read.ts +2 -0
- package/src/tools/implementations/fs_write.ts +2 -0
- package/src/tools/implementations/learn.ts +38 -50
- package/src/tools/implementations/rag_add_documents.ts +6 -4
- package/src/tools/implementations/rag_create_collection.ts +37 -4
- package/src/tools/implementations/rag_delete_collection.ts +9 -0
- package/src/tools/implementations/{search.ts → rag_search.ts} +31 -25
- package/src/tools/registry.ts +7 -8
- package/src/tools/types.ts +11 -2
- package/src/tools/utils/transcript-args.ts +13 -0
- package/src/utils/cli-theme.ts +13 -0
- package/src/utils/logger.ts +55 -0
- package/src/utils/metadataKeys.ts +17 -0
- package/src/utils/sqlEscaping.ts +39 -0
- package/src/wrapper.ts +7 -3
- package/dist/src/index.js +0 -46790
- package/dist/tenex-backend-wrapper.cjs +0 -3
- package/src/agents/execution/constants.ts +0 -16
- package/src/agents/execution/index.ts +0 -3
- package/src/agents/index.ts +0 -4
- package/src/commands/agent.ts +0 -235
- package/src/conversations/formatters/DelegationXmlFormatter.ts +0 -64
- package/src/conversations/formatters/index.ts +0 -9
- package/src/conversations/index.ts +0 -2
- package/src/conversations/utils/content-utils.ts +0 -69
- package/src/daemon/UnixSocketTransport.ts +0 -318
- package/src/event-handler/newConversation.ts +0 -165
- package/src/events/NDKProjectStatus.ts +0 -384
- package/src/events/index.ts +0 -4
- package/src/lib/json-parser.ts +0 -30
- package/src/llm/RecordingState.ts +0 -37
- package/src/llm/StreamPublisher.ts +0 -40
- package/src/llm/middleware/flight-recorder.ts +0 -188
- package/src/llm/utils/claudeCodePromptCompiler.ts +0 -141
- package/src/nostr/constants.ts +0 -38
- package/src/prompts/core/index.ts +0 -3
- package/src/prompts/index.ts +0 -21
- package/src/services/image/index.ts +0 -12
- package/src/services/status/index.ts +0 -11
- package/src/telemetry/diagnostics.ts +0 -27
- package/src/tools/implementations/rag_query.ts +0 -107
- package/src/types/index.ts +0 -46
- package/src/utils/agentFetcher.ts +0 -107
- package/src/utils/conversation-utils.ts +0 -1
- package/src/utils/process.ts +0 -49
|
@@ -4,10 +4,11 @@ import { Command } from "commander";
|
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
|
6
6
|
import { agentStorage, createStoredAgent } from "@/agents/AgentStorage";
|
|
7
|
+
import { getAgentHomeDirectory } from "@/lib/agent-home";
|
|
7
8
|
import { config as configService } from "@/services/ConfigService";
|
|
8
9
|
import type { LLMConfiguration } from "@/services/config/types";
|
|
9
10
|
import { detectOpenClawStateDir, readOpenClawAgents, convertModelFormat } from "./openclaw-reader";
|
|
10
|
-
import { distillAgentIdentity } from "./openclaw-distiller";
|
|
11
|
+
import { distillAgentIdentity, distillUserContext } from "./openclaw-distiller";
|
|
11
12
|
import type { OpenClawAgent } from "./openclaw-reader";
|
|
12
13
|
|
|
13
14
|
function toSlug(name: string): string {
|
|
@@ -18,23 +19,46 @@ function toSlug(name: string): string {
|
|
|
18
19
|
.replace(/^-+|-+$/g, "");
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
interface CreateHomeDirOptions {
|
|
23
|
+
noSync?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function createHomeDir(
|
|
27
|
+
pubkey: string,
|
|
28
|
+
workspacePath: string,
|
|
29
|
+
options: CreateHomeDirOptions = {},
|
|
30
|
+
): Promise<string> {
|
|
31
|
+
const homeDir = getAgentHomeDirectory(pubkey);
|
|
23
32
|
await fs.mkdir(homeDir, { recursive: true });
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
if (options.noSync) {
|
|
35
|
+
// Copy all workspace files into the home directory
|
|
36
|
+
await fs.cp(workspacePath, homeDir, { recursive: true });
|
|
37
|
+
|
|
38
|
+
const indexContent = `# Memory Files
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
const memoryDirTarget = path.join(workspacePath, "memory");
|
|
33
|
-
const memoryDirLink = path.join(homeDir, "memory");
|
|
34
|
-
await fs.rm(memoryDirLink, { force: true });
|
|
35
|
-
await fs.symlink(memoryDirTarget, memoryDirLink);
|
|
40
|
+
This agent's memory was copied from an OpenClaw installation.
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
- \`MEMORY.md\` — long-term curated memory (copied from OpenClaw)
|
|
43
|
+
- \`memory/YYYY-MM-DD.md\` — daily session logs (copied from OpenClaw)
|
|
44
|
+
|
|
45
|
+
Source: ${workspacePath}
|
|
46
|
+
`;
|
|
47
|
+
await fs.writeFile(path.join(homeDir, "+INDEX.md"), indexContent, "utf-8");
|
|
48
|
+
} else {
|
|
49
|
+
// Symlink MEMORY.md (dangling is ok — file may not exist yet)
|
|
50
|
+
const memoryMdTarget = path.join(workspacePath, "MEMORY.md");
|
|
51
|
+
const memoryMdLink = path.join(homeDir, "MEMORY.md");
|
|
52
|
+
await fs.rm(memoryMdLink, { force: true });
|
|
53
|
+
await fs.symlink(memoryMdTarget, memoryMdLink);
|
|
54
|
+
|
|
55
|
+
// Symlink memory/ directory (dangling is ok)
|
|
56
|
+
const memoryDirTarget = path.join(workspacePath, "memory");
|
|
57
|
+
const memoryDirLink = path.join(homeDir, "memory");
|
|
58
|
+
await fs.rm(memoryDirLink, { force: true });
|
|
59
|
+
await fs.symlink(memoryDirTarget, memoryDirLink);
|
|
60
|
+
|
|
61
|
+
const indexContent = `# Memory Files
|
|
38
62
|
|
|
39
63
|
This agent's memory is synced live from an OpenClaw installation.
|
|
40
64
|
|
|
@@ -43,16 +67,23 @@ This agent's memory is synced live from an OpenClaw installation.
|
|
|
43
67
|
|
|
44
68
|
Source: ${workspacePath}
|
|
45
69
|
`;
|
|
46
|
-
|
|
70
|
+
await fs.writeFile(path.join(homeDir, "+INDEX.md"), indexContent, "utf-8");
|
|
71
|
+
}
|
|
47
72
|
|
|
48
73
|
return homeDir;
|
|
49
74
|
}
|
|
50
75
|
|
|
51
|
-
async function appendUserMdToGlobalPrompt(
|
|
76
|
+
async function appendUserMdToGlobalPrompt(
|
|
77
|
+
rawUserMd: string,
|
|
78
|
+
llmConfigs: LLMConfiguration[],
|
|
79
|
+
): Promise<void> {
|
|
80
|
+
const distilled = await distillUserContext(rawUserMd, llmConfigs);
|
|
81
|
+
if (!distilled) return;
|
|
82
|
+
|
|
52
83
|
const globalPath = configService.getGlobalPath();
|
|
53
84
|
const existingConfig = await configService.loadTenexConfig(globalPath);
|
|
54
85
|
|
|
55
|
-
const userSection = `\n## About the User (imported from OpenClaw)\n\n${
|
|
86
|
+
const userSection = `\n## About the User (imported from OpenClaw)\n\n${distilled}`;
|
|
56
87
|
const existingContent = existingConfig.globalSystemPrompt?.content ?? "";
|
|
57
88
|
const newContent = existingContent ? `${existingContent}${userSection}` : userSection.trim();
|
|
58
89
|
|
|
@@ -66,11 +97,15 @@ async function appendUserMdToGlobalPrompt(userMdContent: string): Promise<void>
|
|
|
66
97
|
await configService.saveGlobalConfig(newConfig);
|
|
67
98
|
}
|
|
68
99
|
|
|
69
|
-
async function importOneAgent(
|
|
100
|
+
async function importOneAgent(
|
|
101
|
+
agent: OpenClawAgent,
|
|
102
|
+
llmConfigs: LLMConfiguration[],
|
|
103
|
+
options: { noSync?: boolean } = {},
|
|
104
|
+
): Promise<void> {
|
|
70
105
|
const tenexModel = convertModelFormat(agent.modelPrimary);
|
|
71
106
|
|
|
72
107
|
console.log(chalk.blue(`\nDistilling identity for agent '${agent.id}'...`));
|
|
73
|
-
const identity = await distillAgentIdentity(agent.workspaceFiles,
|
|
108
|
+
const identity = await distillAgentIdentity(agent.workspaceFiles, llmConfigs);
|
|
74
109
|
|
|
75
110
|
const slug = toSlug(identity.name) || agent.id;
|
|
76
111
|
|
|
@@ -96,21 +131,35 @@ async function importOneAgent(agent: OpenClawAgent, llmConfig: LLMConfiguration)
|
|
|
96
131
|
});
|
|
97
132
|
|
|
98
133
|
await agentStorage.saveAgent(storedAgent);
|
|
99
|
-
const homeDir = await createHomeDir(pubkey, agent.workspacePath);
|
|
134
|
+
const homeDir = await createHomeDir(pubkey, agent.workspacePath, { noSync: options.noSync });
|
|
100
135
|
|
|
101
136
|
console.log(chalk.green(` ✓ Imported: ${identity.name} (${slug})`));
|
|
102
137
|
console.log(chalk.gray(` Keypair: ${pubkey}`));
|
|
103
138
|
console.log(chalk.gray(` Model: ${tenexModel}`));
|
|
104
139
|
console.log(chalk.gray(` Home dir: ${homeDir}`));
|
|
105
|
-
console.log(chalk.gray(`
|
|
140
|
+
console.log(chalk.gray(` Files: ${options.noSync ? "copied" : "symlinked"} from ${agent.workspacePath}`));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function filterAgents(agents: OpenClawAgent[], slugs?: string): OpenClawAgent[] {
|
|
144
|
+
if (!slugs) return agents;
|
|
145
|
+
const allowed = slugs.split(",").map((s) => s.trim());
|
|
146
|
+
return agents.filter((a) => allowed.includes(a.id));
|
|
106
147
|
}
|
|
107
148
|
|
|
108
149
|
export const openclawImportCommand = new Command("openclaw")
|
|
109
150
|
.description("Import agents from a local OpenClaw installation")
|
|
110
|
-
.
|
|
151
|
+
.option("--dry-run", "Preview what would be imported without making changes")
|
|
152
|
+
.option("--json", "Output as JSON array (implies --dry-run)")
|
|
153
|
+
.option("--no-sync", "Copy workspace files instead of symlinking them")
|
|
154
|
+
.option("--slugs <slugs>", "Comma-separated list of agent IDs to import (default: all)")
|
|
155
|
+
.action(async (options: { dryRun?: boolean; json?: boolean; noSync?: boolean; slugs?: string }) => {
|
|
111
156
|
try {
|
|
112
157
|
const stateDir = await detectOpenClawStateDir();
|
|
113
158
|
if (!stateDir) {
|
|
159
|
+
if (options.json) {
|
|
160
|
+
console.log("[]");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
114
163
|
console.error(chalk.red("No OpenClaw installation detected."));
|
|
115
164
|
console.error(
|
|
116
165
|
chalk.gray(
|
|
@@ -121,23 +170,65 @@ export const openclawImportCommand = new Command("openclaw")
|
|
|
121
170
|
return;
|
|
122
171
|
}
|
|
123
172
|
|
|
124
|
-
|
|
173
|
+
const allAgents = await readOpenClawAgents(stateDir);
|
|
174
|
+
const agents = filterAgents(allAgents, options.slugs);
|
|
125
175
|
|
|
126
|
-
|
|
127
|
-
|
|
176
|
+
if (agents.length === 0) {
|
|
177
|
+
if (options.json) {
|
|
178
|
+
console.log("[]");
|
|
179
|
+
} else {
|
|
180
|
+
console.log(chalk.yellow("No matching OpenClaw agents found."));
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
128
184
|
|
|
129
185
|
await configService.loadConfig();
|
|
186
|
+
const llmConfigs = configService.getAllLLMConfigs();
|
|
187
|
+
|
|
188
|
+
if (options.dryRun || options.json) {
|
|
189
|
+
const previews = [];
|
|
190
|
+
for (const agent of agents) {
|
|
191
|
+
const identity = await distillAgentIdentity(agent.workspaceFiles, llmConfigs);
|
|
192
|
+
const slug = toSlug(identity.name) || agent.id;
|
|
193
|
+
previews.push({
|
|
194
|
+
id: agent.id,
|
|
195
|
+
slug,
|
|
196
|
+
model: convertModelFormat(agent.modelPrimary),
|
|
197
|
+
...identity,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (options.json) {
|
|
202
|
+
console.log(JSON.stringify(previews, null, 2));
|
|
203
|
+
} else {
|
|
204
|
+
console.log(chalk.blue(`Would import ${previews.length} agent(s):\n`));
|
|
205
|
+
for (const p of previews) {
|
|
206
|
+
console.log(chalk.green(` ${p.slug}`) + chalk.gray(` (${p.name})`));
|
|
207
|
+
console.log(chalk.gray(` Role: ${p.role}`));
|
|
208
|
+
console.log(chalk.gray(` Model: ${p.model}`));
|
|
209
|
+
console.log(chalk.gray(` Description: ${p.description}`));
|
|
210
|
+
console.log(chalk.gray(` Instructions: ${p.instructions.slice(0, 120)}...`));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!options.json) {
|
|
217
|
+
console.log(chalk.blue(`Found OpenClaw installation at: ${stateDir}`));
|
|
218
|
+
console.log(chalk.blue(`Found ${agents.length} agent(s) to import.`));
|
|
219
|
+
}
|
|
220
|
+
|
|
130
221
|
await agentStorage.initialize();
|
|
131
222
|
|
|
132
|
-
const llmConfig = configService.getLLMConfig();
|
|
133
223
|
let userMdProcessed = false;
|
|
134
224
|
|
|
135
225
|
for (const agent of agents) {
|
|
136
|
-
await importOneAgent(agent,
|
|
226
|
+
await importOneAgent(agent, llmConfigs, { noSync: options.noSync });
|
|
137
227
|
|
|
138
228
|
if (!userMdProcessed && agent.workspaceFiles.user) {
|
|
139
|
-
|
|
140
|
-
|
|
229
|
+
console.log(chalk.blue(`\nDistilling user context from USER.md...`));
|
|
230
|
+
await appendUserMdToGlobalPrompt(agent.workspaceFiles.user, llmConfigs);
|
|
231
|
+
console.log(chalk.green(" ✓ USER.md distilled and appended to global system prompt"));
|
|
141
232
|
userMdProcessed = true;
|
|
142
233
|
}
|
|
143
234
|
}
|
|
@@ -1,6 +1,87 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { NDKPrivateKeySigner, NDKEvent } from "@nostr-dev-kit/ndk";
|
|
4
|
+
import { agentStorage } from "@/agents/AgentStorage";
|
|
5
|
+
import { installAgentFromNostr, installAgentFromNostrEvent } from "@/agents/agent-installer";
|
|
6
|
+
import { initNDK } from "@/nostr/ndkClient";
|
|
2
7
|
import { importCommand } from "./import/index";
|
|
3
8
|
|
|
9
|
+
// ─── tenex agent add ─────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
async function readStdin(): Promise<string> {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
let data = "";
|
|
14
|
+
process.stdin.setEncoding("utf-8");
|
|
15
|
+
process.stdin.on("data", (chunk) => {
|
|
16
|
+
data += chunk;
|
|
17
|
+
});
|
|
18
|
+
process.stdin.on("end", () => resolve(data.trim()));
|
|
19
|
+
process.stdin.on("error", reject);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function addAgent(eventId: string | undefined): Promise<void> {
|
|
24
|
+
await agentStorage.initialize();
|
|
25
|
+
|
|
26
|
+
if (!process.stdin.isTTY) {
|
|
27
|
+
const raw = await readStdin();
|
|
28
|
+
const rawEvent = JSON.parse(raw);
|
|
29
|
+
const event = new NDKEvent(undefined, rawEvent);
|
|
30
|
+
const stored = await installAgentFromNostrEvent(event);
|
|
31
|
+
const pubkey = new NDKPrivateKeySigner(stored.nsec).pubkey;
|
|
32
|
+
console.log(chalk.green(`✓ Installed agent "${stored.name}" (${stored.slug})`));
|
|
33
|
+
console.log(chalk.gray(` pubkey: ${pubkey}`));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!eventId) {
|
|
38
|
+
console.error(chalk.red("Error: provide an event ID or pipe event JSON via stdin"));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await initNDK();
|
|
43
|
+
const stored = await installAgentFromNostr(eventId);
|
|
44
|
+
const pubkey = new NDKPrivateKeySigner(stored.nsec).pubkey;
|
|
45
|
+
console.log(chalk.green(`✓ Installed agent "${stored.name}" (${stored.slug})`));
|
|
46
|
+
console.log(chalk.gray(` pubkey: ${pubkey}`));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── tenex agent assign ─────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
async function assignAgent(slug: string, projectDTag: string): Promise<void> {
|
|
52
|
+
await agentStorage.initialize();
|
|
53
|
+
|
|
54
|
+
const agent = await agentStorage.getAgentBySlug(slug);
|
|
55
|
+
if (!agent) {
|
|
56
|
+
console.error(chalk.red(`Error: no agent found with slug "${slug}"`));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const pubkey = new NDKPrivateKeySigner(agent.nsec).pubkey;
|
|
61
|
+
await agentStorage.addAgentToProject(pubkey, projectDTag);
|
|
62
|
+
|
|
63
|
+
console.log(chalk.green(`✓ Assigned agent "${slug}" to project "${projectDTag}"`));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─── Command registration ────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
const addCommand = new Command("add")
|
|
69
|
+
.description("Install an agent from a Nostr event ID or stdin JSON")
|
|
70
|
+
.argument("[event-id]", "Nostr event ID of the agent definition")
|
|
71
|
+
.action(async (eventId: string | undefined) => {
|
|
72
|
+
await addAgent(eventId);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const assignCommand = new Command("assign")
|
|
76
|
+
.description("Assign an existing agent to a project")
|
|
77
|
+
.argument("<slug>", "Agent slug")
|
|
78
|
+
.argument("<project-dtag>", "Project d-tag to assign the agent to")
|
|
79
|
+
.action(async (slug: string, projectDTag: string) => {
|
|
80
|
+
await assignAgent(slug, projectDTag);
|
|
81
|
+
});
|
|
82
|
+
|
|
4
83
|
export const agentCommand = new Command("agent")
|
|
5
|
-
.description("
|
|
6
|
-
.addCommand(importCommand)
|
|
84
|
+
.description("Manage TENEX agents")
|
|
85
|
+
.addCommand(importCommand)
|
|
86
|
+
.addCommand(addCommand)
|
|
87
|
+
.addCommand(assignCommand);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
// Match Rust TUI's xterm-256 color scheme exactly
|
|
4
|
+
const ACCENT = chalk.ansi256(214); // amber #FFC107
|
|
5
|
+
const INFO = chalk.ansi256(117); // sky blue
|
|
6
|
+
const SELECTED = chalk.ansi256(114); // bright green
|
|
7
|
+
|
|
8
|
+
// Logo palette (xterm-256)
|
|
9
|
+
const DARK = chalk.ansi256(130);
|
|
10
|
+
const MID = chalk.ansi256(172);
|
|
11
|
+
const BRIGHT = chalk.ansi256(220);
|
|
12
|
+
const GLOW = chalk.ansi256(222);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Print an onboarding step header with step number and color rule.
|
|
16
|
+
*
|
|
17
|
+
* 3/8 AI Providers
|
|
18
|
+
* ─────────────────────────────────────────────
|
|
19
|
+
*/
|
|
20
|
+
export function step(number: number, total: number, title: string): void {
|
|
21
|
+
const rule = "─".repeat(45);
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(` ${ACCENT.bold(`${number}/${total}`)} ${ACCENT.bold(title)}`);
|
|
24
|
+
console.log(` ${ACCENT(chalk.dim(rule))}`);
|
|
25
|
+
console.log();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Print dim context/explanation text, 2-space indent.
|
|
30
|
+
*/
|
|
31
|
+
export function context(text: string): void {
|
|
32
|
+
for (const line of text.split("\n")) {
|
|
33
|
+
console.log(` ${chalk.dim(line)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Print a success message: ✓ text
|
|
39
|
+
*/
|
|
40
|
+
export function success(text: string): void {
|
|
41
|
+
console.log(` ${chalk.green.bold("✓")} ${text}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Print a hint/tip with a colored arrow.
|
|
46
|
+
*/
|
|
47
|
+
export function hint(text: string): void {
|
|
48
|
+
console.log(` ${ACCENT("→")} ${ACCENT(text)}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Print a blank line.
|
|
53
|
+
*/
|
|
54
|
+
export function blank(): void {
|
|
55
|
+
console.log();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Print the welcome banner — stippled Sierpinski triangle
|
|
60
|
+
* with "T E N E X" and tagline to the right.
|
|
61
|
+
* Matches the Rust TUI's logo.rs exactly.
|
|
62
|
+
*/
|
|
63
|
+
export function welcome(): void {
|
|
64
|
+
const art: Array<[string, typeof ACCENT]> = [
|
|
65
|
+
[" • ", GLOW],
|
|
66
|
+
[" • • ", BRIGHT],
|
|
67
|
+
[" • • ", ACCENT],
|
|
68
|
+
[" • • • • • ", MID],
|
|
69
|
+
[" • • • • • • ", DARK],
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
console.log();
|
|
73
|
+
for (let i = 0; i < art.length; i++) {
|
|
74
|
+
const [line, color] = art[i];
|
|
75
|
+
let row = " ";
|
|
76
|
+
for (const ch of line) {
|
|
77
|
+
row += ch === " " ? " " : color.bold(ch);
|
|
78
|
+
}
|
|
79
|
+
if (i === 2) row += " " + ACCENT.bold("T E N E X");
|
|
80
|
+
if (i === 3) row += " " + chalk.bold("Your AI agent team, powered by Nostr.");
|
|
81
|
+
if (i === 4) row += " " + chalk.dim("Let's get everything set up.");
|
|
82
|
+
process.stdout.write(row + "\n");
|
|
83
|
+
}
|
|
84
|
+
console.log();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Print the final setup summary banner.
|
|
89
|
+
*/
|
|
90
|
+
export function setupComplete(): void {
|
|
91
|
+
console.log();
|
|
92
|
+
console.log(` ${ACCENT.bold("▲")} ${ACCENT.bold("Setup complete!")}`);
|
|
93
|
+
console.log();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Print a summary line for the final recap.
|
|
98
|
+
*/
|
|
99
|
+
export function summaryLine(label: string, value: string): void {
|
|
100
|
+
const paddedLabel = `${label}:`.padEnd(16);
|
|
101
|
+
console.log(` ${INFO(paddedLabel)}${value}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Format a provider label with checkmark (for use in choices).
|
|
106
|
+
*/
|
|
107
|
+
export function providerCheck(text: string): string {
|
|
108
|
+
return `${SELECTED.bold("[✓]")} ${text}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Format a provider label with unchecked brackets (for use in choices).
|
|
113
|
+
*/
|
|
114
|
+
export function providerUncheck(text: string): string {
|
|
115
|
+
return `${chalk.dim("[ ]")} ${text}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Format a "Done" label in amber style.
|
|
120
|
+
*/
|
|
121
|
+
export function doneLabel(): string {
|
|
122
|
+
return ACCENT.bold(" Done");
|
|
123
|
+
}
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
EmbeddingProviderFactory,
|
|
6
6
|
} from "@/services/rag/EmbeddingProviderFactory";
|
|
7
7
|
import { config as configService } from "@/services/ConfigService";
|
|
8
|
-
import {
|
|
8
|
+
import { inquirerTheme } from "@/utils/cli-theme";
|
|
9
|
+
import chalk from "chalk";
|
|
9
10
|
import { Command } from "commander";
|
|
10
11
|
import inquirer from "inquirer";
|
|
11
12
|
|
|
@@ -61,9 +62,7 @@ export const embedCommand = new Command("embed")
|
|
|
61
62
|
if (scope === "project") {
|
|
62
63
|
// Check if we're in a TENEX project
|
|
63
64
|
if (!(await fileSystem.directoryExists(baseDir))) {
|
|
64
|
-
|
|
65
|
-
"No .tenex directory found. Make sure you're in a TENEX project directory."
|
|
66
|
-
);
|
|
65
|
+
console.log(chalk.red("❌ No .tenex directory found. Make sure you're in a TENEX project directory."));
|
|
67
66
|
process.exitCode = 1;
|
|
68
67
|
return;
|
|
69
68
|
}
|
|
@@ -81,9 +80,7 @@ export const embedCommand = new Command("embed")
|
|
|
81
80
|
// Load configured providers from providers.json
|
|
82
81
|
const providersConfig = await configService.loadTenexProviders(configService.getGlobalPath());
|
|
83
82
|
if (Object.keys(providersConfig.providers).length === 0) {
|
|
84
|
-
|
|
85
|
-
"No providers configured. Run `tenex setup providers` before configuring embeddings."
|
|
86
|
-
);
|
|
83
|
+
console.log(chalk.red("❌ No providers configured. Run `tenex setup providers` before configuring embeddings."));
|
|
87
84
|
process.exitCode = 1;
|
|
88
85
|
return;
|
|
89
86
|
}
|
|
@@ -112,6 +109,7 @@ export const embedCommand = new Command("embed")
|
|
|
112
109
|
message: "Select embedding provider:",
|
|
113
110
|
choices: providerChoices,
|
|
114
111
|
default: existing?.provider || "local",
|
|
112
|
+
theme: inquirerTheme,
|
|
115
113
|
},
|
|
116
114
|
]);
|
|
117
115
|
|
|
@@ -136,6 +134,7 @@ export const embedCommand = new Command("embed")
|
|
|
136
134
|
message: `Select ${displayName} embedding model:`,
|
|
137
135
|
choices: modelChoices,
|
|
138
136
|
default: existing?.provider === provider ? existing?.model : modelChoices[0]?.value,
|
|
137
|
+
theme: inquirerTheme,
|
|
139
138
|
},
|
|
140
139
|
]);
|
|
141
140
|
|
|
@@ -145,6 +144,7 @@ export const embedCommand = new Command("embed")
|
|
|
145
144
|
type: "input",
|
|
146
145
|
name: "customModel",
|
|
147
146
|
message: "Enter model ID:",
|
|
147
|
+
theme: inquirerTheme,
|
|
148
148
|
validate: (input: string) =>
|
|
149
149
|
input.trim().length > 0 || "Model ID cannot be empty",
|
|
150
150
|
},
|
|
@@ -160,6 +160,7 @@ export const embedCommand = new Command("embed")
|
|
|
160
160
|
type: "select",
|
|
161
161
|
name: "model",
|
|
162
162
|
message: "Select local embedding model:",
|
|
163
|
+
theme: inquirerTheme,
|
|
163
164
|
choices: [
|
|
164
165
|
{
|
|
165
166
|
name: "all-MiniLM-L6-v2 (default, fast, good for general use)",
|
|
@@ -189,6 +190,7 @@ export const embedCommand = new Command("embed")
|
|
|
189
190
|
name: "customModel",
|
|
190
191
|
message:
|
|
191
192
|
"Enter HuggingFace model ID (e.g., sentence-transformers/all-MiniLM-L6-v2):",
|
|
193
|
+
theme: inquirerTheme,
|
|
192
194
|
validate: (input: string) =>
|
|
193
195
|
input.trim().length > 0 || "Model ID cannot be empty",
|
|
194
196
|
},
|
|
@@ -210,11 +212,9 @@ export const embedCommand = new Command("embed")
|
|
|
210
212
|
projectPath: scope === "project" ? projectPath : undefined,
|
|
211
213
|
});
|
|
212
214
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
` Model: ${model}`
|
|
217
|
-
);
|
|
215
|
+
console.log("\n" + chalk.green("✓") + chalk.bold(` Embedding configuration saved to ${scope} config`));
|
|
216
|
+
console.log(chalk.gray(` Provider: ${provider}`));
|
|
217
|
+
console.log(chalk.gray(` Model: ${model}`));
|
|
218
218
|
} catch (error: unknown) {
|
|
219
219
|
// Handle SIGINT (Ctrl+C) gracefully
|
|
220
220
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -222,7 +222,7 @@ export const embedCommand = new Command("embed")
|
|
|
222
222
|
return;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
console.log(chalk.red(`❌ Failed to configure embedding model: ${error}`));
|
|
226
226
|
process.exitCode = 1;
|
|
227
227
|
}
|
|
228
228
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fileSystem from "@/lib/fs";
|
|
2
2
|
import { config as configService } from "@/services/ConfigService";
|
|
3
|
+
import { amber, amberBold } from "@/utils/cli-theme";
|
|
3
4
|
import { logger } from "@/utils/logger";
|
|
5
|
+
import chalk from "chalk";
|
|
4
6
|
import { Command } from "commander";
|
|
5
7
|
import { spawn } from "node:child_process";
|
|
6
8
|
import * as fs from "node:fs/promises";
|
|
@@ -88,12 +90,12 @@ export const globalSystemPromptCommand = new Command("global-system-prompt")
|
|
|
88
90
|
const enabled = existingConfig.globalSystemPrompt?.enabled !== false;
|
|
89
91
|
|
|
90
92
|
if (!content || content.trim().length === 0) {
|
|
91
|
-
|
|
93
|
+
console.log(chalk.gray("No global system prompt configured."));
|
|
92
94
|
} else {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
console.log(amberBold(`Global System Prompt (${enabled ? "enabled" : "disabled"}):`));
|
|
96
|
+
console.log(amber("─".repeat(50)));
|
|
95
97
|
console.log(content);
|
|
96
|
-
|
|
98
|
+
console.log(amber("─".repeat(50)));
|
|
97
99
|
}
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
@@ -108,7 +110,7 @@ export const globalSystemPromptCommand = new Command("global-system-prompt")
|
|
|
108
110
|
},
|
|
109
111
|
};
|
|
110
112
|
await configService.saveGlobalConfig(newConfig);
|
|
111
|
-
|
|
113
|
+
console.log(chalk.green("✓") + chalk.bold(" Global system prompt disabled."));
|
|
112
114
|
return;
|
|
113
115
|
}
|
|
114
116
|
|
|
@@ -122,7 +124,7 @@ export const globalSystemPromptCommand = new Command("global-system-prompt")
|
|
|
122
124
|
},
|
|
123
125
|
};
|
|
124
126
|
await configService.saveGlobalConfig(newConfig);
|
|
125
|
-
|
|
127
|
+
console.log(chalk.green("✓") + chalk.bold(" Global system prompt enabled."));
|
|
126
128
|
return;
|
|
127
129
|
}
|
|
128
130
|
|
|
@@ -157,10 +159,8 @@ ${CONTENT_DELIMITER}
|
|
|
157
159
|
|
|
158
160
|
await fs.writeFile(tempFile, templateContent, "utf-8");
|
|
159
161
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
`(Using editor: ${getEditor()})\n`
|
|
163
|
-
);
|
|
162
|
+
console.log(amberBold("Opening editor to configure global system prompt..."));
|
|
163
|
+
console.log(chalk.gray(`(Using editor: ${getEditor()})\n`));
|
|
164
164
|
|
|
165
165
|
// Open editor and wait for it to close - use try/finally for cleanup
|
|
166
166
|
let editedContent: string;
|
|
@@ -202,13 +202,11 @@ ${CONTENT_DELIMITER}
|
|
|
202
202
|
await configService.saveGlobalConfig(newConfig);
|
|
203
203
|
|
|
204
204
|
if (cleanedContent.length === 0) {
|
|
205
|
-
|
|
205
|
+
console.log(chalk.green("✓") + chalk.bold(" Global system prompt cleared (no content)."));
|
|
206
206
|
} else {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
"This prompt will be added to all agents' system prompts."
|
|
211
|
-
);
|
|
207
|
+
console.log(chalk.green("✓") + chalk.bold(" Global system prompt saved successfully!"));
|
|
208
|
+
console.log(chalk.gray(`Content length: ${cleanedContent.length} characters`));
|
|
209
|
+
console.log(chalk.gray("\nThis prompt will be added to all agents' system prompts."));
|
|
212
210
|
}
|
|
213
211
|
} catch (error: unknown) {
|
|
214
212
|
// Handle SIGINT (Ctrl+C) gracefully
|
|
@@ -217,7 +215,7 @@ ${CONTENT_DELIMITER}
|
|
|
217
215
|
return;
|
|
218
216
|
}
|
|
219
217
|
|
|
220
|
-
|
|
218
|
+
console.log(chalk.red(`❌ Failed to configure global system prompt: ${error}`));
|
|
221
219
|
process.exitCode = 1;
|
|
222
220
|
}
|
|
223
221
|
});
|