Skip to content

Commit 6dd0274

Browse files
authored
refactor(shared): move the SKILL.md serializer into @posthog/shared (#3445)
1 parent 53ae2df commit 6dd0274

3 files changed

Lines changed: 49 additions & 37 deletions

File tree

packages/shared/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,11 @@ export type {
225225
SkillSource,
226226
UploadableSkillSource,
227227
} from "./skills";
228-
export { SKILL_EXISTS_MARKER, stripFrontmatter } from "./skills";
228+
export {
229+
SKILL_EXISTS_MARKER,
230+
serializeSkillMarkdown,
231+
stripFrontmatter,
232+
} from "./skills";
229233
export type {
230234
ArtifactType,
231235
PostHogAPIConfig,

packages/shared/src/skills.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,46 @@ export interface ExportedSkill {
3333
files: ExportedSkillFile[];
3434
}
3535

36+
/**
37+
* Serializes a SKILL.md file from frontmatter metadata plus a markdown body.
38+
*
39+
* The output must round-trip through `parseSkillFrontmatter` and also be valid
40+
* YAML for the agents that consume these files, so scalars fall back from plain
41+
* → double-quoted → literal block as they get more hostile. Lives here (shared)
42+
* so both the workspace-server bundler and the web-host bundler produce the
43+
* exact same SKILL.md — this is a serialization contract consumed by the cloud
44+
* sandbox, so it must not drift between hosts.
45+
*/
46+
export function serializeSkillMarkdown(
47+
meta: { name: string; description: string },
48+
body: string,
49+
): string {
50+
const frontmatter = [
51+
"---",
52+
`name: ${serializeSkillScalar(meta.name)}`,
53+
`description: ${serializeSkillScalar(meta.description)}`,
54+
"---",
55+
].join("\n");
56+
57+
const trimmedBody = body.replace(/^\n+/, "");
58+
return `${frontmatter}\n\n${trimmedBody.trimEnd()}\n`;
59+
}
60+
61+
const SKILL_PLAIN_SAFE = /^[A-Za-z0-9][A-Za-z0-9 _.,;()/-]*$/;
62+
63+
function serializeSkillScalar(value: string): string {
64+
if (value === "") return '""';
65+
if (!value.includes("\n")) {
66+
if (SKILL_PLAIN_SAFE.test(value) && !value.endsWith(" ")) return value;
67+
if (!value.includes('"') && !value.includes("\\")) return `"${value}"`;
68+
}
69+
// Literal block: survives quotes, backslashes, and newlines.
70+
const lines = value
71+
.split("\n")
72+
.map((line) => (line.trim() ? ` ${line}` : ""));
73+
return `|-\n${lines.join("\n")}`;
74+
}
75+
3676
/**
3777
* Server "skill already exists" messages must include this marker verbatim;
3878
* the UI keys its overwrite-confirmation flow on it.
Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,4 @@
1-
/**
2-
* Serializes a SKILL.md file from frontmatter metadata plus a markdown body.
3-
*
4-
* The output must round-trip through `parseSkillFrontmatter` and also be
5-
* valid YAML for the agents that consume these files, so scalars fall back
6-
* from plain → double-quoted → literal block as they get more hostile.
7-
*/
8-
export function serializeSkillMarkdown(
9-
meta: { name: string; description: string },
10-
body: string,
11-
): string {
12-
const frontmatter = [
13-
"---",
14-
`name: ${serializeScalar(meta.name)}`,
15-
`description: ${serializeScalar(meta.description)}`,
16-
"---",
17-
].join("\n");
18-
19-
const trimmedBody = body.replace(/^\n+/, "");
20-
return `${frontmatter}\n\n${trimmedBody.trimEnd()}\n`;
21-
}
22-
23-
const PLAIN_SAFE = /^[A-Za-z0-9][A-Za-z0-9 _.,;()/-]*$/;
24-
25-
function serializeScalar(value: string): string {
26-
if (value === "") return '""';
27-
if (!value.includes("\n")) {
28-
if (PLAIN_SAFE.test(value) && !value.endsWith(" ")) return value;
29-
if (!value.includes('"') && !value.includes("\\")) return `"${value}"`;
30-
}
31-
// Literal block: survives quotes, backslashes, and newlines.
32-
const lines = value
33-
.split("\n")
34-
.map((line) => (line.trim() ? ` ${line}` : ""));
35-
return `|-\n${lines.join("\n")}`;
36-
}
1+
// The SKILL.md serializer lives in @posthog/shared so the workspace-server
2+
// bundler and the web-host bundler emit byte-identical SKILL.md files — this is
3+
// a serialization contract the cloud sandbox consumes and must not drift.
4+
export { serializeSkillMarkdown } from "@posthog/shared";

0 commit comments

Comments
 (0)