Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdb39ba404 | ||
|
|
36ef885141 | ||
|
|
909ce37826 | ||
|
|
132bb3c373 | ||
|
|
180d16b977 | ||
|
|
eba89a6626 | ||
|
|
0a82787614 | ||
|
|
a1a2d2fdb3 |
31
.github/workflows/publish.yml
vendored
31
.github/workflows/publish.yml
vendored
@@ -48,10 +48,37 @@ jobs:
|
||||
run: npm config set registry https://registry.npmjs.org
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --ignore-scripts
|
||||
run: bun install
|
||||
env:
|
||||
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
||||
|
||||
- name: Debug environment
|
||||
run: |
|
||||
echo "=== Bun version ==="
|
||||
bun --version
|
||||
echo "=== Node version ==="
|
||||
node --version
|
||||
echo "=== Current directory ==="
|
||||
pwd
|
||||
echo "=== List src/ ==="
|
||||
ls -la src/
|
||||
echo "=== package.json scripts ==="
|
||||
cat package.json | jq '.scripts'
|
||||
|
||||
- name: Build
|
||||
run: bun run build
|
||||
run: |
|
||||
echo "=== Running bun build ==="
|
||||
bun build src/index.ts --outdir dist --target bun --format esm --external @ast-grep/napi
|
||||
echo "=== bun build exit code: $? ==="
|
||||
echo "=== Running tsc ==="
|
||||
tsc --emitDeclarationOnly
|
||||
echo "=== Running build:schema ==="
|
||||
bun run build:schema
|
||||
|
||||
- name: Verify build output
|
||||
run: |
|
||||
ls -la dist/
|
||||
test -f dist/index.js || (echo "ERROR: dist/index.js not found!" && exit 1)
|
||||
|
||||
- name: Publish
|
||||
run: bun run script/publish.ts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "oh-my-opencode",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.7",
|
||||
"description": "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -57,6 +57,7 @@
|
||||
"bun": ">=1.0.0"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@ast-grep/cli"
|
||||
"@ast-grep/cli",
|
||||
"@ast-grep/napi"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -63,10 +63,11 @@ async function generateChangelog(previous: string): Promise<string> {
|
||||
|
||||
async function buildAndPublish(): Promise<void> {
|
||||
console.log("\nPublishing to npm...")
|
||||
// --ignore-scripts: workflow에서 이미 빌드 완료, prepublishOnly 재실행 방지
|
||||
if (process.env.CI) {
|
||||
await $`npm publish --access public --provenance`
|
||||
await $`npm publish --access public --provenance --ignore-scripts`
|
||||
} else {
|
||||
await $`npm publish --access public`
|
||||
await $`npm publish --access public --ignore-scripts`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod"
|
||||
import { McpNameSchema } from "../mcp/types"
|
||||
|
||||
const PermissionValue = z.enum(["ask", "allow", "deny"])
|
||||
|
||||
@@ -23,8 +24,6 @@ export const AgentNameSchema = z.enum([
|
||||
"document-writer",
|
||||
])
|
||||
|
||||
export const McpNameSchema = z.enum(["websearch_exa", "context7"])
|
||||
|
||||
export const AgentOverrideConfigSchema = z.object({
|
||||
model: z.string().optional(),
|
||||
temperature: z.number().min(0).max(2).optional(),
|
||||
@@ -53,5 +52,6 @@ export const OhMyOpenCodeConfigSchema = z.object({
|
||||
export type OhMyOpenCodeConfig = z.infer<typeof OhMyOpenCodeConfigSchema>
|
||||
export type AgentOverrideConfig = z.infer<typeof AgentOverrideConfigSchema>
|
||||
export type AgentOverrides = z.infer<typeof AgentOverridesSchema>
|
||||
export type McpName = z.infer<typeof McpNameSchema>
|
||||
export type AgentName = z.infer<typeof AgentNameSchema>
|
||||
|
||||
export { McpNameSchema, type McpName } from "../mcp/types"
|
||||
|
||||
@@ -94,8 +94,4 @@ export function createCommentCheckerHooks() {
|
||||
}
|
||||
}
|
||||
|
||||
export * from "./types"
|
||||
export * from "./constants"
|
||||
export * from "./detector"
|
||||
export * from "./filters"
|
||||
export * from "./output"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from "./todo-continuation-enforcer"
|
||||
export * from "./context-window-monitor"
|
||||
export * from "./session-notification"
|
||||
export * from "./session-recovery"
|
||||
export * from "./comment-checker"
|
||||
export { createTodoContinuationEnforcer } from "./todo-continuation-enforcer"
|
||||
export { createContextWindowMonitorHook } from "./context-window-monitor"
|
||||
export { createSessionNotification } from "./session-notification"
|
||||
export { createSessionRecoveryHook } from "./session-recovery"
|
||||
export { createCommentCheckerHooks } from "./comment-checker"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Session Recovery - Message State Error Recovery
|
||||
*
|
||||
* Handles THREE specific scenarios:
|
||||
* Handles FOUR specific scenarios:
|
||||
* 1. tool_use block exists without tool_result
|
||||
* - Recovery: inject tool_result with "cancelled" content
|
||||
*
|
||||
@@ -10,6 +10,9 @@
|
||||
*
|
||||
* 3. Thinking disabled but message contains thinking blocks
|
||||
* - Recovery: strip thinking/redacted_thinking blocks
|
||||
*
|
||||
* 4. Empty content message (non-empty content required)
|
||||
* - Recovery: delete the empty message via revert
|
||||
*/
|
||||
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
@@ -17,7 +20,7 @@ import type { createOpencodeClient } from "@opencode-ai/sdk"
|
||||
|
||||
type Client = ReturnType<typeof createOpencodeClient>
|
||||
|
||||
type RecoveryErrorType = "tool_result_missing" | "thinking_block_order" | "thinking_disabled_violation" | null
|
||||
type RecoveryErrorType = "tool_result_missing" | "thinking_block_order" | "thinking_disabled_violation" | "empty_content_message" | null
|
||||
|
||||
interface MessageInfo {
|
||||
id?: string
|
||||
@@ -75,6 +78,10 @@ function detectErrorType(error: unknown): RecoveryErrorType {
|
||||
return "thinking_disabled_violation"
|
||||
}
|
||||
|
||||
if (message.includes("non-empty content") || message.includes("must have non-empty content")) {
|
||||
return "empty_content_message"
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -204,6 +211,35 @@ async function recoverThinkingDisabledViolation(
|
||||
return false
|
||||
}
|
||||
|
||||
async function recoverEmptyContentMessage(
|
||||
client: Client,
|
||||
sessionID: string,
|
||||
failedAssistantMsg: MessageData,
|
||||
directory: string
|
||||
): Promise<boolean> {
|
||||
const messageID = failedAssistantMsg.info?.id
|
||||
const parentMsgID = failedAssistantMsg.info?.parentID
|
||||
|
||||
if (!messageID) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Revert to parent message (delete the empty message)
|
||||
const revertTargetID = parentMsgID || messageID
|
||||
|
||||
try {
|
||||
await client.session.revert({
|
||||
path: { id: sessionID },
|
||||
body: { messageID: revertTargetID },
|
||||
query: { directory },
|
||||
})
|
||||
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function fallbackRevertStrategy(
|
||||
client: Client,
|
||||
sessionID: string,
|
||||
@@ -308,11 +344,13 @@ export function createSessionRecoveryHook(ctx: PluginInput) {
|
||||
tool_result_missing: "Tool Crash Recovery",
|
||||
thinking_block_order: "Thinking Block Recovery",
|
||||
thinking_disabled_violation: "Thinking Strip Recovery",
|
||||
empty_content_message: "Empty Message Recovery",
|
||||
}
|
||||
const toastMessages: Record<RecoveryErrorType & string, string> = {
|
||||
tool_result_missing: "Injecting cancelled tool results...",
|
||||
thinking_block_order: "Fixing message structure...",
|
||||
thinking_disabled_violation: "Stripping thinking blocks...",
|
||||
empty_content_message: "Deleting empty message...",
|
||||
}
|
||||
const toastTitle = toastTitles[errorType]
|
||||
const toastMessage = toastMessages[errorType]
|
||||
@@ -336,6 +374,8 @@ export function createSessionRecoveryHook(ctx: PluginInput) {
|
||||
success = await recoverThinkingBlockOrder(ctx.client, sessionID, failedMsg, ctx.directory)
|
||||
} else if (errorType === "thinking_disabled_violation") {
|
||||
success = await recoverThinkingDisabledViolation(ctx.client, sessionID, failedMsg)
|
||||
} else if (errorType === "empty_content_message") {
|
||||
success = await recoverEmptyContentMessage(ctx.client, sessionID, failedMsg, ctx.directory)
|
||||
}
|
||||
|
||||
return success
|
||||
|
||||
@@ -190,7 +190,6 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
|
||||
export default OhMyOpenCodePlugin
|
||||
|
||||
export { OhMyOpenCodeConfigSchema } from "./config"
|
||||
export type {
|
||||
OhMyOpenCodeConfig,
|
||||
AgentName,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { websearch_exa } from "./websearch-exa"
|
||||
import { context7 } from "./context7"
|
||||
import type { McpName } from "./types"
|
||||
|
||||
export type McpName = "websearch_exa" | "context7"
|
||||
export { McpNameSchema, type McpName } from "./types"
|
||||
|
||||
const allBuiltinMcps: Record<McpName, { type: "remote"; url: string; enabled: boolean }> = {
|
||||
websearch_exa,
|
||||
|
||||
5
src/mcp/types.ts
Normal file
5
src/mcp/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const McpNameSchema = z.enum(["websearch_exa", "context7"])
|
||||
|
||||
export type McpName = z.infer<typeof McpNameSchema>
|
||||
Reference in New Issue
Block a user