diff --git a/packages/cli/src/__tests__/e2e-validate-init.test.ts b/packages/cli/src/__tests__/e2e-validate-init.test.ts index bccd77a..dbf8429 100644 --- a/packages/cli/src/__tests__/e2e-validate-init.test.ts +++ b/packages/cli/src/__tests__/e2e-validate-init.test.ts @@ -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 () => { diff --git a/packages/cli/src/commands/create.ts b/packages/cli/src/commands/create.ts index d079d57..c8685a2 100644 --- a/packages/cli/src/commands/create.ts +++ b/packages/cli/src/commands/create.ts @@ -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`, ); } diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 8bef2f8..d79f511 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -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 { 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