Skip to content

Commit a97228e

Browse files
committed
refactor: unify prompt/status formatting and bump version
1 parent 163818b commit a97228e

File tree

7 files changed

+149
-610
lines changed

7 files changed

+149
-610
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
{
22
"name": "ode",
3-
"version": "0.0.33",
3+
"version": "0.0.34",
44
"description": "Ode - OpenCode chat controller for Slack",
55
"module": "packages/core/index.ts",
66
"type": "module",
77
"private": true,
88
"scripts": {
9+
"setup": "bun install --concurrent-scripts=1 --network-concurrency=8 && bun install --concurrent-scripts=1 --network-concurrency=8 --cwd packages/web-ui",
910
"start": "bun run packages/core/index.ts",
1011
"dev": "bun --watch run packages/core/index.ts --local",
1112
"postinstall": "bun install --cwd packages/web-ui",
13+
"test:ci": "bun run setup && bun test",
1214
"build:web": "bun --cwd packages/web-ui build",
15+
"check:web": "bun run setup && bun --cwd packages/web-ui run check",
1316
"embed:web": "bun run scripts/embed-web-assets.ts",
1417
"typecheck": "tsc --noEmit"
1518
},

packages/agents/claude/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
setOpenCodeSession,
55
} from "@/config/local/settings";
66
import { log } from "@/utils";
7-
import { buildPromptParts, buildPromptText, buildSlackSystemPrompt } from "../shared";
7+
import { buildPromptParts, buildPromptText, buildSystemPrompt } from "../shared";
88
import type {
99
OpenCodeMessage,
1010
OpenCodeMessageContext,
@@ -310,7 +310,7 @@ export async function sendMessage(
310310

311311
const parts = buildPromptParts(channelId, message, { ...options, agent }, context);
312312
const prompt = buildPromptText(parts);
313-
const systemPrompt = buildSlackSystemPrompt(context?.slack);
313+
const systemPrompt = buildSystemPrompt(context?.slack);
314314

315315
const isNewSession = newSessions.has(sessionId);
316316
const args = buildClaudeCommandArgs({

packages/agents/opencode/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from "@/config/local/settings";
1313
import { getChannelModel, isLocalMode } from "@/config";
1414
import { log } from "@/utils";
15-
import { buildPromptParts, buildSlackSystemPrompt } from "../shared";
15+
import { buildPromptParts, buildSystemPrompt } from "../shared";
1616
import type {
1717
OpenCodeMessage,
1818
OpenCodeMessageContext,
@@ -197,7 +197,7 @@ export async function sendMessage(
197197
const parts = buildPromptParts(channelId, message, { ...options, agent }, context);
198198

199199
// Build system prompt with Slack context
200-
const system = buildSlackSystemPrompt(context?.slack);
200+
const system = buildSystemPrompt(context?.slack);
201201
const payload = { directory: workingPath, parts, agent, model, system };
202202
// const payload = { directory: workingPath, parts, agent, model };
203203
const serverUrl = getSessionServerUrl(activeSessionId);

packages/agents/shared.ts

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
import type { OpenCodeMessageContext, OpenCodeOptions, PromptPart, SlackContext } from "./types";
66
import { getSlackActionApiUrl } from "@/config";
77

8-
export function buildSlackSystemPrompt(slack?: SlackContext): string {
8+
export function buildSystemPrompt(slack?: SlackContext): string {
99
const lines = [
1010
"COMMUNICATION STYLE:",
1111
"- Be concise and conversational - this is chat, not documentation",
@@ -23,46 +23,46 @@ export function buildSlackSystemPrompt(slack?: SlackContext): string {
2323
"- Prefer: git commit --author=\"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\" -m \"...\"",
2424
"- Use GH_TOKEN for gh commands when available; fall back to git commands when GH_TOKEN is not set.",
2525
"",
26-
"SLACK CONTEXT:",
2726
];
2827

2928
if (slack) {
29+
lines.push("SLACK CONTEXT:");
3030
lines.push(`- Channel: ${slack.channelId}`);
3131
lines.push(`- Thread: ${slack.threadId}`);
3232
lines.push(`- User: <@${slack.userId}>`);
3333
if (slack.hasGitHubToken !== undefined) {
3434
lines.push(`- GitHub token available: ${slack.hasGitHubToken ? "yes" : "no"}`);
3535
}
36-
}
3736

38-
lines.push("");
39-
lines.push("SLACK ACTIONS:");
40-
if (slack?.hasCustomSlackTool) {
41-
lines.push("- Use `ode_action` tool for Slack actions (messages, reactions, thread history, questions, uploads).");
42-
} else {
43-
const baseUrl = slack?.odeSlackApiUrl ?? getSlackActionApiUrl();
44-
lines.push("- Use bash + curl to call the Ode Slack API.");
45-
lines.push(`- Endpoint: ${baseUrl}/action`);
46-
lines.push("- Payload: {\"action\":\"post_message\",\"channelId\":\"...\",\"threadId\":\"...\",\"messageId\":\"...\",\"text\":\"...\"}");
37+
lines.push("");
38+
lines.push("SLACK ACTIONS:");
39+
if (slack.hasCustomSlackTool) {
40+
lines.push("- Use `ode_action` tool for Slack actions (messages, reactions, thread history, questions, uploads).");
41+
} else {
42+
const baseUrl = slack.odeSlackApiUrl ?? getSlackActionApiUrl();
43+
lines.push("- Use bash + curl to call the Ode Slack API.");
44+
lines.push(`- Endpoint: ${baseUrl}/action`);
45+
lines.push("- Payload: {\"action\":\"post_message\",\"channelId\":\"...\",\"threadId\":\"...\",\"messageId\":\"...\",\"text\":\"...\"}");
46+
}
47+
lines.push("- Supported actions: post_message, add_reaction, get_thread_messages, ask_user, get_user_info, upload_file.");
48+
lines.push("- Required fields: channelId; threadId for thread actions; messageId + emoji for reactions; userId for get_user_info.");
49+
lines.push("- add_reaction schema: { action: \"add_reaction\", channelId: string, messageId: string, emoji: \"thumbsup\" | \"eyes\" | \"ok_hand\" }");
50+
lines.push("- You can use any tool available via bash, curl");
51+
lines.push("");
52+
lines.push("IMPORTANT: Your text output is automatically posted to Slack.");
53+
lines.push("- When asking the user to choose options, you can send an ask_user Slack action, do NOT also output text - the buttons are enough.");
54+
lines.push("- Only output text OR use a messaging tool, never both.");
55+
lines.push("");
56+
lines.push("FORMATTING:");
57+
lines.push("- Slack uses *bold* and _italic_ (not **bold** or *italic*)");
58+
lines.push("- Use ` for inline code and ``` for code blocks");
59+
lines.push("- Keep responses readable on mobile screens");
60+
lines.push("");
61+
lines.push("TASK LISTS:");
62+
lines.push("- When sharing tasks, put each item on its own line");
63+
lines.push("- Use four states: * not started, ♻️ in progress, ✅ done, 🚫 cancelled");
64+
lines.push("- If you include a task list, keep the tasks you have done at the top of the response");
4765
}
48-
lines.push("- Supported actions: post_message, add_reaction, get_thread_messages, ask_user, get_user_info, upload_file.");
49-
lines.push("- Required fields: channelId; threadId for thread actions; messageId + emoji for reactions; userId for get_user_info.");
50-
lines.push("- add_reaction schema: { action: \"add_reaction\", channelId: string, messageId: string, emoji: \"thumbsup\" | \"eyes\" | \"ok_hand\" }");
51-
lines.push("- You can use any tool available via bash, curl");
52-
lines.push("");
53-
lines.push("IMPORTANT: Your text output is automatically posted to Slack.");
54-
lines.push("- When asking the user to choose options, you can send an ask_user Slack action, do NOT also output text - the buttons are enough.");
55-
lines.push("- Only output text OR use a messaging tool, never both.");
56-
lines.push("");
57-
lines.push("FORMATTING:");
58-
lines.push("- Slack uses *bold* and _italic_ (not **bold** or *italic*)");
59-
lines.push("- Use ` for inline code and ``` for code blocks");
60-
lines.push("- Keep responses readable on mobile screens");
61-
lines.push("");
62-
lines.push("TASK LISTS:");
63-
lines.push("- When sharing tasks, put each item on its own line");
64-
lines.push("- Use four states: * not started, ♻️ in progress, ✅ done, 🚫 cancelled");
65-
lines.push("- If you include a task list, keep the tasks you have done at the top of the response");
6666

6767
return lines.join("\n");
6868
}

packages/agents/test/cli-command.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "bun:test";
2-
import { buildPromptParts, buildPromptText, buildSlackSystemPrompt } from "../shared";
2+
import { buildPromptParts, buildPromptText, buildSystemPrompt } from "../shared";
33
import { buildOpenCodeCommand } from "../opencode/client";
44
import { buildClaudeCommand, buildClaudeCommandArgs } from "../claude/client";
55

@@ -8,7 +8,7 @@ describe("agent cli command formatting", () => {
88
const message = "hello world";
99
const parts = buildPromptParts("C123", message);
1010
const prompt = buildPromptText(parts);
11-
const systemPrompt = buildSlackSystemPrompt({
11+
const systemPrompt = buildSystemPrompt({
1212
channelId: "C123",
1313
threadId: "T456",
1414
userId: "U789",

packages/utils/status.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ export function trimToolPath(label: string, workingPath: string): string {
7777
function formatTodoLines(todos: TrackedTodo[], limit = PLAN_TODO_LIMIT): string[] {
7878
const lines: string[] = [];
7979
for (const todo of todos.slice(0, limit)) {
80-
const icon = getTodoIcon(todo.status);
81-
lines.push(`${icon} ${todo.content}`);
80+
const statusLabel = todo.status === "in_progress"
81+
? "in progress"
82+
: todo.status;
83+
lines.push(`\`${statusLabel}\` ${todo.content}`);
8284
}
8385
if (todos.length > limit) {
8486
lines.push(`_(+${todos.length - limit} more)_`);
@@ -147,8 +149,8 @@ export function buildToolLines(
147149
const { itemLimit, detailLimit } = TOOL_DISPLAY_CONFIG[frequency];
148150
const items = tools.length > itemLimit ? tools.slice(-itemLimit) : tools;
149151
const header = tools.length > itemLimit
150-
? `Tool execution (Last ${itemLimit} items in ${tools.length})`
151-
: "Tool execution";
152+
? `*Tool execution (Last ${itemLimit} items in ${tools.length})*`
153+
: "*Tool execution*";
152154

153155
const lines = [header];
154156
const codeMark = "`";
@@ -194,11 +196,12 @@ export function buildLiveStatusMessage(
194196
content: todo.content,
195197
status: todo.status as TrackedTodo["status"],
196198
}));
197-
lines.push("Tasks", ...formatTodoLines(todos));
199+
lines.push("", "*Tasks*", ...formatTodoLines(todos));
198200
}
199201

200202
const toolLines = buildToolLines(state, workingPath, resolveMessageFrequency());
201203
if (toolLines.length > 0) {
204+
lines.push("");
202205
lines.push(...toolLines);
203206
}
204207

0 commit comments

Comments
 (0)