From 977bf4ee39c76746e592b0995d208306d625dc93 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Thu, 28 May 2026 17:08:06 +0200 Subject: [PATCH] refactor(cli): remove Project promise facade --- .../src/kilocode/session-import/service.ts | 3 +- .../opencode/src/kilocode/worktree-family.ts | 3 +- packages/opencode/src/project/project.ts | 7 ---- .../kilocode/session-import-service.test.ts | 33 +++++++++++++++++ .../test/kilocode/worktree-family.test.ts | 35 +++++++++++++++++++ script/check-opencode-promise-facades.ts | 1 - 6 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 packages/opencode/test/kilocode/worktree-family.test.ts diff --git a/packages/opencode/src/kilocode/session-import/service.ts b/packages/opencode/src/kilocode/session-import/service.ts index 6eb12e2e00a..e907263b69e 100644 --- a/packages/opencode/src/kilocode/session-import/service.ts +++ b/packages/opencode/src/kilocode/session-import/service.ts @@ -5,6 +5,7 @@ import { ProjectID } from "../../project/schema" import { WorkspaceID } from "../../control-plane/schema" import { SessionImportType } from "./types" import { Project } from "../../project/project" +import { AppRuntime } from "../../effect/app-runtime" import { eq } from "drizzle-orm" const key = (input: unknown) => [input] as never @@ -18,7 +19,7 @@ export namespace SessionImportService { throw new Error("Legacy project import requires a non-empty worktree") } - const result = await Project.fromDirectory(input.worktree) + const result = await AppRuntime.runPromise(Project.Service.use((svc) => svc.fromDirectory(input.worktree))) return { ok: true, id: result.project.id } } diff --git a/packages/opencode/src/kilocode/worktree-family.ts b/packages/opencode/src/kilocode/worktree-family.ts index ae449361ceb..f7aa58a9b4f 100644 --- a/packages/opencode/src/kilocode/worktree-family.ts +++ b/packages/opencode/src/kilocode/worktree-family.ts @@ -32,7 +32,8 @@ export namespace WorktreeFamily { } } - const dirs = [ctx.worktree, ...(yield* Effect.promise(() => Project.sandboxes(ctx.project.id)))] + const project = yield* Project.Service + const dirs = [ctx.worktree, ...(yield* project.sandboxes(ctx.project.id))] return [...new Set(dirs.map((dir) => Filesystem.resolve(dir)))] }) } diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index badc6247c17..21a8eb19fd7 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -5,7 +5,6 @@ import { eq } from "drizzle-orm" import { ProjectTable } from "./project.sql" import { SessionTable } from "../session/session.sql" import * as Log from "@opencode-ai/core/util/log" -import { makeRuntime } from "@/effect/run-service" // kilocode_change import { Flag } from "@opencode-ai/core/flag/flag" import { BusEvent } from "@/bus/bus-event" import { GlobalBus } from "@/bus/global" @@ -540,10 +539,4 @@ export function setInitialized(id: ProjectID) { ) } -// kilocode_change start - legacy promise helpers for Kilo callsites -const { runPromise } = makeRuntime(Service, defaultLayer) -export const fromDirectory = (directory: string) => runPromise((svc) => svc.fromDirectory(directory)) -export const sandboxes = (id: ProjectID) => runPromise((svc) => svc.sandboxes(id)) -// kilocode_change end - export * as Project from "./project" diff --git a/packages/opencode/test/kilocode/session-import-service.test.ts b/packages/opencode/test/kilocode/session-import-service.test.ts index 6afc1e39b10..4180da9ed08 100644 --- a/packages/opencode/test/kilocode/session-import-service.test.ts +++ b/packages/opencode/test/kilocode/session-import-service.test.ts @@ -1,6 +1,8 @@ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test" import { Database } from "../../src/storage/db" import { SessionImportService } from "../../src/kilocode/session-import/service" +import { resetDatabase } from "../fixture/db" +import { tmpdir } from "../fixture/fixture" let spy: ReturnType @@ -76,6 +78,37 @@ function input(force?: boolean) { } } +function project(worktree: string) { + return { + id: "legacy_project", + worktree, + timeCreated: 1, + timeUpdated: 1, + sandboxes: [], + } +} + +describe("SessionImportService.project", () => { + afterEach(async () => { + await resetDatabase() + }) + + test("rejects an empty legacy worktree", async () => { + await expect(SessionImportService.project(project(" "))).rejects.toThrow( + "Legacy project import requires a non-empty worktree", + ) + }) + + test("resolves a valid legacy project through Project.Service", async () => { + await using tmp = await tmpdir({ git: true }) + + const result = await SessionImportService.project(project(tmp.path)) + + expect(result.ok).toBe(true) + expect(result.id).not.toBe("global") + }) +}) + describe("SessionImportService.session", () => { beforeEach(() => { spy = spyOn(Database, "use").mockImplementation((fn: any) => fn(db)) diff --git a/packages/opencode/test/kilocode/worktree-family.test.ts b/packages/opencode/test/kilocode/worktree-family.test.ts new file mode 100644 index 00000000000..abd6d4e369f --- /dev/null +++ b/packages/opencode/test/kilocode/worktree-family.test.ts @@ -0,0 +1,35 @@ +import { describe, expect } from "bun:test" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { Effect, Layer } from "effect" +import { Git } from "../../src/git" +import { InstanceRef } from "../../src/effect/instance-ref" +import { WorktreeFamily } from "../../src/kilocode/worktree-family" +import { Project } from "../../src/project/project" +import { resetDatabase } from "../fixture/db" +import { tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(Layer.mergeAll(Project.defaultLayer, Git.defaultLayer, CrossSpawnSpawner.defaultLayer)) + +describe("WorktreeFamily.list", () => { + it.live("returns recorded sandboxes when git worktree listing fails", () => + Effect.gen(function* () { + yield* Effect.addFinalizer(() => Effect.promise(() => resetDatabase())) + const root = yield* tmpdirScoped() + const sandbox = yield* tmpdirScoped() + const project = yield* Project.Service + const info = (yield* project.fromDirectory(root)).project + yield* project.addSandbox(info.id, sandbox) + + const dirs = yield* WorktreeFamily.list().pipe( + Effect.provideService(InstanceRef, { + directory: root, + worktree: root, + project: { ...info, vcs: "git" }, + }), + ) + + expect(dirs).toEqual([root, sandbox]) + }), + ) +}) diff --git a/script/check-opencode-promise-facades.ts b/script/check-opencode-promise-facades.ts index 8081741aff6..bfe265b8f03 100644 --- a/script/check-opencode-promise-facades.ts +++ b/script/check-opencode-promise-facades.ts @@ -20,7 +20,6 @@ const allow: Record = { "cli/cmd/tui/config/tui.ts": "separately tracked TUI config facade", "installation/index.ts": "existing installation facade outside #10655", "permission/index.ts": "transitional facade removed by #10620", - "project/project.ts": "transitional facade removed by #10620", "project/vcs.ts": "transitional facade removed by #10620", "provider/provider.ts": "transitional facade tracked by #10655", "question/index.ts": "transitional facade deferred for upstream reconciliation in #10655",