@letta-ai/letta-code 0.27.24 → 0.27.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/types/protocol_v2.d.ts +16 -0
- package/dist/types/types/protocol_v2.d.ts.map +1 -1
- package/letta.js +474 -300
- package/package.json +2 -1
- package/scripts/check-skill-frontmatter.js +116 -0
- package/scripts/check.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letta-ai/letta-code",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.25",
|
|
4
4
|
"description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "bun@1.3.0",
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"check:filename-casing": "node scripts/check-filename-casing.js",
|
|
98
98
|
"check:test-mock-isolation": "bun run scripts/check-test-mock-isolation.js",
|
|
99
99
|
"check:test-coverage": "node scripts/check-test-coverage.cjs",
|
|
100
|
+
"check:skill-frontmatter": "node scripts/check-skill-frontmatter.js",
|
|
100
101
|
"check:bundled-skill-scripts": "node scripts/check-bundled-skill-scripts.js",
|
|
101
102
|
"check": "bun run scripts/check.js",
|
|
102
103
|
"dev": "node scripts/dev.cjs",
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
|
|
6
|
+
const isStagedOnly = process.argv.includes("--staged");
|
|
7
|
+
|
|
8
|
+
function git(args) {
|
|
9
|
+
const result = spawnSync("git", args, {
|
|
10
|
+
cwd: process.cwd(),
|
|
11
|
+
encoding: "utf8",
|
|
12
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (result.status !== 0) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
(result.stderr || result.stdout || "git command failed").trim(),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return result.stdout;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function splitNul(output) {
|
|
25
|
+
return output.split("\0").filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isSkillMarkdownFile(file) {
|
|
29
|
+
return file.split(/[\\/]/).pop() === "SKILL.md";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getCandidateFiles() {
|
|
33
|
+
if (isStagedOnly) {
|
|
34
|
+
return splitNul(
|
|
35
|
+
git(["diff", "--cached", "--name-only", "--diff-filter=ACMR", "-z"]),
|
|
36
|
+
).filter(isSkillMarkdownFile);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return splitNul(git(["ls-files", "-z"])).filter(isSkillMarkdownFile);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readStagedFile(file) {
|
|
43
|
+
return git(["show", `:${file}`]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readCurrentFile(file) {
|
|
47
|
+
if (!existsSync(file)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return readFileSync(file, "utf8");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function checkSkillFrontmatterName(content) {
|
|
54
|
+
const normalized = content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n");
|
|
55
|
+
const match = normalized.match(/^---\n([\s\S]*?)\n---(?:\n|$)/);
|
|
56
|
+
if (!match) {
|
|
57
|
+
return "missing YAML frontmatter";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const frontmatter = match[1] ?? "";
|
|
61
|
+
const nameLine = frontmatter
|
|
62
|
+
.split("\n")
|
|
63
|
+
.find((line) => /^\s*name\s*:/.test(line));
|
|
64
|
+
|
|
65
|
+
if (!nameLine) {
|
|
66
|
+
return "missing name frontmatter";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const nameValue = nameLine.replace(/^\s*name\s*:\s*/, "").trim();
|
|
70
|
+
if (!nameValue) {
|
|
71
|
+
return "empty name frontmatter";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let violations = [];
|
|
78
|
+
let files = [];
|
|
79
|
+
try {
|
|
80
|
+
files = getCandidateFiles();
|
|
81
|
+
violations = files.flatMap((file) => {
|
|
82
|
+
const content = isStagedOnly ? readStagedFile(file) : readCurrentFile(file);
|
|
83
|
+
if (content === null) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const reason = checkSkillFrontmatterName(content);
|
|
88
|
+
return reason ? [{ file, reason }] : [];
|
|
89
|
+
});
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error(
|
|
92
|
+
`Failed to check skill frontmatter: ${error instanceof Error ? error.message : String(error)}`,
|
|
93
|
+
);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (violations.length > 0) {
|
|
98
|
+
console.error("\n❌ Skill frontmatter violations found:\n");
|
|
99
|
+
for (const violation of violations) {
|
|
100
|
+
console.error(`${violation.file}`);
|
|
101
|
+
console.error(` ${violation.reason}`);
|
|
102
|
+
console.error(
|
|
103
|
+
" ↳ Add a non-empty `name:` field to SKILL.md frontmatter.\n",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
console.error(
|
|
107
|
+
`Found ${violations.length} skill frontmatter violation${violations.length === 1 ? "" : "s"}.`,
|
|
108
|
+
);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!isStagedOnly) {
|
|
113
|
+
console.log(
|
|
114
|
+
`✅ Skill frontmatter name headers present (${files.length} SKILL.md file${files.length === 1 ? "" : "s"}).`,
|
|
115
|
+
);
|
|
116
|
+
}
|
package/scripts/check.js
CHANGED
|
@@ -19,6 +19,7 @@ const checks = [
|
|
|
19
19
|
{ name: "filename casing", script: ["check:filename-casing"] },
|
|
20
20
|
{ name: "test mock isolation", script: ["check:test-mock-isolation"] },
|
|
21
21
|
{ name: "test coverage", script: ["check:test-coverage"] },
|
|
22
|
+
{ name: "skill frontmatter", script: ["check:skill-frontmatter"] },
|
|
22
23
|
{ name: "bundled skill scripts", script: ["check:bundled-skill-scripts"] },
|
|
23
24
|
{ name: "biome", script: ["lint"] },
|
|
24
25
|
{ name: "typescript", script: ["typecheck"] },
|