feat: enrich system prompt docs guidance
This commit is contained in:
14
CHANGELOG.md
14
CHANGELOG.md
@@ -10,15 +10,19 @@ Docs: https://docs.clawd.bot
|
||||
- Swabble: use the tagged Commander Swift package release.
|
||||
- CLI: add `clawdbot acp client` interactive ACP harness for debugging.
|
||||
- Plugins: route command detection/text chunking helpers through the plugin runtime and drop runtime exports from the SDK.
|
||||
- Memory: add native Gemini embeddings provider for memory search. (#1151) — thanks @gumadeiras.
|
||||
- Media: auto-enable audio understanding when provider keys are configured (OpenAI/Groq/Deepgram).
|
||||
- Docs: add API usage + costs overview. https://docs.clawd.bot/reference/api-usage-costs
|
||||
- Memory: add native Gemini embeddings provider for memory search. (#1151)
|
||||
- Agents: add local docs path resolution and include docs/mirror/source/community pointers in the system prompt.
|
||||
|
||||
### Fixes
|
||||
- Auth profiles: keep auto-pinned preference while allowing rotation on failover; user pins stay locked. (#1138) — thanks @cheeeee.
|
||||
- macOS: avoid touching launchd in Remote over SSH so quitting the app no longer disables the remote gateway. (#1105)
|
||||
- Memory: index atomically so failed reindex preserves the previous memory database. (#1151) — thanks @gumadeiras.
|
||||
- Memory: avoid sqlite-vec unique constraint failures when reindexing duplicate chunk ids. (#1151) — thanks @gumadeiras.
|
||||
- Memory: index atomically so failed reindex preserves the previous memory database. (#1151)
|
||||
- Memory: avoid sqlite-vec unique constraint failures when reindexing duplicate chunk ids. (#1151)
|
||||
|
||||
## 2026.1.18-5
|
||||
|
||||
### Changes
|
||||
- Dependencies: update core + plugin deps (grammy, vitest, openai, Microsoft agents hosting, etc.).
|
||||
|
||||
## 2026.1.18-3
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ The prompt is intentionally compact and uses fixed sections:
|
||||
- **Skills** (when available): tells the model how to load skill instructions on demand.
|
||||
- **Clawdbot Self-Update**: how to run `config.apply` and `update.run`.
|
||||
- **Workspace**: working directory (`agents.defaults.workspace`).
|
||||
- **Documentation**: local path to Clawdbot docs (repo or npm package) and when to read them.
|
||||
- **Workspace Files (injected)**: indicates bootstrap files are included below.
|
||||
- **Sandbox** (when enabled): indicates sandboxed runtime, sandbox paths, and whether elevated exec is available.
|
||||
- **Current Date & Time**: user-local time, timezone, and time format.
|
||||
@@ -98,3 +99,12 @@ Skills section is omitted.
|
||||
```
|
||||
|
||||
This keeps the base prompt small while still enabling targeted skill usage.
|
||||
|
||||
## Documentation
|
||||
|
||||
When available, the system prompt includes a **Documentation** section that points to the
|
||||
local Clawdbot docs directory (either `docs/` in the repo workspace or the bundled npm
|
||||
package docs) and also notes the public mirror, source repo, community Discord, and
|
||||
ClawdHub (https://clawdhub.com) for skills discovery. The prompt instructs the model to consult local docs first
|
||||
for Clawdbot behavior, commands, configuration, or architecture, and to run
|
||||
`clawdbot status` itself when possible (asking the user only when it lacks access).
|
||||
|
||||
@@ -6,6 +6,7 @@ import { shouldLogVerbose } from "../globals.js";
|
||||
import { createSubsystemLogger } from "../logging.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { resolveClawdbotDocsPath } from "./docs-path.js";
|
||||
import { resolveSessionAgentIds } from "./agent-scope.js";
|
||||
import { makeBootstrapWarn, resolveBootstrapContextForRun } from "./bootstrap-files.js";
|
||||
import { resolveCliBackendConfig } from "./cli-backends.js";
|
||||
@@ -83,6 +84,12 @@ export async function runCliAgent(params: {
|
||||
sessionAgentId === defaultAgentId
|
||||
? resolveHeartbeatPrompt(params.config?.agents?.defaults?.heartbeat?.prompt)
|
||||
: undefined;
|
||||
const docsPath = await resolveClawdbotDocsPath({
|
||||
workspaceDir,
|
||||
argv1: process.argv[1],
|
||||
cwd: process.cwd(),
|
||||
moduleUrl: import.meta.url,
|
||||
});
|
||||
const systemPrompt = buildSystemPrompt({
|
||||
workspaceDir,
|
||||
config: params.config,
|
||||
@@ -90,6 +97,7 @@ export async function runCliAgent(params: {
|
||||
extraSystemPrompt,
|
||||
ownerNumbers: params.ownerNumbers,
|
||||
heartbeatPrompt,
|
||||
docsPath,
|
||||
tools: [],
|
||||
contextFiles,
|
||||
modelDisplay,
|
||||
|
||||
@@ -168,6 +168,7 @@ export function buildSystemPrompt(params: {
|
||||
extraSystemPrompt?: string;
|
||||
ownerNumbers?: string[];
|
||||
heartbeatPrompt?: string;
|
||||
docsPath?: string;
|
||||
tools: AgentTool[];
|
||||
contextFiles?: EmbeddedContextFile[];
|
||||
modelDisplay: string;
|
||||
@@ -182,6 +183,7 @@ export function buildSystemPrompt(params: {
|
||||
ownerNumbers: params.ownerNumbers,
|
||||
reasoningTagHint: false,
|
||||
heartbeatPrompt: params.heartbeatPrompt,
|
||||
docsPath: params.docsPath,
|
||||
runtimeInfo: {
|
||||
host: "clawdbot",
|
||||
os: `${os.type()} ${os.release()}`,
|
||||
|
||||
27
src/agents/docs-path.ts
Normal file
27
src/agents/docs-path.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import { resolveClawdbotPackageRoot } from "../infra/clawdbot-root.js";
|
||||
|
||||
export async function resolveClawdbotDocsPath(params: {
|
||||
workspaceDir?: string;
|
||||
argv1?: string;
|
||||
cwd?: string;
|
||||
moduleUrl?: string;
|
||||
}): Promise<string | null> {
|
||||
const workspaceDir = params.workspaceDir?.trim();
|
||||
if (workspaceDir) {
|
||||
const workspaceDocs = path.join(workspaceDir, "docs");
|
||||
if (fs.existsSync(workspaceDocs)) return workspaceDocs;
|
||||
}
|
||||
|
||||
const packageRoot = await resolveClawdbotPackageRoot({
|
||||
cwd: params.cwd,
|
||||
argv1: params.argv1,
|
||||
moduleUrl: params.moduleUrl,
|
||||
});
|
||||
if (!packageRoot) return null;
|
||||
|
||||
const packageDocs = path.join(packageRoot, "docs");
|
||||
return fs.existsSync(packageDocs) ? packageDocs : null;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { resolveUserPath } from "../../utils.js";
|
||||
import { resolveClawdbotAgentDir } from "../agent-paths.js";
|
||||
import { resolveSessionAgentIds } from "../agent-scope.js";
|
||||
import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../bootstrap-files.js";
|
||||
import { resolveClawdbotDocsPath } from "../docs-path.js";
|
||||
import type { ExecElevatedDefaults } from "../bash-tools.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../defaults.js";
|
||||
import { getApiKeyForModel, resolveModelAuthMode } from "../model-auth.js";
|
||||
@@ -250,6 +251,12 @@ export async function compactEmbeddedPiSession(params: {
|
||||
});
|
||||
const isDefaultAgent = sessionAgentId === defaultAgentId;
|
||||
const promptMode = isSubagentSessionKey(params.sessionKey) ? "minimal" : "full";
|
||||
const docsPath = await resolveClawdbotDocsPath({
|
||||
workspaceDir: effectiveWorkspace,
|
||||
argv1: process.argv[1],
|
||||
cwd: process.cwd(),
|
||||
moduleUrl: import.meta.url,
|
||||
});
|
||||
const appendPrompt = buildEmbeddedSystemPrompt({
|
||||
workspaceDir: effectiveWorkspace,
|
||||
defaultThinkLevel: params.thinkLevel,
|
||||
@@ -261,6 +268,7 @@ export async function compactEmbeddedPiSession(params: {
|
||||
? resolveHeartbeatPrompt(params.config?.agents?.defaults?.heartbeat?.prompt)
|
||||
: undefined,
|
||||
skillsPrompt,
|
||||
docsPath,
|
||||
promptMode,
|
||||
runtimeInfo,
|
||||
sandboxInfo,
|
||||
|
||||
@@ -18,6 +18,7 @@ import { resolveUserPath } from "../../../utils.js";
|
||||
import { resolveClawdbotAgentDir } from "../../agent-paths.js";
|
||||
import { resolveSessionAgentIds } from "../../agent-scope.js";
|
||||
import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../../bootstrap-files.js";
|
||||
import { resolveClawdbotDocsPath } from "../../docs-path.js";
|
||||
import { resolveModelAuthMode } from "../../model-auth.js";
|
||||
import {
|
||||
isCloudCodeAssistFormatError,
|
||||
@@ -216,6 +217,12 @@ export async function runEmbeddedAttempt(
|
||||
});
|
||||
const isDefaultAgent = sessionAgentId === defaultAgentId;
|
||||
const promptMode = isSubagentSessionKey(params.sessionKey) ? "minimal" : "full";
|
||||
const docsPath = await resolveClawdbotDocsPath({
|
||||
workspaceDir: effectiveWorkspace,
|
||||
argv1: process.argv[1],
|
||||
cwd: process.cwd(),
|
||||
moduleUrl: import.meta.url,
|
||||
});
|
||||
|
||||
const appendPrompt = buildEmbeddedSystemPrompt({
|
||||
workspaceDir: effectiveWorkspace,
|
||||
@@ -228,6 +235,7 @@ export async function runEmbeddedAttempt(
|
||||
? resolveHeartbeatPrompt(params.config?.agents?.defaults?.heartbeat?.prompt)
|
||||
: undefined,
|
||||
skillsPrompt,
|
||||
docsPath,
|
||||
reactionGuidance,
|
||||
promptMode,
|
||||
runtimeInfo,
|
||||
|
||||
@@ -15,6 +15,7 @@ export function buildEmbeddedSystemPrompt(params: {
|
||||
reasoningTagHint: boolean;
|
||||
heartbeatPrompt?: string;
|
||||
skillsPrompt?: string;
|
||||
docsPath?: string;
|
||||
reactionGuidance?: {
|
||||
level: "minimal" | "extensive";
|
||||
channel: string;
|
||||
@@ -48,6 +49,7 @@ export function buildEmbeddedSystemPrompt(params: {
|
||||
reasoningTagHint: params.reasoningTagHint,
|
||||
heartbeatPrompt: params.heartbeatPrompt,
|
||||
skillsPrompt: params.skillsPrompt,
|
||||
docsPath: params.docsPath,
|
||||
reactionGuidance: params.reactionGuidance,
|
||||
promptMode: params.promptMode,
|
||||
runtimeInfo: params.runtimeInfo,
|
||||
|
||||
@@ -32,12 +32,14 @@ describe("buildAgentSystemPrompt", () => {
|
||||
"<available_skills>\n <skill>\n <name>demo</name>\n </skill>\n</available_skills>",
|
||||
heartbeatPrompt: "ping",
|
||||
toolNames: ["message", "memory_search"],
|
||||
docsPath: "/tmp/clawd/docs",
|
||||
extraSystemPrompt: "Subagent details",
|
||||
});
|
||||
|
||||
expect(prompt).not.toContain("## User Identity");
|
||||
expect(prompt).not.toContain("## Skills");
|
||||
expect(prompt).not.toContain("## Memory Recall");
|
||||
expect(prompt).not.toContain("## Documentation");
|
||||
expect(prompt).not.toContain("## Reply Tags");
|
||||
expect(prompt).not.toContain("## Messaging");
|
||||
expect(prompt).not.toContain("## Silent Replies");
|
||||
@@ -86,6 +88,7 @@ describe("buildAgentSystemPrompt", () => {
|
||||
toolNames: ["Read", "Exec", "process"],
|
||||
skillsPrompt:
|
||||
"<available_skills>\n <skill>\n <name>demo</name>\n </skill>\n</available_skills>",
|
||||
docsPath: "/tmp/clawd/docs",
|
||||
});
|
||||
|
||||
expect(prompt).toContain("- Read: Read file contents");
|
||||
@@ -93,6 +96,21 @@ describe("buildAgentSystemPrompt", () => {
|
||||
expect(prompt).toContain(
|
||||
"Use `Read` to load the SKILL.md at the location listed for that skill.",
|
||||
);
|
||||
expect(prompt).toContain("Clawdbot docs: /tmp/clawd/docs");
|
||||
expect(prompt).toContain("read the docs first using `Read`");
|
||||
});
|
||||
|
||||
it("includes docs guidance when docsPath is provided", () => {
|
||||
const prompt = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/clawd",
|
||||
docsPath: "/tmp/clawd/docs",
|
||||
});
|
||||
|
||||
expect(prompt).toContain("## Documentation");
|
||||
expect(prompt).toContain("Clawdbot docs: /tmp/clawd/docs");
|
||||
expect(prompt).toContain(
|
||||
"When a user asks about Clawdbot behavior, commands, config, or architecture",
|
||||
);
|
||||
});
|
||||
|
||||
it("includes user time when provided (12-hour)", () => {
|
||||
|
||||
@@ -109,6 +109,26 @@ function buildMessagingSection(params: {
|
||||
];
|
||||
}
|
||||
|
||||
function buildDocsSection(params: {
|
||||
docsPath?: string;
|
||||
isMinimal: boolean;
|
||||
readToolName: string;
|
||||
}) {
|
||||
const docsPath = params.docsPath?.trim();
|
||||
if (!docsPath || params.isMinimal) return [];
|
||||
return [
|
||||
"## Documentation",
|
||||
`Clawdbot docs: ${docsPath}`,
|
||||
"Mirror: https://docs.clawd.bot",
|
||||
"Source: https://github.com/clawdbot/clawdbot",
|
||||
"Community: https://discord.com/invite/clawd",
|
||||
"Find new skills: https://clawdhub.com",
|
||||
"For Clawdbot behavior, commands, config, or architecture: consult local docs first.",
|
||||
"When diagnosing issues, run `clawdbot status` yourself when possible; only ask the user if you lack access (e.g., sandboxed).",
|
||||
"",
|
||||
];
|
||||
}
|
||||
|
||||
export function buildAgentSystemPrompt(params: {
|
||||
workspaceDir: string;
|
||||
defaultThinkLevel?: ThinkLevel;
|
||||
@@ -125,6 +145,7 @@ export function buildAgentSystemPrompt(params: {
|
||||
contextFiles?: EmbeddedContextFile[];
|
||||
skillsPrompt?: string;
|
||||
heartbeatPrompt?: string;
|
||||
docsPath?: string;
|
||||
/** Controls which hardcoded sections to include. Defaults to "full". */
|
||||
promptMode?: PromptMode;
|
||||
runtimeInfo?: {
|
||||
@@ -295,6 +316,11 @@ export function buildAgentSystemPrompt(params: {
|
||||
readToolName,
|
||||
});
|
||||
const memorySection = buildMemorySection({ isMinimal, availableTools });
|
||||
const docsSection = buildDocsSection({
|
||||
docsPath: params.docsPath,
|
||||
isMinimal,
|
||||
readToolName,
|
||||
});
|
||||
|
||||
// For "none" mode, return just the basic identity line
|
||||
if (promptMode === "none") {
|
||||
@@ -371,6 +397,7 @@ export function buildAgentSystemPrompt(params: {
|
||||
`Your working directory is: ${params.workspaceDir}`,
|
||||
"Treat this directory as the single global workspace for file operations unless explicitly instructed otherwise.",
|
||||
"",
|
||||
...docsSection,
|
||||
params.sandboxInfo?.enabled ? "## Sandbox" : "",
|
||||
params.sandboxInfo?.enabled
|
||||
? [
|
||||
|
||||
Reference in New Issue
Block a user