diff --git a/bun.lock b/bun.lock index 798170796..04be30353 100644 --- a/bun.lock +++ b/bun.lock @@ -108,6 +108,14 @@ "@oh-my-opencode/utils": "workspace:*", }, }, + "packages/prompts-core": { + "name": "@oh-my-opencode/prompts-core", + "version": "0.1.0", + "peerDependencies": { + "@oh-my-opencode/model-core": "workspace:*", + "@oh-my-opencode/utils": "workspace:*", + }, + }, "packages/rules-engine": { "name": "@oh-my-opencode/rules-engine", "version": "0.1.0", @@ -209,6 +217,8 @@ "@oh-my-opencode/model-core": ["@oh-my-opencode/model-core@workspace:packages/model-core"], + "@oh-my-opencode/prompts-core": ["@oh-my-opencode/prompts-core@workspace:packages/prompts-core"], + "@oh-my-opencode/rules-engine": ["@oh-my-opencode/rules-engine@workspace:packages/rules-engine"], "@oh-my-opencode/utils": ["@oh-my-opencode/utils@workspace:packages/utils"], diff --git a/package.json b/package.json index 873783204..b42a560fe 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "packages/ast-grep-mcp", "packages/utils", "packages/model-core", + "packages/prompts-core", "packages/comment-checker-core", "packages/hashline-core", "packages/boulder-state", diff --git a/packages/prompts-core/package.json b/packages/prompts-core/package.json new file mode 100644 index 000000000..73c10187e --- /dev/null +++ b/packages/prompts-core/package.json @@ -0,0 +1,22 @@ +{ + "name": "@oh-my-opencode/prompts-core", + "version": "0.1.0", + "type": "module", + "private": true, + "description": "Harness-agnostic markdown prompt loading and model-variant routing for oh-my-opencode.", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./src/index.ts" + } + }, + "types": "./index.d.ts", + "scripts": { + "typecheck": "tsgo --noEmit -p tsconfig.json", + "test": "bun test src/*.test.ts test/*.test.ts" + }, + "peerDependencies": { + "@oh-my-opencode/model-core": "workspace:*", + "@oh-my-opencode/utils": "workspace:*" + } +} diff --git a/packages/prompts-core/src/index.ts b/packages/prompts-core/src/index.ts new file mode 100644 index 000000000..f12a83c8b --- /dev/null +++ b/packages/prompts-core/src/index.ts @@ -0,0 +1,8 @@ +export type { + LoadedPrompt, + LoadPromptInput, + ModelVariant, + PromptSource, + RuntimeInjection, + VariantTable, +} from "./types" diff --git a/packages/prompts-core/src/types.test.ts b/packages/prompts-core/src/types.test.ts new file mode 100644 index 000000000..50b1a4b47 --- /dev/null +++ b/packages/prompts-core/src/types.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, test } from "bun:test" +import type { LoadPromptInput, LoadedPrompt, PromptSource, RuntimeInjection } from "./types" + +describe("prompt core types", () => { + test("#given loader input shape #then accepts source name variant and runtime injections", () => { + const source: PromptSource = { baseDir: "/tmp/prompts" } + const injection: RuntimeInjection = { placeholder: "{X}", resolver: () => "Y" } + + const input = { + source, + name: "test", + variant: "default", + inject: [injection], + } satisfies LoadPromptInput + + expect(input.source.baseDir).toBe("/tmp/prompts") + expect(input.inject[0]?.placeholder).toBe("{X}") + }) + + test("#given loaded prompt shape #then carries frontmatter and rendered body", () => { + const loaded = { + frontmatter: { title: "Fixture" }, + body: "Prompt body", + hadFrontmatter: true, + parseError: false, + filePath: "/tmp/prompts/test/default.md", + } satisfies LoadedPrompt<{ readonly title: string }> + + expect(loaded.frontmatter.title).toBe("Fixture") + expect(loaded.body).toBe("Prompt body") + }) +}) diff --git a/packages/prompts-core/src/types.ts b/packages/prompts-core/src/types.ts new file mode 100644 index 000000000..c70351455 --- /dev/null +++ b/packages/prompts-core/src/types.ts @@ -0,0 +1,35 @@ +export type ModelVariant = + | "default" + | "gpt" + | "gemini" + | "kimi" + | "glm" + | "planner" + | "opus-4-7" + | "minimax" + +export type PromptSource = { + readonly baseDir: string +} + +export type RuntimeInjection = { + readonly placeholder: string + readonly resolver: () => string | Promise +} + +export type LoadPromptInput = { + readonly source: PromptSource + readonly name: string + readonly variant: string + readonly inject?: readonly RuntimeInjection[] +} + +export type LoadedPrompt> = { + readonly frontmatter: TFrontmatter + readonly body: string + readonly hadFrontmatter: boolean + readonly parseError: boolean + readonly filePath: string +} + +export type VariantTable = Readonly> diff --git a/packages/prompts-core/tsconfig.json b/packages/prompts-core/tsconfig.json new file mode 100644 index 000000000..6a768d440 --- /dev/null +++ b/packages/prompts-core/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ESNext"], + "types": ["bun-types"] + }, + "include": ["src/**/*", "test/**/*"] +}