108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { DEFAULT_IDENTITY_FILENAME } from "./workspace.js";
|
|
|
|
export type AgentIdentityFile = {
|
|
name?: string;
|
|
emoji?: string;
|
|
theme?: string;
|
|
creature?: string;
|
|
vibe?: string;
|
|
avatar?: string;
|
|
};
|
|
|
|
const IDENTITY_PLACEHOLDER_VALUES = new Set([
|
|
"pick something you like",
|
|
"ai? robot? familiar? ghost in the machine? something weirder?",
|
|
"how do you come across? sharp? warm? chaotic? calm?",
|
|
"your signature - pick one that feels right",
|
|
"workspace-relative path, http(s) url, or data uri",
|
|
]);
|
|
|
|
function normalizeIdentityValue(value: string): string {
|
|
let normalized = value.trim();
|
|
normalized = normalized.replace(/^[*_]+|[*_]+$/g, "").trim();
|
|
if (normalized.startsWith("(") && normalized.endsWith(")")) {
|
|
normalized = normalized.slice(1, -1).trim();
|
|
}
|
|
normalized = normalized.replace(/[\u2013\u2014]/g, "-");
|
|
normalized = normalized.replace(/\s+/g, " ").toLowerCase();
|
|
return normalized;
|
|
}
|
|
|
|
function isIdentityPlaceholder(value: string): boolean {
|
|
const normalized = normalizeIdentityValue(value);
|
|
return IDENTITY_PLACEHOLDER_VALUES.has(normalized);
|
|
}
|
|
|
|
export function parseIdentityMarkdown(content: string): AgentIdentityFile {
|
|
const identity: AgentIdentityFile = {};
|
|
const lines = content.split(/\r?\n/);
|
|
for (const line of lines) {
|
|
const cleaned = line.trim().replace(/^\s*-\s*/, "");
|
|
const colonIndex = cleaned.indexOf(":");
|
|
if (colonIndex === -1) {
|
|
continue;
|
|
}
|
|
const label = cleaned.slice(0, colonIndex).replace(/[*_]/g, "").trim().toLowerCase();
|
|
const value = cleaned
|
|
.slice(colonIndex + 1)
|
|
.replace(/^[*_]+|[*_]+$/g, "")
|
|
.trim();
|
|
if (!value) {
|
|
continue;
|
|
}
|
|
if (isIdentityPlaceholder(value)) {
|
|
continue;
|
|
}
|
|
if (label === "name") {
|
|
identity.name = value;
|
|
}
|
|
if (label === "emoji") {
|
|
identity.emoji = value;
|
|
}
|
|
if (label === "creature") {
|
|
identity.creature = value;
|
|
}
|
|
if (label === "vibe") {
|
|
identity.vibe = value;
|
|
}
|
|
if (label === "theme") {
|
|
identity.theme = value;
|
|
}
|
|
if (label === "avatar") {
|
|
identity.avatar = value;
|
|
}
|
|
}
|
|
return identity;
|
|
}
|
|
|
|
export function identityHasValues(identity: AgentIdentityFile): boolean {
|
|
return Boolean(
|
|
identity.name ||
|
|
identity.emoji ||
|
|
identity.theme ||
|
|
identity.creature ||
|
|
identity.vibe ||
|
|
identity.avatar,
|
|
);
|
|
}
|
|
|
|
export function loadIdentityFromFile(identityPath: string): AgentIdentityFile | null {
|
|
try {
|
|
const content = fs.readFileSync(identityPath, "utf-8");
|
|
const parsed = parseIdentityMarkdown(content);
|
|
if (!identityHasValues(parsed)) {
|
|
return null;
|
|
}
|
|
return parsed;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function loadAgentIdentityFromWorkspace(workspace: string): AgentIdentityFile | null {
|
|
const identityPath = path.join(workspace, DEFAULT_IDENTITY_FILENAME);
|
|
return loadIdentityFromFile(identityPath);
|
|
}
|