Merge pull request 'feat(cli): init generates pnpm workspace with TypeScript senses' (#230) from feat/229-init-workspace-pnpm into main

This commit was merged in pull request #230.
This commit is contained in:
2026-04-28 12:34:20 +00:00
3 changed files with 85 additions and 27 deletions
@@ -202,10 +202,12 @@ describe("e2e init", () => {
// Verify key files exist
expect(existsSync(join(nerveRoot, "nerve.yaml"))).toBe(true);
expect(existsSync(join(nerveRoot, "package.json"))).toBe(true);
expect(existsSync(join(nerveRoot, "pnpm-workspace.yaml"))).toBe(true);
expect(existsSync(join(nerveRoot, "biome.json"))).toBe(true);
expect(existsSync(join(nerveRoot, ".gitignore"))).toBe(true);
expect(existsSync(join(nerveRoot, "senses", "cpu-usage", "schema.ts"))).toBe(true);
expect(existsSync(join(nerveRoot, "senses", "cpu-usage", "index.js"))).toBe(true);
expect(existsSync(join(nerveRoot, "senses", "cpu-usage", "package.json"))).toBe(true);
expect(existsSync(join(nerveRoot, "senses", "cpu-usage", "src", "index.ts"))).toBe(true);
expect(existsSync(join(nerveRoot, "senses", "cpu-usage", "src", "schema.ts"))).toBe(true);
expect(existsSync(join(nerveRoot, "senses", "cpu-usage", "migrations", "0001_init.sql"))).toBe(
true,
);
@@ -213,6 +215,18 @@ describe("e2e init", () => {
const pkgJson = readFileSync(join(nerveRoot, "package.json"), "utf8");
expect(pkgJson).toContain('"@uncaged/nerve-skills": "latest"');
expect(pkgJson).toContain('"build": "pnpm -r build"');
const workspaceYaml = readFileSync(join(nerveRoot, "pnpm-workspace.yaml"), "utf8");
expect(workspaceYaml).toContain("workflows/*");
expect(workspaceYaml).toContain("senses/*");
const sensePkgJson = readFileSync(
join(nerveRoot, "senses", "cpu-usage", "package.json"),
"utf8",
);
expect(sensePkgJson).toContain("nerve-sense-cpu-usage");
expect(sensePkgJson).toContain("esbuild");
});
it("generated nerve.yaml passes validate", { timeout: 10_000 }, async () => {
+2 -2
View File
@@ -324,12 +324,12 @@ const createSenseCommand = defineCommand({
process.stdout.write("\nInstalling sense dependencies and building…\n");
try {
await spawnAsync("pnpm", ["install", "--no-cache"], senseDir);
await spawnAsync("pnpm", ["install", "--no-cache", "--ignore-workspace"], senseDir);
await spawnAsync("pnpm", ["run", "build"], senseDir);
process.stdout.write("✅ Build complete — index.js ready.\n");
} catch {
process.stdout.write(
`⚠️ Build failed. Run manually:\n cd ${senseDir} && pnpm install --no-cache && pnpm run build\n`,
`⚠️ Build failed. Run manually:\n cd ${senseDir} && pnpm install --no-cache --ignore-workspace && pnpm run build\n`,
);
}
+67 -23
View File
@@ -17,6 +17,11 @@ senses:
interval: 10s
`;
const PNPM_WORKSPACE_YAML = `packages:
- 'workflows/*'
- 'senses/*'
`;
const BIOME_JSON = `{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"formatter": {
@@ -42,26 +47,32 @@ const BIOME_JSON = `{
}
`;
const PACKAGE_JSON = `{
"name": "my-nerve-workspace",
"version": "0.0.1",
"private": true,
"type": "module",
"dependencies": {
"@uncaged/nerve-core": "latest",
"@uncaged/nerve-daemon": "latest",
"@uncaged/nerve-skills": "latest",
"drizzle-orm": "latest"
const PACKAGE_JSON = `${JSON.stringify(
{
name: "my-nerve-workspace",
version: "0.0.1",
private: true,
type: "module",
scripts: {
build: "pnpm -r build",
},
dependencies: {
"@uncaged/nerve-core": "latest",
"@uncaged/nerve-daemon": "latest",
"@uncaged/nerve-skills": "latest",
"drizzle-orm": "latest",
},
devDependencies: {
"@biomejs/biome": "latest",
"drizzle-kit": "latest",
},
pnpm: {
onlyBuiltDependencies: ["esbuild"],
},
},
"devDependencies": {
"@biomejs/biome": "latest",
"drizzle-kit": "latest"
},
"pnpm": {
"onlyBuiltDependencies": ["esbuild"]
}
}
`;
null,
2,
)}\n`;
const GITIGNORE = `data/
logs/
@@ -110,9 +121,14 @@ export const cpuUsage = sqliteTable("cpu_usage", {
});
`;
const CPU_INDEX_JS = `import { cpus } from "node:os";
const CPU_INDEX_TS = `import { cpus } from "node:os";
export async function compute() {
type SenseResult = {
signal: { model: string; loadPercent: number; ts: number };
workflow: null;
};
export async function compute(): Promise<SenseResult> {
const cpuList = cpus();
let totalIdle = 0;
@@ -137,6 +153,24 @@ export async function compute() {
}
`;
const CPU_SENSE_PACKAGE_JSON = `${JSON.stringify(
{
name: "nerve-sense-cpu-usage",
private: true,
type: "module",
scripts: {
build:
"esbuild src/index.ts --bundle --platform=node --format=esm --outdir=. --out-extension:.js=.js --packages=external",
},
devDependencies: {
esbuild: "^0.27.0",
"drizzle-orm": "*",
},
},
null,
2,
)}\n`;
const CPU_MIGRATION_SQL = `CREATE TABLE IF NOT EXISTS cpu_usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER NOT NULL,
@@ -294,14 +328,17 @@ async function runInitWorkspace(force: boolean, skipInstall = false): Promise<vo
mkdirSync(join(nerveRoot, "data"), { recursive: true });
mkdirSync(join(nerveRoot, "data", "senses"), { recursive: true });
mkdirSync(join(nerveRoot, "senses", "cpu-usage", "src"), { recursive: true });
mkdirSync(join(nerveRoot, "senses", "cpu-usage", "migrations"), { recursive: true });
writeFile(join(nerveRoot, "nerve.yaml"), NERVE_YAML);
writeFile(join(nerveRoot, "package.json"), PACKAGE_JSON);
writeFile(join(nerveRoot, "pnpm-workspace.yaml"), PNPM_WORKSPACE_YAML);
writeFile(join(nerveRoot, "biome.json"), BIOME_JSON);
writeFile(join(nerveRoot, ".gitignore"), GITIGNORE);
writeFile(join(nerveRoot, "senses", "cpu-usage", "schema.ts"), CPU_SCHEMA_TS);
writeFile(join(nerveRoot, "senses", "cpu-usage", "index.js"), CPU_INDEX_JS);
writeFile(join(nerveRoot, "senses", "cpu-usage", "package.json"), CPU_SENSE_PACKAGE_JSON);
writeFile(join(nerveRoot, "senses", "cpu-usage", "src", "index.ts"), CPU_INDEX_TS);
writeFile(join(nerveRoot, "senses", "cpu-usage", "src", "schema.ts"), CPU_SCHEMA_TS);
writeFile(
join(nerveRoot, "senses", "cpu-usage", "migrations", "0001_init.sql"),
CPU_MIGRATION_SQL,
@@ -319,6 +356,13 @@ async function runInitWorkspace(force: boolean, skipInstall = false): Promise<vo
);
}
process.stdout.write("Building senses…\n");
try {
await runCommand("pnpm", ["run", "build"], nerveRoot);
} catch {
process.stdout.write(`⚠️ Build failed. Try manually:\n cd ${nerveRoot} && pnpm run build\n`);
}
if (!(await verifyNodeSqlite())) {
process.stdout.write(
"⚠️ Built-in SQLite (node:sqlite) is not available in this Node.js build. " +