@alejandroroman/agent-kit 0.1.3 → 0.2.0

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.
Files changed (102) hide show
  1. package/dist/_memory/dist/config.d.ts +14 -0
  2. package/dist/_memory/dist/config.js +16 -0
  3. package/dist/_memory/dist/db/client.d.ts +2 -0
  4. package/dist/_memory/dist/db/client.js +15 -0
  5. package/dist/_memory/dist/db/schema.d.ts +14 -0
  6. package/dist/_memory/dist/db/schema.js +51 -0
  7. package/dist/_memory/dist/embeddings/ollama.d.ts +12 -0
  8. package/dist/_memory/dist/embeddings/ollama.js +22 -0
  9. package/dist/_memory/dist/embeddings/provider.d.ts +4 -0
  10. package/dist/_memory/dist/embeddings/provider.js +1 -0
  11. package/dist/_memory/dist/index.d.ts +10 -0
  12. package/dist/_memory/dist/index.js +6 -0
  13. package/dist/_memory/dist/search.d.ts +30 -0
  14. package/dist/_memory/dist/search.js +121 -0
  15. package/dist/_memory/dist/server.d.ts +8 -0
  16. package/dist/_memory/dist/server.js +126 -0
  17. package/dist/_memory/dist/store.d.ts +51 -0
  18. package/dist/_memory/dist/store.js +115 -0
  19. package/dist/_memory/server.js +0 -0
  20. package/dist/agent/loop.js +210 -111
  21. package/dist/api/errors.d.ts +3 -0
  22. package/dist/api/errors.js +37 -0
  23. package/dist/api/events.d.ts +5 -0
  24. package/dist/api/events.js +28 -0
  25. package/dist/api/router.js +10 -0
  26. package/dist/api/traces.d.ts +3 -0
  27. package/dist/api/traces.js +35 -0
  28. package/dist/api/types.d.ts +2 -0
  29. package/dist/bootstrap.d.ts +6 -5
  30. package/dist/bootstrap.js +26 -7
  31. package/dist/cli/chat.js +18 -63
  32. package/dist/cli/claude-md-template.d.ts +5 -0
  33. package/dist/cli/claude-md-template.js +220 -0
  34. package/dist/cli/config-writer.js +3 -0
  35. package/dist/cli/create.js +1 -4
  36. package/dist/cli/env.d.ts +14 -0
  37. package/dist/cli/env.js +68 -0
  38. package/dist/cli/init.js +14 -7
  39. package/dist/cli/list.js +1 -2
  40. package/dist/cli/paths.d.ts +3 -0
  41. package/dist/cli/paths.js +4 -0
  42. package/dist/cli/repl.d.ts +23 -0
  43. package/dist/cli/repl.js +73 -0
  44. package/dist/cli/slack-setup.d.ts +6 -0
  45. package/dist/cli/slack-setup.js +234 -0
  46. package/dist/cli/start.js +96 -96
  47. package/dist/cli/ui.d.ts +2 -2
  48. package/dist/cli/ui.js +5 -5
  49. package/dist/cli/validate.js +1 -4
  50. package/dist/cli/whats-new.d.ts +1 -0
  51. package/dist/cli/whats-new.js +69 -0
  52. package/dist/cli.js +14 -0
  53. package/dist/config/resolve.d.ts +1 -0
  54. package/dist/config/resolve.js +1 -0
  55. package/dist/config/schema.d.ts +2 -0
  56. package/dist/config/schema.js +1 -0
  57. package/dist/config/writer.d.ts +18 -0
  58. package/dist/config/writer.js +85 -0
  59. package/dist/cron/scheduler.d.ts +4 -1
  60. package/dist/cron/scheduler.js +99 -52
  61. package/dist/gateways/slack/client.d.ts +1 -0
  62. package/dist/gateways/slack/client.js +9 -0
  63. package/dist/gateways/slack/handler.js +2 -1
  64. package/dist/gateways/slack/index.js +75 -29
  65. package/dist/gateways/slack/listener.d.ts +8 -1
  66. package/dist/gateways/slack/listener.js +36 -10
  67. package/dist/heartbeat/runner.js +99 -82
  68. package/dist/index.js +4 -209
  69. package/dist/llm/anthropic.d.ts +1 -0
  70. package/dist/llm/anthropic.js +11 -2
  71. package/dist/llm/fallback.js +34 -2
  72. package/dist/llm/openai.d.ts +2 -0
  73. package/dist/llm/openai.js +33 -2
  74. package/dist/llm/types.d.ts +16 -2
  75. package/dist/llm/types.js +9 -0
  76. package/dist/logger.js +8 -0
  77. package/dist/media/sanitize.d.ts +5 -0
  78. package/dist/media/sanitize.js +53 -0
  79. package/dist/multi/spawn.js +29 -10
  80. package/dist/session/compaction.js +3 -1
  81. package/dist/session/prune-images.d.ts +9 -0
  82. package/dist/session/prune-images.js +42 -0
  83. package/dist/skills/activate.d.ts +6 -0
  84. package/dist/skills/activate.js +72 -27
  85. package/dist/skills/index.d.ts +1 -1
  86. package/dist/skills/index.js +1 -1
  87. package/dist/telemetry/db.d.ts +63 -0
  88. package/dist/telemetry/db.js +193 -0
  89. package/dist/telemetry/index.d.ts +17 -0
  90. package/dist/telemetry/index.js +82 -0
  91. package/dist/telemetry/sanitize.d.ts +6 -0
  92. package/dist/telemetry/sanitize.js +48 -0
  93. package/dist/telemetry/sqlite-processor.d.ts +11 -0
  94. package/dist/telemetry/sqlite-processor.js +108 -0
  95. package/dist/telemetry/types.d.ts +30 -0
  96. package/dist/telemetry/types.js +31 -0
  97. package/dist/tools/builtin/index.d.ts +2 -0
  98. package/dist/tools/builtin/index.js +2 -0
  99. package/dist/tools/builtin/self-config.d.ts +4 -0
  100. package/dist/tools/builtin/self-config.js +182 -0
  101. package/dist/tools/registry.js +8 -1
  102. package/package.json +26 -20
package/dist/cli/chat.js CHANGED
@@ -1,14 +1,10 @@
1
- import * as readline from "readline";
2
- import * as path from "path";
3
1
  import { loadConfig } from "../config/index.js";
4
- import { buildAgentRuntime, buildSystemPrompt } from "../bootstrap.js";
5
- import { runAgentLoop } from "../agent/loop.js";
6
- import { compactMessages } from "../session/compaction.js";
2
+ import { buildAgentRuntime } from "../bootstrap.js";
7
3
  import { ensureOllama } from "./ollama.js";
8
4
  import { resolveApiKey } from "./ui.js";
9
- const CONFIG_PATH = path.join(process.cwd(), "agent-kit.json");
10
- const DATA_DIR = path.join(process.cwd(), "data");
11
- const SKILLS_DIR = path.join(process.cwd(), "skills");
5
+ import { showWhatsNew } from "./whats-new.js";
6
+ import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
7
+ import { startRepl } from "./repl.js";
12
8
  export async function chat(agentName) {
13
9
  if (!agentName) {
14
10
  const config = loadConfig(CONFIG_PATH);
@@ -24,20 +20,20 @@ export async function chat(agentName) {
24
20
  }
25
21
  }
26
22
  await resolveApiKey({ save: false });
23
+ showWhatsNew();
27
24
  const config = loadConfig(CONFIG_PATH);
28
25
  if (!config.agents[agentName]) {
29
26
  const names = Object.keys(config.agents).join(", ");
30
27
  console.error(`Agent "${agentName}" not found. Available: ${names}`);
31
28
  process.exit(1);
32
29
  }
33
- // Warn if Ollama isn't running (memory tools won't work)
34
30
  if (config.defaults.memory) {
35
31
  const ollama = await ensureOllama();
36
32
  if (!ollama.running) {
37
33
  console.log(" (Ollama not running — memory tools unavailable)\n");
38
34
  }
39
35
  }
40
- const runtime = buildAgentRuntime(agentName, config, {
36
+ const runtime = await buildAgentRuntime(agentName, config, {
41
37
  dataDir: DATA_DIR,
42
38
  skillsDir: SKILLS_DIR,
43
39
  });
@@ -54,58 +50,17 @@ export async function chat(agentName) {
54
50
  }
55
51
  console.log(` Commands: /new (reset), /quit`);
56
52
  console.log();
57
- let messages = session.getMessages();
58
- const rl = readline.createInterface({
59
- input: process.stdin,
60
- output: process.stdout,
53
+ startRepl({
54
+ agentName,
55
+ model: resolved.model,
56
+ fallbacks: resolved.fallbacks,
57
+ maxIterations: resolved.maxIterations,
58
+ compactionThreshold: resolved.compactionThreshold,
59
+ maxToolResultSize: resolved.maxToolResultSize,
60
+ toolRegistry,
61
+ session,
62
+ soul,
63
+ skillsIndex,
64
+ promptFragments,
61
65
  });
62
- const ask = () => {
63
- rl.question("You: ", async (input) => {
64
- const trimmed = input.trim();
65
- if (trimmed === "/quit") {
66
- rl.close();
67
- return;
68
- }
69
- if (trimmed === "/new") {
70
- messages = [];
71
- console.log(" Session reset.\n");
72
- ask();
73
- return;
74
- }
75
- if (!trimmed) {
76
- ask();
77
- return;
78
- }
79
- const userMsg = { role: "user", content: trimmed };
80
- messages.push(userMsg);
81
- session.append(userMsg);
82
- messages = await compactMessages(messages, resolved.model, resolved.compactionThreshold);
83
- try {
84
- const systemPrompt = buildSystemPrompt(soul, skillsIndex, promptFragments);
85
- const result = await runAgentLoop(messages, {
86
- model: resolved.model,
87
- fallbacks: resolved.fallbacks,
88
- systemPrompt,
89
- toolRegistry,
90
- maxIterations: resolved.maxIterations,
91
- compactionThreshold: resolved.compactionThreshold,
92
- maxToolResultSize: resolved.maxToolResultSize,
93
- agentName,
94
- source: "cli",
95
- });
96
- const newMessages = result.messages.slice(messages.length - 1);
97
- for (const msg of newMessages) {
98
- session.append(msg);
99
- }
100
- messages = result.messages;
101
- console.log(`\nAgent: ${result.text}`);
102
- console.log(` (${result.usage.inputTokens + result.usage.outputTokens} tokens)\n`);
103
- }
104
- catch (err) {
105
- console.error(`\n Error: ${err instanceof Error ? err.message : err}\n`);
106
- }
107
- ask();
108
- });
109
- };
110
- ask();
111
66
  }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * CLAUDE.md template generated by `agent-kit init`.
3
+ * Gives any Claude Code session full context for working on this project.
4
+ */
5
+ export declare const CLAUDE_MD_TEMPLATE = "# Agent Kit Project\n\nPersonal AI agent framework \u2014 multi-model agent loop with Slack gateway, cron scheduling, skills, and tool sandboxing.\n\nPowered by [`@alejandroroman/agent-kit`](https://www.npmjs.com/package/@alejandroroman/agent-kit).\n\n## Run Commands\n\n- `pnpm start` \u2014 start all services (cron, Slack, heartbeat, REPL)\n- `pnpm chat` \u2014 lightweight single-agent chat\n- `pnpm create` \u2014 AI-powered agent creation wizard\n\n## Project Layout\n\n```\nagent-kit.json # Main config \u2014 agents, cron jobs, models, defaults\n.env # Secrets (API keys, Slack tokens)\ndata/ # Runtime data (per-agent sessions, SOUL.md files, DBs)\n agents/<name>/\n SOUL.md # Agent personality / system prompt\n sessions/ # JSONL conversation logs\nskills/ # Custom skill definitions\n <skill-name>/\n skill.json # Manifest (name, description, tools list)\n SKILL.md # Instructions injected when skill is activated\n tools/ # Tool implementations (TypeScript)\n```\n\n## Configuration (agent-kit.json)\n\n### Models\n\n```json\n\"models\": [\n { \"model\": \"anthropic:claude-sonnet-4-6\", \"alias\": \"sonnet\" },\n { \"model\": \"anthropic:claude-haiku-4-5-20251001\", \"alias\": \"haiku\" }\n]\n```\n\nAgents reference models by alias. The framework handles automatic fallback between models on rate limits/server errors.\n\n### Agent Definition\n\n```json\n\"agents\": {\n \"my-agent\": {\n \"displayName\": \"My Agent\",\n \"emoji\": \"\uD83E\uDD16\",\n \"model\": \"sonnet\",\n \"tools\": [\"read_file\", \"write_file\", \"web_search\"],\n \"skills\": [\"my-skill\"],\n \"sandbox\": { \"allowedPaths\": [\"data/agents/my-agent/\"] },\n \"slack\": { \"channelId\": \"C0XXX\", \"channelName\": \"#my-channel\" },\n \"heartbeat\": { \"enabled\": true, \"intervalMinutes\": 60, \"model\": \"haiku\" },\n \"can_spawn\": [{ \"agent\": \"helper\", \"tool\": \"delegate\", \"description\": \"...\" }]\n }\n}\n```\n\n**Key fields:**\n- `tools` \u2014 builtin tools the agent can use (see list below)\n- `skills` \u2014 skill directories to make available via `activate_skill`\n- `sandbox` \u2014 restrict file access (`allowedPaths`) or commands (`allowedCommands`)\n- `slack` \u2014 bind agent to a Slack channel for inbound/outbound messages\n- `heartbeat` \u2014 periodic autonomous check-ins (posts to Slack if something noteworthy)\n- `spawn_only: true` \u2014 agent can only be invoked by other agents, not directly\n\n### Cron Jobs\n\n```json\n\"cron\": [\n {\n \"id\": \"daily-task\",\n \"agent\": \"my-agent\",\n \"schedule\": \"0 8 * * *\",\n \"prompt\": \"Activate the my-skill skill. Do the daily task.\",\n \"enabled\": true\n }\n]\n```\n\nSchedule uses standard cron syntax. Agents must activate their skill first in the prompt.\n\n### Defaults\n\n```json\n\"defaults\": {\n \"model\": \"sonnet\",\n \"maxTokens\": 4096,\n \"maxIterations\": 20,\n \"memory\": {\n \"dbPath\": \"./data/memory.db\",\n \"ollamaEndpoint\": \"http://localhost:11434\",\n \"ollamaModel\": \"all-minilm:l6-v2\"\n }\n}\n```\n\n## Built-in Tools\n\n| Tool | Description |\n|------|-------------|\n| `read_file` | Read a file (respects sandbox allowedPaths) |\n| `write_file` | Write a file (respects sandbox allowedPaths) |\n| `run_command` | Execute a shell command (respects sandbox allowedCommands) |\n| `web_search` | Search the web (Brave or Grok provider) |\n| `store_memory` | Store a memory with content and metadata |\n| `search_memory` | Semantic search across stored memories |\n| `get_memory` | Retrieve a specific memory by ID |\n| `update_memory` | Update an existing memory |\n| `forget_memory` | Delete a memory |\n| `list_memories` | List all memories with optional filtering |\n| `update_agent_config` | Agent updates its own config (allowlisted fields) |\n| `manage_cron` | Agent manages its own cron jobs (CRUD) |\n\nMemory tools require Ollama running with `all-minilm:l6-v2` model.\n\n## Creating Skills\n\nA skill is a directory in `skills/` with:\n\n1. **`skill.json`** \u2014 manifest\n```json\n{\n \"name\": \"my-skill\",\n \"description\": \"What this skill does\",\n \"tools\": [\"my_tool\"],\n \"prompt\": \"SKILL.md\"\n}\n```\n\n2. **`SKILL.md`** \u2014 instructions injected into agent context when activated\n\n3. **`tools/<name>.ts`** \u2014 tool implementations\n```typescript\nimport type { Tool } from \"@alejandroroman/agent-kit/dist/tools/types.js\";\n\nconst tool: Tool = {\n name: \"my_tool\",\n description: \"What this tool does\",\n parameters: {\n type: \"object\",\n properties: {\n input: { type: \"string\", description: \"The input\" }\n },\n required: [\"input\"]\n },\n execute: async (args) => {\n return \"result\";\n },\n};\n\nexport default tool;\n```\n\n## Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `ANTHROPIC_API_KEY` | Yes | Anthropic API key for Claude models |\n| `OPENAI_API_KEY` | No | OpenAI API key (for fallback models) |\n| `SLACK_BOT_TOKEN` | No | Slack bot token (xoxb-...) for gateway |\n| `SLACK_APP_TOKEN` | No | Slack app token (xapp-...) for Socket Mode |\n| `BRAVE_SEARCH_API_KEY` | No | Brave Search API key for web_search |\n| `XAI_API_KEY` | No | xAI/Grok API key for web_search |\n\n## Slack Setup\n\n1. Create a Slack app at api.slack.com/apps \u2192 From Manifest (YAML):\n```yaml\ndisplay_information:\n name: Agent Kit\nsettings:\n socket_mode_enabled: true\nfeatures:\n bot_user:\n display_name: Agent Kit\n always_online: true\n event_subscriptions:\n bot_events:\n - message.channels\n - message.groups\noauth_config:\n scopes:\n bot:\n - chat:write\n - channels:history\n - channels:read\n - groups:history\n - groups:read\n```\n2. Generate an App-Level Token (Settings \u2192 Basic Information) with `connections:write` scope \u2192 `SLACK_APP_TOKEN`\n3. Install to workspace \u2192 copy Bot Token \u2192 `SLACK_BOT_TOKEN`\n4. Create channels, invite the bot (`/invite @Agent Kit`), copy channel IDs\n5. Add `slack` binding to each agent in `agent-kit.json`\n\n## Ollama (for Memory)\n\nMemory tools use Ollama for local embeddings:\n```bash\n# Install: https://ollama.com\nollama pull all-minilm:l6-v2\n# Ollama must be running when agent-kit starts\n```\n\nIf Ollama isn't running, everything else works \u2014 memory tools are just unavailable.\n\n## pnpm v10 Note\n\nIf using pnpm v10+, native dependencies need explicit approval:\n```json\n// In package.json:\n\"pnpm\": { \"onlyBuiltDependencies\": [\"better-sqlite3\"] }\n```\nThen `pnpm install` to rebuild native bindings.\n";
@@ -0,0 +1,220 @@
1
+ /**
2
+ * CLAUDE.md template generated by `agent-kit init`.
3
+ * Gives any Claude Code session full context for working on this project.
4
+ */
5
+ export const CLAUDE_MD_TEMPLATE = `# Agent Kit Project
6
+
7
+ Personal AI agent framework — multi-model agent loop with Slack gateway, cron scheduling, skills, and tool sandboxing.
8
+
9
+ Powered by [\`@alejandroroman/agent-kit\`](https://www.npmjs.com/package/@alejandroroman/agent-kit).
10
+
11
+ ## Run Commands
12
+
13
+ - \`pnpm start\` — start all services (cron, Slack, heartbeat, REPL)
14
+ - \`pnpm chat\` — lightweight single-agent chat
15
+ - \`pnpm create\` — AI-powered agent creation wizard
16
+
17
+ ## Project Layout
18
+
19
+ \`\`\`
20
+ agent-kit.json # Main config — agents, cron jobs, models, defaults
21
+ .env # Secrets (API keys, Slack tokens)
22
+ data/ # Runtime data (per-agent sessions, SOUL.md files, DBs)
23
+ agents/<name>/
24
+ SOUL.md # Agent personality / system prompt
25
+ sessions/ # JSONL conversation logs
26
+ skills/ # Custom skill definitions
27
+ <skill-name>/
28
+ skill.json # Manifest (name, description, tools list)
29
+ SKILL.md # Instructions injected when skill is activated
30
+ tools/ # Tool implementations (TypeScript)
31
+ \`\`\`
32
+
33
+ ## Configuration (agent-kit.json)
34
+
35
+ ### Models
36
+
37
+ \`\`\`json
38
+ "models": [
39
+ { "model": "anthropic:claude-sonnet-4-6", "alias": "sonnet" },
40
+ { "model": "anthropic:claude-haiku-4-5-20251001", "alias": "haiku" }
41
+ ]
42
+ \`\`\`
43
+
44
+ Agents reference models by alias. The framework handles automatic fallback between models on rate limits/server errors.
45
+
46
+ ### Agent Definition
47
+
48
+ \`\`\`json
49
+ "agents": {
50
+ "my-agent": {
51
+ "displayName": "My Agent",
52
+ "emoji": "🤖",
53
+ "model": "sonnet",
54
+ "tools": ["read_file", "write_file", "web_search"],
55
+ "skills": ["my-skill"],
56
+ "sandbox": { "allowedPaths": ["data/agents/my-agent/"] },
57
+ "slack": { "channelId": "C0XXX", "channelName": "#my-channel" },
58
+ "heartbeat": { "enabled": true, "intervalMinutes": 60, "model": "haiku" },
59
+ "can_spawn": [{ "agent": "helper", "tool": "delegate", "description": "..." }]
60
+ }
61
+ }
62
+ \`\`\`
63
+
64
+ **Key fields:**
65
+ - \`tools\` — builtin tools the agent can use (see list below)
66
+ - \`skills\` — skill directories to make available via \`activate_skill\`
67
+ - \`sandbox\` — restrict file access (\`allowedPaths\`) or commands (\`allowedCommands\`)
68
+ - \`slack\` — bind agent to a Slack channel for inbound/outbound messages
69
+ - \`heartbeat\` — periodic autonomous check-ins (posts to Slack if something noteworthy)
70
+ - \`spawn_only: true\` — agent can only be invoked by other agents, not directly
71
+
72
+ ### Cron Jobs
73
+
74
+ \`\`\`json
75
+ "cron": [
76
+ {
77
+ "id": "daily-task",
78
+ "agent": "my-agent",
79
+ "schedule": "0 8 * * *",
80
+ "prompt": "Activate the my-skill skill. Do the daily task.",
81
+ "enabled": true
82
+ }
83
+ ]
84
+ \`\`\`
85
+
86
+ Schedule uses standard cron syntax. Agents must activate their skill first in the prompt.
87
+
88
+ ### Defaults
89
+
90
+ \`\`\`json
91
+ "defaults": {
92
+ "model": "sonnet",
93
+ "maxTokens": 4096,
94
+ "maxIterations": 20,
95
+ "memory": {
96
+ "dbPath": "./data/memory.db",
97
+ "ollamaEndpoint": "http://localhost:11434",
98
+ "ollamaModel": "all-minilm:l6-v2"
99
+ }
100
+ }
101
+ \`\`\`
102
+
103
+ ## Built-in Tools
104
+
105
+ | Tool | Description |
106
+ |------|-------------|
107
+ | \`read_file\` | Read a file (respects sandbox allowedPaths) |
108
+ | \`write_file\` | Write a file (respects sandbox allowedPaths) |
109
+ | \`run_command\` | Execute a shell command (respects sandbox allowedCommands) |
110
+ | \`web_search\` | Search the web (Brave or Grok provider) |
111
+ | \`store_memory\` | Store a memory with content and metadata |
112
+ | \`search_memory\` | Semantic search across stored memories |
113
+ | \`get_memory\` | Retrieve a specific memory by ID |
114
+ | \`update_memory\` | Update an existing memory |
115
+ | \`forget_memory\` | Delete a memory |
116
+ | \`list_memories\` | List all memories with optional filtering |
117
+ | \`update_agent_config\` | Agent updates its own config (allowlisted fields) |
118
+ | \`manage_cron\` | Agent manages its own cron jobs (CRUD) |
119
+
120
+ Memory tools require Ollama running with \`all-minilm:l6-v2\` model.
121
+
122
+ ## Creating Skills
123
+
124
+ A skill is a directory in \`skills/\` with:
125
+
126
+ 1. **\`skill.json\`** — manifest
127
+ \`\`\`json
128
+ {
129
+ "name": "my-skill",
130
+ "description": "What this skill does",
131
+ "tools": ["my_tool"],
132
+ "prompt": "SKILL.md"
133
+ }
134
+ \`\`\`
135
+
136
+ 2. **\`SKILL.md\`** — instructions injected into agent context when activated
137
+
138
+ 3. **\`tools/<name>.ts\`** — tool implementations
139
+ \`\`\`typescript
140
+ import type { Tool } from "@alejandroroman/agent-kit/dist/tools/types.js";
141
+
142
+ const tool: Tool = {
143
+ name: "my_tool",
144
+ description: "What this tool does",
145
+ parameters: {
146
+ type: "object",
147
+ properties: {
148
+ input: { type: "string", description: "The input" }
149
+ },
150
+ required: ["input"]
151
+ },
152
+ execute: async (args) => {
153
+ return "result";
154
+ },
155
+ };
156
+
157
+ export default tool;
158
+ \`\`\`
159
+
160
+ ## Environment Variables
161
+
162
+ | Variable | Required | Description |
163
+ |----------|----------|-------------|
164
+ | \`ANTHROPIC_API_KEY\` | Yes | Anthropic API key for Claude models |
165
+ | \`OPENAI_API_KEY\` | No | OpenAI API key (for fallback models) |
166
+ | \`SLACK_BOT_TOKEN\` | No | Slack bot token (xoxb-...) for gateway |
167
+ | \`SLACK_APP_TOKEN\` | No | Slack app token (xapp-...) for Socket Mode |
168
+ | \`BRAVE_SEARCH_API_KEY\` | No | Brave Search API key for web_search |
169
+ | \`XAI_API_KEY\` | No | xAI/Grok API key for web_search |
170
+
171
+ ## Slack Setup
172
+
173
+ 1. Create a Slack app at api.slack.com/apps → From Manifest (YAML):
174
+ \`\`\`yaml
175
+ display_information:
176
+ name: Agent Kit
177
+ settings:
178
+ socket_mode_enabled: true
179
+ features:
180
+ bot_user:
181
+ display_name: Agent Kit
182
+ always_online: true
183
+ event_subscriptions:
184
+ bot_events:
185
+ - message.channels
186
+ - message.groups
187
+ oauth_config:
188
+ scopes:
189
+ bot:
190
+ - chat:write
191
+ - channels:history
192
+ - channels:read
193
+ - groups:history
194
+ - groups:read
195
+ \`\`\`
196
+ 2. Generate an App-Level Token (Settings → Basic Information) with \`connections:write\` scope → \`SLACK_APP_TOKEN\`
197
+ 3. Install to workspace → copy Bot Token → \`SLACK_BOT_TOKEN\`
198
+ 4. Create channels, invite the bot (\`/invite @Agent Kit\`), copy channel IDs
199
+ 5. Add \`slack\` binding to each agent in \`agent-kit.json\`
200
+
201
+ ## Ollama (for Memory)
202
+
203
+ Memory tools use Ollama for local embeddings:
204
+ \`\`\`bash
205
+ # Install: https://ollama.com
206
+ ollama pull all-minilm:l6-v2
207
+ # Ollama must be running when agent-kit starts
208
+ \`\`\`
209
+
210
+ If Ollama isn't running, everything else works — memory tools are just unavailable.
211
+
212
+ ## pnpm v10 Note
213
+
214
+ If using pnpm v10+, native dependencies need explicit approval:
215
+ \`\`\`json
216
+ // In package.json:
217
+ "pnpm": { "onlyBuiltDependencies": ["better-sqlite3"] }
218
+ \`\`\`
219
+ Then \`pnpm install\` to rebuild native bindings.
220
+ `;
@@ -103,6 +103,9 @@ export function scaffoldProjectPackageJson(projectDir, projectName) {
103
103
  dependencies: {
104
104
  "@alejandroroman/agent-kit": "^0.1.0",
105
105
  },
106
+ pnpm: {
107
+ onlyBuiltDependencies: ["better-sqlite3"],
108
+ },
106
109
  };
107
110
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
108
111
  // Create .gitignore if missing
@@ -1,12 +1,9 @@
1
1
  import * as fs from "fs";
2
- import * as path from "path";
3
2
  import * as p from "@clack/prompts";
4
3
  import { banner, done, resolveApiKey } from "./ui.js";
5
4
  import { loadConfig } from "../config/index.js";
6
5
  import { runSetupAgent } from "./setup-agent/index.js";
7
- const CONFIG_PATH = path.join(process.cwd(), "agent-kit.json");
8
- const DATA_DIR = path.join(process.cwd(), "data");
9
- const SKILLS_DIR = path.join(process.cwd(), "skills");
6
+ import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
10
7
  export async function create() {
11
8
  banner();
12
9
  // Require existing config
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Parse a .env file into a key-value map.
3
+ * Strips quotes, ignores comments and blank lines.
4
+ */
5
+ export declare function loadEnvFile(envPath: string): Record<string, string>;
6
+ /**
7
+ * Upsert a key in a .env file. Replaces if exists, appends if not.
8
+ * Values are always quoted to prevent shell issues.
9
+ */
10
+ export declare function upsertEnvKey(envPath: string, key: string, value: string): void;
11
+ /**
12
+ * Load specific keys from .env into process.env (if not already set).
13
+ */
14
+ export declare function loadEnvIntoProcess(envPath: string, keys: string[]): void;
@@ -0,0 +1,68 @@
1
+ import * as fs from "fs";
2
+ /**
3
+ * Parse a .env file into a key-value map.
4
+ * Strips quotes, ignores comments and blank lines.
5
+ */
6
+ export function loadEnvFile(envPath) {
7
+ if (!fs.existsSync(envPath))
8
+ return {};
9
+ const result = {};
10
+ const content = fs.readFileSync(envPath, "utf-8");
11
+ for (const line of content.split("\n")) {
12
+ const trimmed = line.trim();
13
+ if (!trimmed || trimmed.startsWith("#"))
14
+ continue;
15
+ const eqIndex = trimmed.indexOf("=");
16
+ if (eqIndex === -1)
17
+ continue;
18
+ const key = trimmed.slice(0, eqIndex).trim();
19
+ let value = trimmed.slice(eqIndex + 1).trim();
20
+ if ((value.startsWith('"') && value.endsWith('"')) ||
21
+ (value.startsWith("'") && value.endsWith("'"))) {
22
+ value = value.slice(1, -1);
23
+ }
24
+ result[key] = value;
25
+ }
26
+ return result;
27
+ }
28
+ /**
29
+ * Upsert a key in a .env file. Replaces if exists, appends if not.
30
+ * Values are always quoted to prevent shell issues.
31
+ */
32
+ export function upsertEnvKey(envPath, key, value) {
33
+ const quotedLine = `${key}="${value}"`;
34
+ if (!fs.existsSync(envPath)) {
35
+ fs.writeFileSync(envPath, quotedLine + "\n", { mode: 0o600 });
36
+ return;
37
+ }
38
+ const content = fs.readFileSync(envPath, "utf-8");
39
+ const lines = content.split("\n");
40
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
41
+ const pattern = new RegExp(`^${escaped}\\s*=`);
42
+ let replaced = false;
43
+ const updated = lines.map((line) => {
44
+ if (pattern.test(line.trim())) {
45
+ replaced = true;
46
+ return quotedLine;
47
+ }
48
+ return line;
49
+ });
50
+ if (!replaced) {
51
+ const needsNewline = content.length > 0 && !content.endsWith("\n");
52
+ fs.writeFileSync(envPath, content + (needsNewline ? "\n" : "") + quotedLine + "\n", { mode: 0o600 });
53
+ }
54
+ else {
55
+ fs.writeFileSync(envPath, updated.join("\n"), { mode: 0o600 });
56
+ }
57
+ }
58
+ /**
59
+ * Load specific keys from .env into process.env (if not already set).
60
+ */
61
+ export function loadEnvIntoProcess(envPath, keys) {
62
+ const env = loadEnvFile(envPath);
63
+ for (const key of keys) {
64
+ if (!process.env[key] && env[key]) {
65
+ process.env[key] = env[key];
66
+ }
67
+ }
68
+ }
package/dist/cli/init.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import * as fs from "fs";
2
- import * as path from "path";
3
2
  import { execSync } from "child_process";
4
3
  import * as p from "@clack/prompts";
5
4
  import { banner, isCancel, done, resolveApiKey } from "./ui.js";
6
5
  import { checkOllama } from "./ollama.js";
7
6
  import { createFreshConfig, scaffoldProjectPackageJson } from "./config-writer.js";
8
7
  import { runSetupAgent } from "./setup-agent/index.js";
9
- const CONFIG_PATH = path.join(process.cwd(), "agent-kit.json");
10
- const DATA_DIR = path.join(process.cwd(), "data");
11
- const SKILLS_DIR = path.join(process.cwd(), "skills");
8
+ import { setupSlack } from "./slack-setup.js";
9
+ import { CONFIG_PATH, DATA_DIR, SKILLS_DIR } from "./paths.js";
10
+ import { CLAUDE_MD_TEMPLATE } from "./claude-md-template.js";
11
+ import * as path from "path";
12
12
  function detectPackageManager() {
13
13
  try {
14
14
  execSync("pnpm --version", { stdio: "ignore" });
@@ -43,6 +43,11 @@ export async function init() {
43
43
  createFreshConfig({ configPath: CONFIG_PATH });
44
44
  fs.mkdirSync(DATA_DIR, { recursive: true });
45
45
  fs.mkdirSync(SKILLS_DIR, { recursive: true });
46
+ // Generate CLAUDE.md for Claude Code context
47
+ const claudeMdPath = path.join(process.cwd(), "CLAUDE.md");
48
+ if (!fs.existsSync(claudeMdPath)) {
49
+ fs.writeFileSync(claudeMdPath, CLAUDE_MD_TEMPLATE);
50
+ }
46
51
  }
47
52
  catch (err) {
48
53
  s.stop(`Failed to create project: ${err instanceof Error ? err.message : err}`);
@@ -53,13 +58,13 @@ export async function init() {
53
58
  const pm = detectPackageManager();
54
59
  s.start(`Installing dependencies (${pm} install)...`);
55
60
  try {
56
- execSync(`${pm} install`, { cwd: process.cwd(), stdio: "ignore" });
61
+ execSync(`${pm} install`, { cwd: process.cwd(), stdio: "pipe" });
62
+ s.stop("Dependencies installed");
57
63
  }
58
64
  catch (err) {
59
65
  s.stop(`Failed to install dependencies: ${err instanceof Error ? err.message : err}`);
60
- p.log.warn("You can install manually later with: npm install");
66
+ p.log.warn(`Run \`${pm} install\` manually before starting.`);
61
67
  }
62
- s.stop("Dependencies installed");
63
68
  // Ask if they want to create agents now
64
69
  const createNow = await p.confirm({
65
70
  message: "Create your first agent now? (AI-powered setup)",
@@ -82,4 +87,6 @@ export async function init() {
82
87
  });
83
88
  console.log();
84
89
  done(summary);
90
+ // Offer Slack setup after agent creation
91
+ await setupSlack({ configPath: CONFIG_PATH, fromInit: true });
85
92
  }
package/dist/cli/list.js CHANGED
@@ -1,6 +1,5 @@
1
- import * as path from "path";
2
1
  import { loadConfig } from "../config/index.js";
3
- const CONFIG_PATH = path.join(process.cwd(), "agent-kit.json");
2
+ import { CONFIG_PATH } from "./paths.js";
4
3
  export function list() {
5
4
  const config = loadConfig(CONFIG_PATH);
6
5
  const agents = Object.entries(config.agents);
@@ -0,0 +1,3 @@
1
+ export declare const CONFIG_PATH: string;
2
+ export declare const DATA_DIR: string;
3
+ export declare const SKILLS_DIR: string;
@@ -0,0 +1,4 @@
1
+ import * as path from "path";
2
+ export const CONFIG_PATH = path.join(process.cwd(), "agent-kit.json");
3
+ export const DATA_DIR = path.join(process.cwd(), "data");
4
+ export const SKILLS_DIR = path.join(process.cwd(), "skills");
@@ -0,0 +1,23 @@
1
+ import type { ToolRegistry } from "../tools/registry.js";
2
+ import type { SessionManager } from "../session/manager.js";
3
+ import type { UsageStore } from "../usage/index.js";
4
+ interface ReplOptions {
5
+ agentName: string;
6
+ model: string;
7
+ fallbacks: string[];
8
+ maxIterations: number;
9
+ compactionThreshold: number;
10
+ maxToolResultSize: number;
11
+ toolRegistry: ToolRegistry;
12
+ session: SessionManager;
13
+ soul: string | undefined;
14
+ skillsIndex: string;
15
+ promptFragments: string[];
16
+ usageStore?: UsageStore;
17
+ /** Extra commands beyond /quit and /new */
18
+ commands?: Record<string, () => void>;
19
+ /** Called on /quit before closing */
20
+ onQuit?: () => Promise<void>;
21
+ }
22
+ export declare function startRepl(opts: ReplOptions): void;
23
+ export {};
@@ -0,0 +1,73 @@
1
+ import * as readline from "readline";
2
+ import { runAgentLoop } from "../agent/loop.js";
3
+ import { compactMessages } from "../session/compaction.js";
4
+ import { buildSystemPrompt } from "../bootstrap.js";
5
+ export function startRepl(opts) {
6
+ let messages = opts.session.getMessages();
7
+ const rl = readline.createInterface({
8
+ input: process.stdin,
9
+ output: process.stdout,
10
+ });
11
+ const ask = () => {
12
+ rl.question("You: ", async (input) => {
13
+ const trimmed = input.trim();
14
+ if (trimmed === "/quit") {
15
+ try {
16
+ await opts.onQuit?.();
17
+ }
18
+ catch (err) {
19
+ console.error(`Warning: cleanup error: ${err instanceof Error ? err.message : err}`);
20
+ }
21
+ rl.close();
22
+ return;
23
+ }
24
+ if (trimmed === "/new") {
25
+ messages = [];
26
+ console.log(" Session reset.\n");
27
+ ask();
28
+ return;
29
+ }
30
+ // Custom commands
31
+ if (opts.commands && trimmed in opts.commands) {
32
+ opts.commands[trimmed]();
33
+ ask();
34
+ return;
35
+ }
36
+ if (!trimmed) {
37
+ ask();
38
+ return;
39
+ }
40
+ const userMsg = { role: "user", content: trimmed };
41
+ messages.push(userMsg);
42
+ opts.session.append(userMsg);
43
+ messages = await compactMessages(messages, opts.model, opts.compactionThreshold);
44
+ try {
45
+ const systemPrompt = buildSystemPrompt(opts.soul, opts.skillsIndex, opts.promptFragments);
46
+ const result = await runAgentLoop(messages, {
47
+ model: opts.model,
48
+ fallbacks: opts.fallbacks,
49
+ systemPrompt,
50
+ toolRegistry: opts.toolRegistry,
51
+ maxIterations: opts.maxIterations,
52
+ compactionThreshold: opts.compactionThreshold,
53
+ maxToolResultSize: opts.maxToolResultSize,
54
+ agentName: opts.agentName,
55
+ usageStore: opts.usageStore,
56
+ source: "cli",
57
+ });
58
+ const newMessages = result.messages.slice(messages.length);
59
+ for (const msg of newMessages) {
60
+ opts.session.append(msg);
61
+ }
62
+ messages = result.messages;
63
+ console.log(`\nAgent: ${result.text}`);
64
+ console.log(` (${result.usage.inputTokens + result.usage.outputTokens} tokens)\n`);
65
+ }
66
+ catch (err) {
67
+ console.error(`\n Error: ${err instanceof Error ? err.message : err}\n`);
68
+ }
69
+ ask();
70
+ });
71
+ };
72
+ ask();
73
+ }
@@ -0,0 +1,6 @@
1
+ interface SetupSlackOpts {
2
+ configPath: string;
3
+ fromInit?: boolean;
4
+ }
5
+ export declare function setupSlack(opts: SetupSlackOpts): Promise<void>;
6
+ export {};