@lizard-build/cli 4.0.41 → 4.0.43
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/AGENTS.md +2 -2
- package/dist/commands/add.d.ts +9 -0
- package/dist/commands/add.js +8 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/login.js +8 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/skill.d.ts +2 -0
- package/dist/commands/skill.js +17 -1
- package/dist/commands/skill.js.map +1 -1
- package/dist/index.js +22 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/skill-route.d.ts +2 -0
- package/dist/lib/skill-route.js +7 -0
- package/dist/lib/skill-route.js.map +1 -0
- package/dist/lib/skills-data.generated.js +5 -1
- package/dist/lib/skills-data.generated.js.map +1 -1
- package/dist/lib/updater.d.ts +1 -1
- package/dist/lib/updater.js +1 -1
- package/package.json +1 -1
- package/scripts/gen-skills.mjs +15 -1
- package/skill-data/_shared/basics.md +92 -0
- package/skill-data/core/SKILL.md +7 -190
- package/skill-data/sandbox/SKILL.md +200 -0
- package/src/commands/add.ts +9 -1
- package/src/commands/login.ts +9 -1
- package/src/commands/skill.ts +15 -1
- package/src/index.ts +23 -14
- package/src/lib/skill-route.ts +7 -0
- package/src/lib/updater.ts +1 -1
- package/test/unit/default-project.test.ts +13 -0
- package/test/unit/login.test.ts +5 -4
- package/test/unit/skills.test.ts +78 -0
package/src/lib/updater.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { join, dirname } from "node:path";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
|
|
8
|
-
export const CURRENT_VERSION = "4.0.
|
|
8
|
+
export const CURRENT_VERSION = "4.0.43";
|
|
9
9
|
const RELEASES_API = "https://api.github.com/repos/lizard-build/lizard-cli/releases/latest";
|
|
10
10
|
const RELEASE_BASE = "https://github.com/lizard-build/lizard-cli/releases/latest/download";
|
|
11
11
|
|
|
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
|
|
5
5
|
import { resolveProjectId, getProjectLink, setProjectLink } from "../../src/lib/config.js";
|
|
6
6
|
import { resetDefaultProject } from "../../src/lib/default-project.js";
|
|
7
7
|
import { ensureLinked } from "../../src/commands/init.js";
|
|
8
|
+
import { resolveProject as resolveAddProject } from "../../src/commands/add.js";
|
|
8
9
|
import { setBaseURL, setAccessToken } from "../../src/lib/api.js";
|
|
9
10
|
import { setJSONMode } from "../../src/lib/format.js";
|
|
10
11
|
|
|
@@ -102,3 +103,15 @@ describe("lizard up in an unlinked folder", () => {
|
|
|
102
103
|
expect(requested()).toEqual([]);
|
|
103
104
|
});
|
|
104
105
|
});
|
|
106
|
+
|
|
107
|
+
describe("lizard add in an unlinked folder", () => {
|
|
108
|
+
test("uses the default project too", async () => {
|
|
109
|
+
routes({ "GET /api/projects/default": () => reply(200, project) });
|
|
110
|
+
expect(await resolveAddProject(undefined)).toBe("p-default");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("no default project: the old message", async () => {
|
|
114
|
+
routes({ "GET /api/projects/default": () => reply(404, { code: "NO_DEFAULT_PROJECT" }) });
|
|
115
|
+
await expect(resolveAddProject(undefined)).rejects.toThrow(/No project linked to this directory/);
|
|
116
|
+
});
|
|
117
|
+
});
|
package/test/unit/login.test.ts
CHANGED
|
@@ -192,15 +192,16 @@ describe("account state", () => {
|
|
|
192
192
|
"lizard sandbox ssh <id> Open a shell in it",
|
|
193
193
|
"lizard sandbox exec <id> -- <cmd> Run a command in it",
|
|
194
194
|
"lizard sandbox list See your sandboxes",
|
|
195
|
-
"lizard
|
|
195
|
+
"lizard add postgres redis s3 Add Postgres, Redis and object storage",
|
|
196
|
+
"lizard up Deploy this folder",
|
|
196
197
|
]);
|
|
197
|
-
for (const line of plain(nextStepsNote()).split("\n")) expect(line.length).toBeLessThanOrEqual(
|
|
198
|
+
for (const line of plain(nextStepsNote()).split("\n")) expect(line.length).toBeLessThanOrEqual(76);
|
|
198
199
|
});
|
|
199
200
|
|
|
200
201
|
test("no lizard init needed: an unlinked folder uses the default project", () => {
|
|
201
|
-
expect(nextSteps(false).map((s) => s.command)).toEqual(["lizard skills get core", "lizard sandbox create", "lizard up"]);
|
|
202
|
+
expect(nextSteps(false).map((s) => s.command)).toEqual(["lizard skills get core", "lizard sandbox create", "lizard add postgres redis s3", "lizard up"]);
|
|
202
203
|
expect(nextSteps(false)[1].why).toContain("default project");
|
|
203
|
-
expect(nextSteps(true).map((s) => s.command)).toEqual(["lizard skills get core", "lizard sandbox create", "lizard up"]);
|
|
204
|
+
expect(nextSteps(true).map((s) => s.command)).toEqual(["lizard skills get core", "lizard sandbox create", "lizard add postgres redis s3", "lizard up"]);
|
|
204
205
|
expect(nextSteps(true)[1].why).toContain("linked project");
|
|
205
206
|
});
|
|
206
207
|
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, expect, test, afterEach } from "vitest";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { EMBEDDED_SKILLS } from "../../src/lib/skills-data.generated.js";
|
|
4
|
+
import { expandIncludes } from "../../src/commands/skill.js";
|
|
5
|
+
import { skillFor } from "../../src/lib/skill-route.js";
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
|
|
8
|
+
const SKILL_DATA = path.resolve(import.meta.dirname, "../../skill-data");
|
|
9
|
+
|
|
10
|
+
function frontmatter(md: string): Record<string, string> {
|
|
11
|
+
const block = md.slice(4, md.indexOf("\n---", 3));
|
|
12
|
+
return Object.fromEntries(
|
|
13
|
+
block.split("\n").map((l) => l.match(/^([\w-]+):\s*"?(.*?)"?$/)).filter(Boolean).map((m) => [m![1], m![2]]),
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** GitHub-style heading slug, which the skills' own links use. */
|
|
18
|
+
const slug = (h: string) => h.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
|
|
19
|
+
|
|
20
|
+
describe("embedded skills", () => {
|
|
21
|
+
test("ship core and sandbox, and not the shared parts as a skill", () => {
|
|
22
|
+
expect(Object.keys(EMBEDDED_SKILLS).sort()).toEqual(["core", "sandbox"]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test.each(["core", "sandbox"])("%s has the shared basics inlined", (name) => {
|
|
26
|
+
const md = EMBEDDED_SKILLS[name].content;
|
|
27
|
+
expect(md).not.toContain("<!-- include");
|
|
28
|
+
for (const h of ["## Execution rules", "## Discovery", "## Sign-in", "## Exit codes", "## Default project", "## Billing"]) {
|
|
29
|
+
expect(md).toContain(`\n${h}\n`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test.each(["core", "sandbox"])("%s: every #link points at a heading in the same skill", (name) => {
|
|
34
|
+
const md = EMBEDDED_SKILLS[name].content;
|
|
35
|
+
const headings = new Set([...md.matchAll(/^#{2,3} (.+)$/gm)].map((m) => slug(m[1])));
|
|
36
|
+
const links = [...md.matchAll(/\]\(#([\w-]+)\)/g)].map((m) => m[1]);
|
|
37
|
+
expect(links.filter((l) => !headings.has(l))).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("descriptions fit the 1024-character limit and say who they are for", () => {
|
|
41
|
+
const core = frontmatter(EMBEDDED_SKILLS.core.content);
|
|
42
|
+
const sandbox = frontmatter(EMBEDDED_SKILLS.sandbox.content);
|
|
43
|
+
expect(sandbox.name).toBe("lizard-sandbox");
|
|
44
|
+
expect(core.description.length).toBeLessThanOrEqual(1024);
|
|
45
|
+
expect(sandbox.description.length).toBeLessThanOrEqual(1024);
|
|
46
|
+
expect(sandbox.description).toMatch(/^Creates and manages Lizard sandboxes/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("sandbox work lives only in the sandbox skill, and core points there", () => {
|
|
50
|
+
const core = EMBEDDED_SKILLS.core.content;
|
|
51
|
+
const sandbox = EMBEDDED_SKILLS.sandbox.content;
|
|
52
|
+
expect(core).not.toMatch(/^## Sandboxes/m);
|
|
53
|
+
expect(core).toContain("lizard skills get sandbox --json");
|
|
54
|
+
for (const h of ["## Quickstart", "## Templates", "## Lifetime", "## Snapshots and pause/resume", "## From code: Lizard SDK"]) {
|
|
55
|
+
expect(sandbox).toContain(`\n${h}\n`);
|
|
56
|
+
}
|
|
57
|
+
expect(sandbox).toContain("lizard skills get core --json");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("skill routing in --help", () => {
|
|
62
|
+
test("sandbox, volume and agents point at the sandbox skill, the rest at core", () => {
|
|
63
|
+
expect(["sandbox", "volume", "agents"].map(skillFor)).toEqual(["sandbox", "sandbox", "sandbox"]);
|
|
64
|
+
expect(["up", "logs", "secrets", "add"].map(skillFor)).toEqual(["core", "core", "core", "core"]);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("LIZARD_SKILLS_DIR (authoring from files)", () => {
|
|
69
|
+
test("includes expand the same way as the embedded copy", () => {
|
|
70
|
+
const raw = fs.readFileSync(path.join(SKILL_DATA, "sandbox", "SKILL.md"), "utf8");
|
|
71
|
+
expect(expandIncludes(raw, SKILL_DATA)).toBe(EMBEDDED_SKILLS.sandbox.content);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("an include outside the skills folder is left as it is", () => {
|
|
75
|
+
const line = "<!-- include: ../package.json -->";
|
|
76
|
+
expect(expandIncludes(line, SKILL_DATA)).toBe(line);
|
|
77
|
+
});
|
|
78
|
+
});
|