106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
import type { ClawdbotConfig } from "../config/config.js";
|
|
import {
|
|
formatUserTime,
|
|
resolveUserTimeFormat,
|
|
resolveUserTimezone,
|
|
type ResolvedTimeFormat,
|
|
} from "./date-time.js";
|
|
|
|
export type RuntimeInfoInput = {
|
|
agentId?: string;
|
|
host: string;
|
|
os: string;
|
|
arch: string;
|
|
node: string;
|
|
model: string;
|
|
defaultModel?: string;
|
|
channel?: string;
|
|
capabilities?: string[];
|
|
/** Supported message actions for the current channel (e.g., react, edit, unsend) */
|
|
channelActions?: string[];
|
|
repoRoot?: string;
|
|
};
|
|
|
|
export type SystemPromptRuntimeParams = {
|
|
runtimeInfo: RuntimeInfoInput;
|
|
userTimezone: string;
|
|
userTime?: string;
|
|
userTimeFormat?: ResolvedTimeFormat;
|
|
};
|
|
|
|
export function buildSystemPromptParams(params: {
|
|
config?: ClawdbotConfig;
|
|
agentId?: string;
|
|
runtime: Omit<RuntimeInfoInput, "agentId">;
|
|
workspaceDir?: string;
|
|
cwd?: string;
|
|
}): SystemPromptRuntimeParams {
|
|
const repoRoot = resolveRepoRoot({
|
|
config: params.config,
|
|
workspaceDir: params.workspaceDir,
|
|
cwd: params.cwd,
|
|
});
|
|
const userTimezone = resolveUserTimezone(params.config?.agents?.defaults?.userTimezone);
|
|
const userTimeFormat = resolveUserTimeFormat(params.config?.agents?.defaults?.timeFormat);
|
|
const userTime = formatUserTime(new Date(), userTimezone, userTimeFormat);
|
|
return {
|
|
runtimeInfo: {
|
|
agentId: params.agentId,
|
|
...params.runtime,
|
|
repoRoot,
|
|
},
|
|
userTimezone,
|
|
userTime,
|
|
userTimeFormat,
|
|
};
|
|
}
|
|
|
|
function resolveRepoRoot(params: {
|
|
config?: ClawdbotConfig;
|
|
workspaceDir?: string;
|
|
cwd?: string;
|
|
}): string | undefined {
|
|
const configured = params.config?.agents?.defaults?.repoRoot?.trim();
|
|
if (configured) {
|
|
try {
|
|
const resolved = path.resolve(configured);
|
|
const stat = fs.statSync(resolved);
|
|
if (stat.isDirectory()) return resolved;
|
|
} catch {
|
|
// ignore invalid config path
|
|
}
|
|
}
|
|
const candidates = [params.workspaceDir, params.cwd]
|
|
.map((value) => value?.trim())
|
|
.filter(Boolean) as string[];
|
|
const seen = new Set<string>();
|
|
for (const candidate of candidates) {
|
|
const resolved = path.resolve(candidate);
|
|
if (seen.has(resolved)) continue;
|
|
seen.add(resolved);
|
|
const root = findGitRoot(resolved);
|
|
if (root) return root;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function findGitRoot(startDir: string): string | null {
|
|
let current = path.resolve(startDir);
|
|
for (let i = 0; i < 12; i += 1) {
|
|
const gitPath = path.join(current, ".git");
|
|
try {
|
|
const stat = fs.statSync(gitPath);
|
|
if (stat.isDirectory() || stat.isFile()) return current;
|
|
} catch {
|
|
// ignore missing .git at this level
|
|
}
|
|
const parent = path.dirname(current);
|
|
if (parent === current) break;
|
|
current = parent;
|
|
}
|
|
return null;
|
|
}
|