agent-dj 0.1.1 → 0.1.3

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 CHANGED
@@ -1,23 +1,49 @@
1
1
  # Agent DJ
2
2
 
3
- Self-hosted AI agent platform with a beautiful dashboard. One command install, multi-agent orchestration, RAG, MCP support.
3
+ **Self-hosted AI agent platform. One command, beautiful dashboard, 8 providers, multi-agent orchestration.**
4
+
5
+ ```bash
6
+ npx agent-dj@latest
7
+ ```
8
+
9
+ Bring your own API key. Supports Claude, GPT, Gemini, Grok, Mistral, DeepSeek, Groq, and Ollama (local/free). No cloud accounts, no sign-ups — runs entirely on your machine. Apache-2.0.
10
+
11
+ **What you get:**
12
+
13
+ - Beautiful web dashboard at localhost
14
+ - 8 LLM providers — Claude, GPT-4o, Gemini, Grok, Mistral, DeepSeek, Groq, Ollama
15
+ - Multi-agent system — orchestrator auto-routes to specialist agents (Coder, Researcher, Writer, System, RAG)
16
+ - Knowledge base with RAG — upload documents, agents search them automatically
17
+ - MCP support — connect external tools (GitHub, Slack, databases, etc.)
18
+ - Skills — create custom instructions that shape how all agents behave
19
+ - Memory — agents remember context across conversations
20
+ - Full audit log of every action
21
+ - Permission system — control what agents can do
22
+
23
+ **BYOK (Bring Your Own Key).** Your keys, your data, your machine. Nothing leaves localhost unless you tell it to.
24
+
25
+ ---
4
26
 
5
27
  ## Quick Start
6
28
 
7
29
  ```bash
8
- # Install globally
9
- npm install -g agent-dj
10
-
11
- # Or run directly with npx
12
- npx agent-dj
30
+ npx agent-dj@latest
13
31
  ```
14
32
 
15
- On first run, the setup wizard walks you through choosing a provider and entering your API key. Then the dashboard opens automatically.
33
+ On first run, the setup wizard walks you through choosing a provider and entering your API key. Then the dashboard opens automatically at **http://localhost:3456**.
34
+
35
+ You can also install globally:
36
+
37
+ ```bash
38
+ npm install -g agent-dj
39
+ agent-dj
40
+ ```
16
41
 
17
42
  ## Features
18
43
 
19
44
  - Multi-agent orchestration (Coder, Researcher, Writer, System, RAG)
20
45
  - Chat interface with conversation history
46
+ - Skills system (custom instructions for all agents, works across all providers)
21
47
  - Memory system (user context, feedback, projects)
22
48
  - Knowledge base with RAG (document upload, hybrid search)
23
49
  - MCP (Model Context Protocol) server support
@@ -32,6 +58,9 @@ On first run, the setup wizard walks you through choosing a provider and enterin
32
58
  | **Anthropic** | API key or OAuth token | Claude Opus, Sonnet, Haiku |
33
59
  | **OpenAI** | API key | GPT-4o, o1, o3-mini |
34
60
  | **Google** | API key | Gemini 2.0 Flash/Pro |
61
+ | **xAI** | API key | Grok-3 |
62
+ | **Mistral** | API key | Mistral Large |
63
+ | **DeepSeek** | API key | DeepSeek Chat |
35
64
  | **Groq** | API key | Llama 3.3 70B (fast, free tier) |
36
65
  | **Ollama** | None | Local models, free |
37
66
 
package/dist/cli.js CHANGED
@@ -124,6 +124,18 @@ var init_defaults = __esm({
124
124
  ollama: {
125
125
  default: "llama3.2",
126
126
  options: ["llama3.2", "mistral", "codellama", "phi3"]
127
+ },
128
+ deepseek: {
129
+ default: "deepseek-chat",
130
+ options: ["deepseek-chat", "deepseek-reasoner"]
131
+ },
132
+ xai: {
133
+ default: "grok-3",
134
+ options: ["grok-3", "grok-3-mini", "grok-2", "grok-2-mini"]
135
+ },
136
+ mistral: {
137
+ default: "mistral-large-latest",
138
+ options: ["mistral-large-latest", "codestral-latest", "mistral-small-latest"]
127
139
  }
128
140
  };
129
141
  }
@@ -519,14 +531,20 @@ var init_setup = __esm({
519
531
  openai: "OpenAI (GPT)",
520
532
  google: "Google (Gemini)",
521
533
  groq: "Groq (fast inference)",
522
- ollama: "Ollama (local, free)"
534
+ ollama: "Ollama (local, free)",
535
+ deepseek: "DeepSeek (cheap & strong)",
536
+ xai: "xAI (Grok)",
537
+ mistral: "Mistral AI"
523
538
  };
524
539
  API_KEY_HINTS = {
525
540
  anthropic: "Get key at console.anthropic.com",
526
541
  openai: "Get key at platform.openai.com",
527
542
  google: "Get key at aistudio.google.com",
528
543
  groq: "Get key at console.groq.com (free tier available)",
529
- ollama: "No key needed \u2014 runs locally"
544
+ ollama: "No key needed \u2014 runs locally",
545
+ deepseek: "Get key at platform.deepseek.com",
546
+ xai: "Get key at console.x.ai",
547
+ mistral: "Get key at console.mistral.ai"
530
548
  };
531
549
  }
532
550
  });
@@ -652,6 +670,17 @@ CREATE TABLE IF NOT EXISTS permission_cache (
652
670
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
653
671
  );
654
672
  CREATE INDEX IF NOT EXISTS idx_perm_session ON permission_cache(session_id, resource_type, resource_path);
673
+
674
+ -- Skills
675
+ CREATE TABLE IF NOT EXISTS skills (
676
+ id TEXT PRIMARY KEY,
677
+ name TEXT NOT NULL,
678
+ description TEXT NOT NULL DEFAULT '',
679
+ content TEXT NOT NULL,
680
+ enabled INTEGER NOT NULL DEFAULT 1,
681
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
682
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
683
+ );
655
684
  `;
656
685
  }
657
686
  });
@@ -762,6 +791,37 @@ function getAuditLog(limit = 100, offset = 0) {
762
791
  function clearAuditLog() {
763
792
  getDb().prepare("DELETE FROM audit_log").run();
764
793
  }
794
+ function createSkill(params) {
795
+ const id = randomUUID();
796
+ getDb().prepare("INSERT INTO skills (id, name, description, content) VALUES (?, ?, ?, ?)").run(id, params.name, params.description, params.content);
797
+ return id;
798
+ }
799
+ function listSkills() {
800
+ return getDb().prepare("SELECT * FROM skills ORDER BY created_at DESC").all();
801
+ }
802
+ function getSkill(id) {
803
+ return getDb().prepare("SELECT * FROM skills WHERE id = ?").get(id);
804
+ }
805
+ function updateSkill(id, updates) {
806
+ const sets = [];
807
+ const values2 = [];
808
+ for (const [key, val] of Object.entries(updates)) {
809
+ if (val !== void 0) {
810
+ sets.push(`${key} = ?`);
811
+ values2.push(val);
812
+ }
813
+ }
814
+ if (sets.length === 0) return;
815
+ sets.push("updated_at = datetime('now')");
816
+ values2.push(id);
817
+ getDb().prepare(`UPDATE skills SET ${sets.join(", ")} WHERE id = ?`).run(...values2);
818
+ }
819
+ function deleteSkill(id) {
820
+ getDb().prepare("DELETE FROM skills WHERE id = ?").run(id);
821
+ }
822
+ function getEnabledSkills() {
823
+ return getDb().prepare("SELECT * FROM skills WHERE enabled = 1 ORDER BY created_at ASC").all();
824
+ }
765
825
  var db;
766
826
  var init_db = __esm({
767
827
  "src/db/index.ts"() {
@@ -272727,6 +272787,24 @@ async function getOrCreateProvider(config) {
272727
272787
  baseURL: config.baseUrl ?? "http://localhost:11434/v1"
272728
272788
  });
272729
272789
  break;
272790
+ case "deepseek":
272791
+ instance = createOpenAI({
272792
+ apiKey: config.apiKey,
272793
+ baseURL: "https://api.deepseek.com/v1"
272794
+ });
272795
+ break;
272796
+ case "xai":
272797
+ instance = createOpenAI({
272798
+ apiKey: config.apiKey,
272799
+ baseURL: "https://api.x.ai/v1"
272800
+ });
272801
+ break;
272802
+ case "mistral":
272803
+ instance = createOpenAI({
272804
+ apiKey: config.apiKey,
272805
+ baseURL: "https://api.mistral.ai/v1"
272806
+ });
272807
+ break;
272730
272808
  default:
272731
272809
  throw new Error(`Unsupported provider: ${config.provider}`);
272732
272810
  }
@@ -272734,7 +272812,7 @@ async function getOrCreateProvider(config) {
272734
272812
  return instance;
272735
272813
  }
272736
272814
  function getLanguageModel(config, modelId) {
272737
- const model = modelId ?? config.model ?? getDefaultModel(config.provider);
272815
+ const model = modelId || config.model || getDefaultModel(config.provider);
272738
272816
  if (config.provider === "anthropic" && config.authType === "session_token") {
272739
272817
  _lastSessionTokenSupportsTools = false;
272740
272818
  if (isClaudeCliAvailable()) {
@@ -272770,7 +272848,10 @@ function getDefaultModel(provider) {
272770
272848
  openai: "gpt-4o",
272771
272849
  google: "gemini-2.0-flash",
272772
272850
  groq: "llama-3.3-70b-versatile",
272773
- ollama: "llama3.2"
272851
+ ollama: "llama3.2",
272852
+ deepseek: "deepseek-chat",
272853
+ xai: "grok-3",
272854
+ mistral: "mistral-large-latest"
272774
272855
  };
272775
272856
  return defaults[provider];
272776
272857
  }
@@ -273275,7 +273356,19 @@ var init_models = __esm({
273275
273356
  { id: "llama-3.1-8b-instant", name: "Llama 3.1 8B", provider: "groq", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
273276
273357
  // Ollama
273277
273358
  { id: "llama3.2", name: "Llama 3.2", provider: "ollama", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
273278
- { id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false }
273359
+ { id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false },
273360
+ // DeepSeek
273361
+ { id: "deepseek-chat", name: "DeepSeek Chat", provider: "deepseek", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: true },
273362
+ { id: "deepseek-reasoner", name: "DeepSeek Reasoner", provider: "deepseek", contextWindow: 128e3, costTier: "standard", supportsTools: false, supportsVision: false },
273363
+ // xAI
273364
+ { id: "grok-3", name: "Grok 3", provider: "xai", contextWindow: 131072, costTier: "premium", supportsTools: true, supportsVision: true },
273365
+ { id: "grok-3-mini", name: "Grok 3 Mini", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
273366
+ { id: "grok-2", name: "Grok 2", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
273367
+ { id: "grok-2-mini", name: "Grok 2 Mini", provider: "xai", contextWindow: 131072, costTier: "cheap", supportsTools: true, supportsVision: false },
273368
+ // Mistral
273369
+ { id: "mistral-large-latest", name: "Mistral Large", provider: "mistral", contextWindow: 128e3, costTier: "standard", supportsTools: true, supportsVision: false },
273370
+ { id: "codestral-latest", name: "Codestral", provider: "mistral", contextWindow: 256e3, costTier: "cheap", supportsTools: true, supportsVision: false },
273371
+ { id: "mistral-small-latest", name: "Mistral Small", provider: "mistral", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false }
273279
273372
  ];
273280
273373
  }
273281
273374
  });
@@ -273357,8 +273450,21 @@ function routeMessage(message) {
273357
273450
  }
273358
273451
  return "orchestrator";
273359
273452
  }
273453
+ function buildSkillsSection() {
273454
+ const skills = getEnabledSkills();
273455
+ if (skills.length === 0) return "";
273456
+ const skillsText = skills.map((s) => `### ${s.name}
273457
+ ${s.content}`).join("\n\n");
273458
+ return `
273459
+
273460
+ ## Custom Skills & Instructions
273461
+ Follow these additional instructions:
273462
+
273463
+ ${skillsText}`;
273464
+ }
273360
273465
  function buildSystemPrompt() {
273361
273466
  let prompt = BASE_SYSTEM_PROMPT;
273467
+ prompt += buildSkillsSection();
273362
273468
  const mcpSummary = getMcpToolSummary();
273363
273469
  if (mcpSummary) {
273364
273470
  prompt += `
@@ -273587,6 +273693,7 @@ async function delegateToAgent(targetAgent, messages, lastMessage, model, provid
273587
273693
  const memoryCtx = memory.buildContext(lastMessage);
273588
273694
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
273589
273695
  let augmentedSystem = agentDef.systemPrompt;
273696
+ augmentedSystem += buildSkillsSection();
273590
273697
  const mcpSummary = getMcpToolSummary();
273591
273698
  if (mcpSummary) augmentedSystem += `
273592
273699
 
@@ -273677,6 +273784,7 @@ async function delegateToAgentStreaming(targetAgent, messages, lastMessage, mode
273677
273784
  const memoryCtx = memory.buildContext(lastMessage);
273678
273785
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
273679
273786
  let augmentedSystem = agentDef.systemPrompt;
273787
+ augmentedSystem += buildSkillsSection();
273680
273788
  const mcpSummary = getMcpToolSummary();
273681
273789
  if (mcpSummary) augmentedSystem += `
273682
273790
 
@@ -274389,6 +274497,18 @@ var init_settings = __esm({
274389
274497
  saveConfig(updated);
274390
274498
  return c.json({ ok: true });
274391
274499
  });
274500
+ settingsRoutes.delete("/providers/:name", (c) => {
274501
+ const name = c.req.param("name");
274502
+ const config = loadConfig();
274503
+ const idx = config.providers.findIndex((p2) => p2.provider === name);
274504
+ if (idx < 0) return c.json({ error: "Provider not found" }, 404);
274505
+ if (config.defaultProvider === name) {
274506
+ return c.json({ error: "Cannot delete the default provider. Set another as default first." }, 400);
274507
+ }
274508
+ config.providers.splice(idx, 1);
274509
+ saveConfig(config);
274510
+ return c.json({ ok: true });
274511
+ });
274392
274512
  settingsRoutes.get("/system", (c) => {
274393
274513
  const stats = getSystemStats();
274394
274514
  return c.json(stats);
@@ -274661,12 +274781,66 @@ var init_apps = __esm({
274661
274781
  }
274662
274782
  });
274663
274783
 
274784
+ // src/server/routes/skills.ts
274785
+ import { Hono as Hono9 } from "hono";
274786
+ var skillsRoutes;
274787
+ var init_skills = __esm({
274788
+ "src/server/routes/skills.ts"() {
274789
+ "use strict";
274790
+ init_db();
274791
+ skillsRoutes = new Hono9();
274792
+ skillsRoutes.get("/", (c) => {
274793
+ return c.json(listSkills());
274794
+ });
274795
+ skillsRoutes.get("/:id", (c) => {
274796
+ const skill = getSkill(c.req.param("id"));
274797
+ if (!skill) return c.json({ error: "Skill not found" }, 404);
274798
+ return c.json(skill);
274799
+ });
274800
+ skillsRoutes.post("/", async (c) => {
274801
+ const body = await c.req.json();
274802
+ const { name, description, content } = body;
274803
+ if (!name || !content) {
274804
+ return c.json({ error: "name and content are required" }, 400);
274805
+ }
274806
+ const id = createSkill({ name, description: description || "", content });
274807
+ return c.json({ id }, 201);
274808
+ });
274809
+ skillsRoutes.put("/:id", async (c) => {
274810
+ const id = c.req.param("id");
274811
+ const skill = getSkill(id);
274812
+ if (!skill) return c.json({ error: "Skill not found" }, 404);
274813
+ const body = await c.req.json();
274814
+ const updates = {};
274815
+ if (body.name !== void 0) updates.name = body.name;
274816
+ if (body.description !== void 0) updates.description = body.description;
274817
+ if (body.content !== void 0) updates.content = body.content;
274818
+ if (body.enabled !== void 0) updates.enabled = body.enabled;
274819
+ updateSkill(id, updates);
274820
+ return c.json({ ok: true });
274821
+ });
274822
+ skillsRoutes.delete("/:id", (c) => {
274823
+ const id = c.req.param("id");
274824
+ deleteSkill(id);
274825
+ return c.json({ ok: true });
274826
+ });
274827
+ skillsRoutes.patch("/:id/toggle", (c) => {
274828
+ const id = c.req.param("id");
274829
+ const skill = getSkill(id);
274830
+ if (!skill) return c.json({ error: "Skill not found" }, 404);
274831
+ updateSkill(id, { enabled: skill.enabled ? 0 : 1 });
274832
+ const updated = getSkill(id);
274833
+ return c.json(updated);
274834
+ });
274835
+ }
274836
+ });
274837
+
274664
274838
  // src/server/index.ts
274665
274839
  var server_exports = {};
274666
274840
  __export(server_exports, {
274667
274841
  createApp: () => createApp
274668
274842
  });
274669
- import { Hono as Hono9 } from "hono";
274843
+ import { Hono as Hono10 } from "hono";
274670
274844
  import { cors } from "hono/cors";
274671
274845
  import { serveStatic } from "@hono/node-server/serve-static";
274672
274846
  import { fileURLToPath } from "url";
@@ -274687,9 +274861,9 @@ function findWebDist() {
274687
274861
  return candidates[0];
274688
274862
  }
274689
274863
  function createApp() {
274690
- const app = new Hono9();
274864
+ const app = new Hono10();
274691
274865
  app.use("*", cors());
274692
- const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps"];
274866
+ const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps", "/api/skills"];
274693
274867
  app.use("/api/*", async (c, next) => {
274694
274868
  const path = c.req.path;
274695
274869
  const method = c.req.method;
@@ -274709,6 +274883,7 @@ function createApp() {
274709
274883
  app.route("/api/settings", settingsRoutes);
274710
274884
  app.route("/api/memory", memoryRoutes);
274711
274885
  app.route("/api/apps", appsRoutes);
274886
+ app.route("/api/skills", skillsRoutes);
274712
274887
  app.get("/api/health", (c) => {
274713
274888
  return c.json({
274714
274889
  status: "ok",
@@ -274732,6 +274907,7 @@ var init_server = __esm({
274732
274907
  init_settings();
274733
274908
  init_memory2();
274734
274909
  init_apps();
274910
+ init_skills();
274735
274911
  __filename = fileURLToPath(import.meta.url);
274736
274912
  __dirname = dirname(__filename);
274737
274913
  WEB_DIST = findWebDist();
package/dist/server.js CHANGED
@@ -270567,7 +270567,7 @@ var require_lib6 = __commonJS({
270567
270567
  });
270568
270568
 
270569
270569
  // src/server/index.ts
270570
- import { Hono as Hono9 } from "hono";
270570
+ import { Hono as Hono10 } from "hono";
270571
270571
  import { cors } from "hono/cors";
270572
270572
  import { serveStatic } from "@hono/node-server/serve-static";
270573
270573
  import { fileURLToPath } from "url";
@@ -270729,6 +270729,37 @@ function getAuditLog(limit = 100, offset = 0) {
270729
270729
  function clearAuditLog() {
270730
270730
  getDb().prepare("DELETE FROM audit_log").run();
270731
270731
  }
270732
+ function createSkill(params) {
270733
+ const id = randomUUID();
270734
+ getDb().prepare("INSERT INTO skills (id, name, description, content) VALUES (?, ?, ?, ?)").run(id, params.name, params.description, params.content);
270735
+ return id;
270736
+ }
270737
+ function listSkills() {
270738
+ return getDb().prepare("SELECT * FROM skills ORDER BY created_at DESC").all();
270739
+ }
270740
+ function getSkill(id) {
270741
+ return getDb().prepare("SELECT * FROM skills WHERE id = ?").get(id);
270742
+ }
270743
+ function updateSkill(id, updates) {
270744
+ const sets = [];
270745
+ const values2 = [];
270746
+ for (const [key, val] of Object.entries(updates)) {
270747
+ if (val !== void 0) {
270748
+ sets.push(`${key} = ?`);
270749
+ values2.push(val);
270750
+ }
270751
+ }
270752
+ if (sets.length === 0) return;
270753
+ sets.push("updated_at = datetime('now')");
270754
+ values2.push(id);
270755
+ getDb().prepare(`UPDATE skills SET ${sets.join(", ")} WHERE id = ?`).run(...values2);
270756
+ }
270757
+ function deleteSkill(id) {
270758
+ getDb().prepare("DELETE FROM skills WHERE id = ?").run(id);
270759
+ }
270760
+ function getEnabledSkills() {
270761
+ return getDb().prepare("SELECT * FROM skills WHERE enabled = 1 ORDER BY created_at ASC").all();
270762
+ }
270732
270763
 
270733
270764
  // src/config/index.ts
270734
270765
  import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync } from "fs";
@@ -270980,6 +271011,24 @@ async function getOrCreateProvider(config) {
270980
271011
  baseURL: config.baseUrl ?? "http://localhost:11434/v1"
270981
271012
  });
270982
271013
  break;
271014
+ case "deepseek":
271015
+ instance = createOpenAI({
271016
+ apiKey: config.apiKey,
271017
+ baseURL: "https://api.deepseek.com/v1"
271018
+ });
271019
+ break;
271020
+ case "xai":
271021
+ instance = createOpenAI({
271022
+ apiKey: config.apiKey,
271023
+ baseURL: "https://api.x.ai/v1"
271024
+ });
271025
+ break;
271026
+ case "mistral":
271027
+ instance = createOpenAI({
271028
+ apiKey: config.apiKey,
271029
+ baseURL: "https://api.mistral.ai/v1"
271030
+ });
271031
+ break;
270983
271032
  default:
270984
271033
  throw new Error(`Unsupported provider: ${config.provider}`);
270985
271034
  }
@@ -270987,7 +271036,7 @@ async function getOrCreateProvider(config) {
270987
271036
  return instance;
270988
271037
  }
270989
271038
  function getLanguageModel(config, modelId) {
270990
- const model = modelId ?? config.model ?? getDefaultModel(config.provider);
271039
+ const model = modelId || config.model || getDefaultModel(config.provider);
270991
271040
  if (config.provider === "anthropic" && config.authType === "session_token") {
270992
271041
  _lastSessionTokenSupportsTools = false;
270993
271042
  if (isClaudeCliAvailable()) {
@@ -271023,7 +271072,10 @@ function getDefaultModel(provider) {
271023
271072
  openai: "gpt-4o",
271024
271073
  google: "gemini-2.0-flash",
271025
271074
  groq: "llama-3.3-70b-versatile",
271026
- ollama: "llama3.2"
271075
+ ollama: "llama3.2",
271076
+ deepseek: "deepseek-chat",
271077
+ xai: "grok-3",
271078
+ mistral: "mistral-large-latest"
271027
271079
  };
271028
271080
  return defaults[provider];
271029
271081
  }
@@ -272494,7 +272546,19 @@ var MODEL_CATALOG = [
272494
272546
  { id: "llama-3.1-8b-instant", name: "Llama 3.1 8B", provider: "groq", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
272495
272547
  // Ollama
272496
272548
  { id: "llama3.2", name: "Llama 3.2", provider: "ollama", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false },
272497
- { id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false }
272549
+ { id: "mistral", name: "Mistral", provider: "ollama", contextWindow: 32e3, costTier: "cheap", supportsTools: true, supportsVision: false },
272550
+ // DeepSeek
272551
+ { id: "deepseek-chat", name: "DeepSeek Chat", provider: "deepseek", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: true },
272552
+ { id: "deepseek-reasoner", name: "DeepSeek Reasoner", provider: "deepseek", contextWindow: 128e3, costTier: "standard", supportsTools: false, supportsVision: false },
272553
+ // xAI
272554
+ { id: "grok-3", name: "Grok 3", provider: "xai", contextWindow: 131072, costTier: "premium", supportsTools: true, supportsVision: true },
272555
+ { id: "grok-3-mini", name: "Grok 3 Mini", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
272556
+ { id: "grok-2", name: "Grok 2", provider: "xai", contextWindow: 131072, costTier: "standard", supportsTools: true, supportsVision: true },
272557
+ { id: "grok-2-mini", name: "Grok 2 Mini", provider: "xai", contextWindow: 131072, costTier: "cheap", supportsTools: true, supportsVision: false },
272558
+ // Mistral
272559
+ { id: "mistral-large-latest", name: "Mistral Large", provider: "mistral", contextWindow: 128e3, costTier: "standard", supportsTools: true, supportsVision: false },
272560
+ { id: "codestral-latest", name: "Codestral", provider: "mistral", contextWindow: 256e3, costTier: "cheap", supportsTools: true, supportsVision: false },
272561
+ { id: "mistral-small-latest", name: "Mistral Small", provider: "mistral", contextWindow: 128e3, costTier: "cheap", supportsTools: true, supportsVision: false }
272498
272562
  ];
272499
272563
  function getModelInfo(modelId) {
272500
272564
  return MODEL_CATALOG.find((m) => m.id === modelId);
@@ -272628,8 +272692,21 @@ You have direct access to tools including web search, file operations, knowledge
272628
272692
  - When listing options, briefly note pros/cons
272629
272693
  - End with a clear next step or action item when relevant
272630
272694
  - ALWAYS try to use your tools to answer questions \u2014 don't just give generic advice`;
272695
+ function buildSkillsSection() {
272696
+ const skills = getEnabledSkills();
272697
+ if (skills.length === 0) return "";
272698
+ const skillsText = skills.map((s) => `### ${s.name}
272699
+ ${s.content}`).join("\n\n");
272700
+ return `
272701
+
272702
+ ## Custom Skills & Instructions
272703
+ Follow these additional instructions:
272704
+
272705
+ ${skillsText}`;
272706
+ }
272631
272707
  function buildSystemPrompt() {
272632
272708
  let prompt = BASE_SYSTEM_PROMPT;
272709
+ prompt += buildSkillsSection();
272633
272710
  const mcpSummary = getMcpToolSummary();
272634
272711
  if (mcpSummary) {
272635
272712
  prompt += `
@@ -272866,6 +272943,7 @@ async function delegateToAgent(targetAgent, messages, lastMessage, model, provid
272866
272943
  const memoryCtx = memory.buildContext(lastMessage);
272867
272944
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
272868
272945
  let augmentedSystem = agentDef.systemPrompt;
272946
+ augmentedSystem += buildSkillsSection();
272869
272947
  const mcpSummary = getMcpToolSummary();
272870
272948
  if (mcpSummary) augmentedSystem += `
272871
272949
 
@@ -272956,6 +273034,7 @@ async function delegateToAgentStreaming(targetAgent, messages, lastMessage, mode
272956
273034
  const memoryCtx = memory.buildContext(lastMessage);
272957
273035
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
272958
273036
  let augmentedSystem = agentDef.systemPrompt;
273037
+ augmentedSystem += buildSkillsSection();
272959
273038
  const mcpSummary = getMcpToolSummary();
272960
273039
  if (mcpSummary) augmentedSystem += `
272961
273040
 
@@ -273669,6 +273748,18 @@ settingsRoutes.post("/providers", async (c) => {
273669
273748
  saveConfig(updated);
273670
273749
  return c.json({ ok: true });
273671
273750
  });
273751
+ settingsRoutes.delete("/providers/:name", (c) => {
273752
+ const name = c.req.param("name");
273753
+ const config = loadConfig();
273754
+ const idx = config.providers.findIndex((p) => p.provider === name);
273755
+ if (idx < 0) return c.json({ error: "Provider not found" }, 404);
273756
+ if (config.defaultProvider === name) {
273757
+ return c.json({ error: "Cannot delete the default provider. Set another as default first." }, 400);
273758
+ }
273759
+ config.providers.splice(idx, 1);
273760
+ saveConfig(config);
273761
+ return c.json({ ok: true });
273762
+ });
273672
273763
  settingsRoutes.get("/system", (c) => {
273673
273764
  const stats = getSystemStats();
273674
273765
  return c.json(stats);
@@ -273921,6 +274012,53 @@ appsRoutes.post("/refresh", async (c) => {
273921
274012
  return c.json({ status: "ok", count: cachedCatalog.apps.length });
273922
274013
  });
273923
274014
 
274015
+ // src/server/routes/skills.ts
274016
+ import { Hono as Hono9 } from "hono";
274017
+ var skillsRoutes = new Hono9();
274018
+ skillsRoutes.get("/", (c) => {
274019
+ return c.json(listSkills());
274020
+ });
274021
+ skillsRoutes.get("/:id", (c) => {
274022
+ const skill = getSkill(c.req.param("id"));
274023
+ if (!skill) return c.json({ error: "Skill not found" }, 404);
274024
+ return c.json(skill);
274025
+ });
274026
+ skillsRoutes.post("/", async (c) => {
274027
+ const body = await c.req.json();
274028
+ const { name, description, content } = body;
274029
+ if (!name || !content) {
274030
+ return c.json({ error: "name and content are required" }, 400);
274031
+ }
274032
+ const id = createSkill({ name, description: description || "", content });
274033
+ return c.json({ id }, 201);
274034
+ });
274035
+ skillsRoutes.put("/:id", async (c) => {
274036
+ const id = c.req.param("id");
274037
+ const skill = getSkill(id);
274038
+ if (!skill) return c.json({ error: "Skill not found" }, 404);
274039
+ const body = await c.req.json();
274040
+ const updates = {};
274041
+ if (body.name !== void 0) updates.name = body.name;
274042
+ if (body.description !== void 0) updates.description = body.description;
274043
+ if (body.content !== void 0) updates.content = body.content;
274044
+ if (body.enabled !== void 0) updates.enabled = body.enabled;
274045
+ updateSkill(id, updates);
274046
+ return c.json({ ok: true });
274047
+ });
274048
+ skillsRoutes.delete("/:id", (c) => {
274049
+ const id = c.req.param("id");
274050
+ deleteSkill(id);
274051
+ return c.json({ ok: true });
274052
+ });
274053
+ skillsRoutes.patch("/:id/toggle", (c) => {
274054
+ const id = c.req.param("id");
274055
+ const skill = getSkill(id);
274056
+ if (!skill) return c.json({ error: "Skill not found" }, 404);
274057
+ updateSkill(id, { enabled: skill.enabled ? 0 : 1 });
274058
+ const updated = getSkill(id);
274059
+ return c.json(updated);
274060
+ });
274061
+
273924
274062
  // src/server/index.ts
273925
274063
  var __filename = fileURLToPath(import.meta.url);
273926
274064
  var __dirname = dirname(__filename);
@@ -273940,9 +274078,9 @@ function findWebDist() {
273940
274078
  }
273941
274079
  var WEB_DIST = findWebDist();
273942
274080
  function createApp() {
273943
- const app = new Hono9();
274081
+ const app = new Hono10();
273944
274082
  app.use("*", cors());
273945
- const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps"];
274083
+ const SILENT_ROUTES = ["/api/tasks", "/api/settings", "/api/conversations", "/api/health", "/api/memory", "/api/mcp", "/api/agents", "/api/knowledge", "/api/apps", "/api/skills"];
273946
274084
  app.use("/api/*", async (c, next) => {
273947
274085
  const path = c.req.path;
273948
274086
  const method = c.req.method;
@@ -273962,6 +274100,7 @@ function createApp() {
273962
274100
  app.route("/api/settings", settingsRoutes);
273963
274101
  app.route("/api/memory", memoryRoutes);
273964
274102
  app.route("/api/apps", appsRoutes);
274103
+ app.route("/api/skills", skillsRoutes);
273965
274104
  app.get("/api/health", (c) => {
273966
274105
  return c.json({
273967
274106
  status: "ok",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-dj",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Self-hosted AI agent platform with beautiful dashboard. One command install, multi-agent orchestration, RAG, MCP support.",
5
5
  "type": "module",
6
6
  "bin": {