agent-dj 0.1.2 → 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
@@ -670,6 +670,17 @@ CREATE TABLE IF NOT EXISTS permission_cache (
670
670
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
671
671
  );
672
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
+ );
673
684
  `;
674
685
  }
675
686
  });
@@ -780,6 +791,37 @@ function getAuditLog(limit = 100, offset = 0) {
780
791
  function clearAuditLog() {
781
792
  getDb().prepare("DELETE FROM audit_log").run();
782
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
+ }
783
825
  var db;
784
826
  var init_db = __esm({
785
827
  "src/db/index.ts"() {
@@ -273408,8 +273450,21 @@ function routeMessage(message) {
273408
273450
  }
273409
273451
  return "orchestrator";
273410
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
+ }
273411
273465
  function buildSystemPrompt() {
273412
273466
  let prompt = BASE_SYSTEM_PROMPT;
273467
+ prompt += buildSkillsSection();
273413
273468
  const mcpSummary = getMcpToolSummary();
273414
273469
  if (mcpSummary) {
273415
273470
  prompt += `
@@ -273638,6 +273693,7 @@ async function delegateToAgent(targetAgent, messages, lastMessage, model, provid
273638
273693
  const memoryCtx = memory.buildContext(lastMessage);
273639
273694
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
273640
273695
  let augmentedSystem = agentDef.systemPrompt;
273696
+ augmentedSystem += buildSkillsSection();
273641
273697
  const mcpSummary = getMcpToolSummary();
273642
273698
  if (mcpSummary) augmentedSystem += `
273643
273699
 
@@ -273728,6 +273784,7 @@ async function delegateToAgentStreaming(targetAgent, messages, lastMessage, mode
273728
273784
  const memoryCtx = memory.buildContext(lastMessage);
273729
273785
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
273730
273786
  let augmentedSystem = agentDef.systemPrompt;
273787
+ augmentedSystem += buildSkillsSection();
273731
273788
  const mcpSummary = getMcpToolSummary();
273732
273789
  if (mcpSummary) augmentedSystem += `
273733
273790
 
@@ -274724,12 +274781,66 @@ var init_apps = __esm({
274724
274781
  }
274725
274782
  });
274726
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
+
274727
274838
  // src/server/index.ts
274728
274839
  var server_exports = {};
274729
274840
  __export(server_exports, {
274730
274841
  createApp: () => createApp
274731
274842
  });
274732
- import { Hono as Hono9 } from "hono";
274843
+ import { Hono as Hono10 } from "hono";
274733
274844
  import { cors } from "hono/cors";
274734
274845
  import { serveStatic } from "@hono/node-server/serve-static";
274735
274846
  import { fileURLToPath } from "url";
@@ -274750,9 +274861,9 @@ function findWebDist() {
274750
274861
  return candidates[0];
274751
274862
  }
274752
274863
  function createApp() {
274753
- const app = new Hono9();
274864
+ const app = new Hono10();
274754
274865
  app.use("*", cors());
274755
- 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"];
274756
274867
  app.use("/api/*", async (c, next) => {
274757
274868
  const path = c.req.path;
274758
274869
  const method = c.req.method;
@@ -274772,6 +274883,7 @@ function createApp() {
274772
274883
  app.route("/api/settings", settingsRoutes);
274773
274884
  app.route("/api/memory", memoryRoutes);
274774
274885
  app.route("/api/apps", appsRoutes);
274886
+ app.route("/api/skills", skillsRoutes);
274775
274887
  app.get("/api/health", (c) => {
274776
274888
  return c.json({
274777
274889
  status: "ok",
@@ -274795,6 +274907,7 @@ var init_server = __esm({
274795
274907
  init_settings();
274796
274908
  init_memory2();
274797
274909
  init_apps();
274910
+ init_skills();
274798
274911
  __filename = fileURLToPath(import.meta.url);
274799
274912
  __dirname = dirname(__filename);
274800
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";
@@ -272661,8 +272692,21 @@ You have direct access to tools including web search, file operations, knowledge
272661
272692
  - When listing options, briefly note pros/cons
272662
272693
  - End with a clear next step or action item when relevant
272663
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
+ }
272664
272707
  function buildSystemPrompt() {
272665
272708
  let prompt = BASE_SYSTEM_PROMPT;
272709
+ prompt += buildSkillsSection();
272666
272710
  const mcpSummary = getMcpToolSummary();
272667
272711
  if (mcpSummary) {
272668
272712
  prompt += `
@@ -272899,6 +272943,7 @@ async function delegateToAgent(targetAgent, messages, lastMessage, model, provid
272899
272943
  const memoryCtx = memory.buildContext(lastMessage);
272900
272944
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
272901
272945
  let augmentedSystem = agentDef.systemPrompt;
272946
+ augmentedSystem += buildSkillsSection();
272902
272947
  const mcpSummary = getMcpToolSummary();
272903
272948
  if (mcpSummary) augmentedSystem += `
272904
272949
 
@@ -272989,6 +273034,7 @@ async function delegateToAgentStreaming(targetAgent, messages, lastMessage, mode
272989
273034
  const memoryCtx = memory.buildContext(lastMessage);
272990
273035
  const knowledgeCtx = buildKnowledgeContext(lastMessage);
272991
273036
  let augmentedSystem = agentDef.systemPrompt;
273037
+ augmentedSystem += buildSkillsSection();
272992
273038
  const mcpSummary = getMcpToolSummary();
272993
273039
  if (mcpSummary) augmentedSystem += `
272994
273040
 
@@ -273966,6 +274012,53 @@ appsRoutes.post("/refresh", async (c) => {
273966
274012
  return c.json({ status: "ok", count: cachedCatalog.apps.length });
273967
274013
  });
273968
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
+
273969
274062
  // src/server/index.ts
273970
274063
  var __filename = fileURLToPath(import.meta.url);
273971
274064
  var __dirname = dirname(__filename);
@@ -273985,9 +274078,9 @@ function findWebDist() {
273985
274078
  }
273986
274079
  var WEB_DIST = findWebDist();
273987
274080
  function createApp() {
273988
- const app = new Hono9();
274081
+ const app = new Hono10();
273989
274082
  app.use("*", cors());
273990
- 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"];
273991
274084
  app.use("/api/*", async (c, next) => {
273992
274085
  const path = c.req.path;
273993
274086
  const method = c.req.method;
@@ -274007,6 +274100,7 @@ function createApp() {
274007
274100
  app.route("/api/settings", settingsRoutes);
274008
274101
  app.route("/api/memory", memoryRoutes);
274009
274102
  app.route("/api/apps", appsRoutes);
274103
+ app.route("/api/skills", skillsRoutes);
274010
274104
  app.get("/api/health", (c) => {
274011
274105
  return c.json({
274012
274106
  status: "ok",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-dj",
3
- "version": "0.1.2",
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": {