oh-my-opencode/src/tools/slashcommand/command-discovery.ts
YeonGyu-Kim fe415319e5 fix: resolve publish blockers for v3.7.4→v3.8.0 release
- Fix #1991 crash: optional chaining for task-history sessionID access
- Fix #1992 think-mode: add antigravity entries to HIGH_VARIANT_MAP
- Fix #1949 Copilot premium misattribution: use createInternalAgentTextPart
- Fix #1982 load_skills: pass directory to discoverSkills for project-level skills
- Fix command priority: sort scopePriority before .find(), project-first return
- Fix Google provider transform: apply in userFallbackModels path
- Fix ralph-loop TUI: optional chaining for event handler
- Fix runtime-fallback: unify dual fallback engines, remove HTTP 400 from retry,
  fix pendingFallbackModel stuck state, add priority gate to skip model-fallback
  when runtime-fallback is active
- Fix Prometheus task system: exempt from todowrite/todoread deny
- Fix background_output: default full_session to true
- Remove orphan hooks: hashline-edit-diff-enhancer (redundant with hashline_edit
  built-in diff), task-reminder (dead code)
- Remove orphan config entries: 3 stale hook names from Zod schema
- Fix disabled_hooks schema: accept arbitrary strings for forward compatibility
- Register json-error-recovery hook in tool-guard pipeline
- Add disabled_hooks gating for question-label-truncator, task-resume-info,
  claude-code-hooks
- Update test expectations to match new behavior
2026-02-21 16:24:18 +09:00

86 lines
3.0 KiB
TypeScript

import { existsSync, readdirSync, readFileSync } from "fs"
import { basename, join } from "path"
import { parseFrontmatter, sanitizeModelField, getOpenCodeConfigDir } from "../../shared"
import type { CommandFrontmatter } from "../../features/claude-code-command-loader/types"
import { isMarkdownFile } from "../../shared/file-utils"
import { getClaudeConfigDir } from "../../shared"
import { loadBuiltinCommands } from "../../features/builtin-commands"
import type { CommandInfo, CommandMetadata, CommandScope } from "./types"
function discoverCommandsFromDir(commandsDir: string, scope: CommandScope): CommandInfo[] {
if (!existsSync(commandsDir)) return []
const entries = readdirSync(commandsDir, { withFileTypes: true })
const commands: CommandInfo[] = []
for (const entry of entries) {
if (!isMarkdownFile(entry)) continue
const commandPath = join(commandsDir, entry.name)
const commandName = basename(entry.name, ".md")
try {
const content = readFileSync(commandPath, "utf-8")
const { data, body } = parseFrontmatter<CommandFrontmatter>(content)
const isOpencodeSource = scope === "opencode" || scope === "opencode-project"
const metadata: CommandMetadata = {
name: commandName,
description: data.description || "",
argumentHint: data["argument-hint"],
model: sanitizeModelField(data.model, isOpencodeSource ? "opencode" : "claude-code"),
agent: data.agent,
subtask: Boolean(data.subtask),
}
commands.push({
name: commandName,
path: commandPath,
metadata,
content: body,
scope,
})
} catch {
continue
}
}
return commands
}
export function discoverCommandsSync(directory?: string): CommandInfo[] {
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
const userCommandsDir = join(getClaudeConfigDir(), "commands")
const projectCommandsDir = join(directory ?? process.cwd(), ".claude", "commands")
const opencodeGlobalDir = join(configDir, "command")
const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "command")
const userCommands = discoverCommandsFromDir(userCommandsDir, "user")
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode")
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project")
const opencodeProjectCommands = discoverCommandsFromDir(opencodeProjectDir, "opencode-project")
const builtinCommandsMap = loadBuiltinCommands()
const builtinCommands: CommandInfo[] = Object.values(builtinCommandsMap).map((command) => ({
name: command.name,
metadata: {
name: command.name,
description: command.description || "",
argumentHint: command.argumentHint,
model: command.model,
agent: command.agent,
subtask: command.subtask,
},
content: command.template,
scope: "builtin",
}))
return [
...projectCommands,
...userCommands,
...opencodeProjectCommands,
...opencodeGlobalCommands,
...builtinCommands,
]
}