Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ adk-claw/
│ ├── AGENTS.md # Agent configuration template
│ ├── SOUL.md # Agent personality template
│ ├── USER.md # User profile template
│ ├── .gitignore
│ └── memory/ # Memory file templates
│ └── .gitignore
├── dist/ # Compiled output (includes templates after build)
└── .adk-claw/ # User's local config (created by init, gitignored)
├── config.json # User configuration (API keys, etc.)
Expand All @@ -59,7 +58,7 @@ pnpm lint:fix # Fix linting issues
3. Creates `.adk-claw/config.json` with all settings
4. Copies templates from `templates/workspace/` to `.adk-claw/workspace/`:
- AGENTS.md, SOUL.md, USER.md (with placeholder replacement)
- memory/facts.md, context.md, preferences.md
- Creates empty `memory/` and `skills/` directories (ADK MemoryService creates memory files automatically)
5. Initializes git in workspace
6. User runs `adk-claw start` to launch the agent

Expand Down Expand Up @@ -108,7 +107,6 @@ This copies templates to `dist/templates/` so the built CLI can find them.
## Known Issues / TODOs

- The `@iqai/adk` package may show warnings about missing CLI files (can be ignored)
- Memory tools in `src/tools/memoryTools.ts` has uncommitted changes

## Tips for Future Sessions

Expand Down
23 changes: 5 additions & 18 deletions CODEBASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,29 +145,17 @@ Telegram Flow:
- **Exports**: `startTelegramBot()`
- **Flow**:
1. Creates agent with `createClawdAgent({ channel: "telegram" })`
2. Wires SessionManager with memory service
3. Creates sampling handler that:
2. Creates sampling handler that:
- Parses incoming MCP messages (`parseTelegramMessage()`)
- Routes `/start`, `/new`, `/reset`, `/help` commands
- Checks pairing for non-command messages
- Forwards to `runner.ask()` for AI responses
4. Initializes Telegram agent (MCP tools)
5. Registers bot commands with Telegram API
6. Initializes scheduler
3. Initializes Telegram agent (MCP tools)
4. Registers bot commands with Telegram API
5. Initializes scheduler
- **Message format**: Key-value format from MCP (user_id, chat_id, content fields)
- **Bot commands**: `/start`, `/new` (save & reset), `/reset` (clear), `/help`

### `src/services/SessionManager.ts` (Session State)
- **Purpose**: Per-chat session tracking with memory persistence
- **Exports**: `sessionManager` (singleton instance)
- **Class**: `SessionManager`
- `setDeps(memoryService, sessionService, adkSession)` — late-bound dependencies
- `getOrCreate(chatId, userId, username?)` → Session
- `addMessage(chatId, role, content)`
- `saveAndReset(chatId)` → summary string (saves to memory via ADK)
- `reset(chatId)` — clears without saving
- `getHistory(chatId)` / `getRecentHistory(chatId, count?)`

### `src/services/SchedulerService.ts` (Cron Jobs)
- **Purpose**: Manages scheduled/recurring tasks via `@iqai/adk` `AgentScheduler`
- **Exports**:
Expand All @@ -179,7 +167,7 @@ Telegram Flow:
- **Key**: Jobs are configured in `config.json` cron section, results broadcast to all paired Telegram users

### `src/services/index.ts` (Services Barrel)
- **Re-exports**: `initScheduler`, `stopScheduler`, `sessionManager`, `startTelegramBot`
- **Re-exports**: `initScheduler`, `stopScheduler`, `startTelegramBot`

---

Expand Down Expand Up @@ -361,7 +349,6 @@ src/services/TelegramService.ts
├── lib/logger.ts, lib/pairing.ts
├── tools/scheduleTools.ts (setSchedulerDeps)
├── services/SchedulerService.ts
└── services/SessionManager.ts

src/agents/agent.ts
├── config/index.ts (getConfig)
Expand Down
17 changes: 0 additions & 17 deletions src/agents/config/agent.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as path from "node:path";
import matter from "gray-matter";
import { configExists, getConfig } from "../../config/index.js";
import { HEARTBEAT_OK, SILENT_REPLY_TOKEN } from "../../lib/tokens.js";
import { buildSkillsPrompt } from "../../skills/SkillService.js";
import type {
AgentsConfig,
RuntimeInfo,
Expand Down Expand Up @@ -396,22 +395,6 @@ export function buildSystemPromptFromParams(
return buildSystemPrompt(params.workspaceConfig, params.runtime);
}

/**
* Build the full system prompt with skills included (async version)
*/
export async function buildSystemPromptWithSkills(
config: WorkspaceConfig,
): Promise<string> {
const basePrompt = buildSystemPrompt(config);
const skillsPrompt = await buildSkillsPrompt(config.workspacePath);

if (skillsPrompt) {
return `${basePrompt}\n\n${skillsPrompt}`;
}

return basePrompt;
}

/**
* Check if workspace exists and has required files
*/
Expand Down
98 changes: 98 additions & 0 deletions src/cli/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* CLI command for interactive terminal chat with the agent
*/

import { existsSync } from "node:fs";
import { join } from "node:path";
import * as p from "@clack/prompts";
import pc from "picocolors";

const CONFIG_PATH = join(process.cwd(), ".adk-claw", "config.json");

export async function chat() {
if (!existsSync(CONFIG_PATH)) {
p.log.error("ADK Claw is not initialized. Run 'adk-claw init' first.");
process.exit(1);
}
Comment on lines +13 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic to check for the existence of config.json is repeated in several of the new CLI command files (e.g., chat.ts, config.ts). To improve maintainability and reduce duplication, consider extracting this into a shared utility function.

For example, you could create a function ensureConfigExists() in a shared utility file:

// in a shared lib/cli-utils.ts file
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import * as p from '@clack/prompts';

export function ensureConfigExists() {
  const CONFIG_PATH = join(process.cwd(), '.adk-claw', 'config.json');
  if (!existsSync(CONFIG_PATH)) {
    p.log.error("ADK Claw is not initialized. Run 'adk-claw init' first.");
    process.exit(1);
  }
}

Then you can just call ensureConfigExists() at the start of each command function that requires it.


const s = p.spinner();
s.start("Initializing agent...");

const { createClawdAgent } = await import("../agents/agent.js");
const { getConfig } = await import("../config/index.js");

let agentResult: Awaited<ReturnType<typeof createClawdAgent>>;
let agentName: string;

try {
agentResult = await createClawdAgent({ channel: "cli" });
agentName = getConfig().agentName;
s.stop(`${pc.cyan(agentName)} is ready`);
} catch (err) {
s.stop("Failed to initialize agent");
p.log.error(err instanceof Error ? err.message : "Unknown error");
process.exit(1);
}

p.intro(
`${pc.bold(pc.magenta(agentName))} ${pc.dim("type /help for commands, /exit to quit")}`,
);

while (true) {
const input = await p.text({
message: pc.cyan("You:"),
placeholder: "Say something...",
});

if (p.isCancel(input)) {
break;
}

const trimmed = (input as string).trim();

if (!trimmed) {
continue;
}

if (trimmed === "/exit" || trimmed === "/quit") {
break;
}

if (trimmed === "/help") {
console.log(`
${pc.bold("Commands:")}
/help Show this help
/new Start a fresh session
/exit Quit the chat
`);
continue;
}

if (trimmed === "/new") {
const newS = p.spinner();
newS.start("Starting new session...");
try {
agentResult = await createClawdAgent({ channel: "cli" });
newS.stop("New session started");
} catch (err) {
newS.stop("Failed to start new session");
p.log.error(err instanceof Error ? err.message : "Unknown error");
}
continue;
}

const thinkS = p.spinner();
thinkS.start("Thinking...");

try {
const response = await agentResult.runner.ask(trimmed);
thinkS.stop("");
console.log(`\n ${pc.bold(pc.magenta(agentName))}: ${response}\n`);
} catch (err) {
thinkS.stop("Error");
p.log.error(err instanceof Error ? err.message : "Unknown error");
}
}

p.outro("Goodbye!");
}
Loading
Loading