2026-02-25 10:45:29 -08:00
/**
* ECC Custom Tool: Format Code
*
2026-03-04 14:48:06 -08:00
* Returns the formatter command that should be run for a given file.
* This avoids shell execution assumptions while still giving precise guidance.
2026-06-16 02:01:34 +08:00
* Supports cross-platform command generation.
2026-02-25 10:45:29 -08:00
*/
2026-04-05 15:11:19 -07:00
import { tool , type ToolDefinition } from "@opencode-ai/plugin/tool"
2026-03-04 14:48:06 -08:00
import * as path from "path"
import * as fs from "fs"
2026-06-16 02:01:34 +08:00
type Formatter = "biome" | "prettier" | "black" | "gofmt" | "rustfmt" | "swift-format"
interface FormatResult {
success : boolean
formatter? : Formatter
command? : string
instructions? : string
message? : string
error? : string
}
2026-02-25 10:45:29 -08:00
2026-04-05 15:11:19 -07:00
const formatCodeTool : ToolDefinition = tool ({
2026-03-04 14:48:06 -08:00
description :
2026-06-16 02:01:34 +08:00
"Detect formatter for a file and return the exact command to run (Biome, Prettier, Black, gofmt, rustfmt, swift-format). Supports cross-platform command generation." ,
2026-03-04 14:48:06 -08:00
args : {
filePath : tool.schema.string (). describe ( "Path to the file to format" ),
formatter : tool.schema
2026-06-16 02:01:34 +08:00
. enum ([ "biome" , "prettier" , "black" , "gofmt" , "rustfmt" , "swift-format" ])
2026-03-04 14:48:06 -08:00
. optional ()
. describe ( "Optional formatter override" ),
},
2026-06-16 02:01:34 +08:00
async execute ( args , context ) : Promise < string > {
try {
const cwd = context . worktree || context . directory
const ext = args . filePath . split ( "." ). pop () ? . toLowerCase () || ""
const detected = args . formatter || detectFormatter ( cwd , ext )
2026-02-25 10:45:29 -08:00
2026-06-16 02:01:34 +08:00
if ( ! detected ) {
return JSON . stringify ({
success : false ,
message : `No formatter detected for . ${ ext } files` ,
supportedFormatters : [ "biome" , "prettier" , "black" , "gofmt" , "rustfmt" , "swift-format" ],
})
}
const command = buildFormatterCommand ( detected , args . filePath , cwd )
return JSON . stringify ({
success : true ,
formatter : detected ,
command ,
instructions : `Run this command: \ n \ n ${ command } ` ,
platform : process.platform ,
})
} catch ( error : unknown ) {
const errorMessage = error instanceof Error ? error.message : String ( error )
2026-03-04 14:48:06 -08:00
return JSON . stringify ({
success : false ,
2026-06-16 02:01:34 +08:00
error : `Failed to detect formatter: ${ errorMessage } ` ,
filePath : args.filePath ,
2026-03-04 14:48:06 -08:00
})
2026-02-25 10:45:29 -08:00
}
},
})
2026-03-04 14:48:06 -08:00
2026-04-05 15:11:19 -07:00
export default formatCodeTool
2026-03-04 14:48:06 -08:00
function detectFormatter ( cwd : string , ext : string ) : Formatter | null {
2026-06-16 02:01:34 +08:00
// Check for formatter config files
const hasConfig = ( configFiles : string []) : boolean => {
return configFiles . some ( configFile => fs . existsSync ( path . join ( cwd , configFile )))
}
// JavaScript/TypeScript files
2026-03-04 14:48:06 -08:00
if ([ "ts" , "tsx" , "js" , "jsx" , "json" , "css" , "scss" , "md" , "yaml" , "yml" ]. includes ( ext )) {
2026-06-16 02:01:34 +08:00
if ( hasConfig ([ "biome.json" , "biome.jsonc" ])) {
2026-03-04 14:48:06 -08:00
return "biome"
}
return "prettier"
}
2026-06-16 02:01:34 +08:00
// Python files
if ([ "py" , "pyi" ]. includes ( ext )) {
return "black"
}
// Go files
if ( ext === "go" ) {
return "gofmt"
}
// Rust files
if ( ext === "rs" ) {
return "rustfmt"
}
// Swift files
if ( ext === "swift" ) {
return "swift-format"
}
2026-03-04 14:48:06 -08:00
return null
}
2026-06-16 02:01:34 +08:00
function buildFormatterCommand ( formatter : Formatter , filePath : string , cwd? : string ) : string {
// Normalize path for cross-platform compatibility
const normalizedPath = path . normalize ( filePath )
// Build command based on formatter and platform
2026-03-04 14:48:06 -08:00
const commands : Record < Formatter , string > = {
2026-06-16 02:01:34 +08:00
biome : `npx @biomejs/biome format --write ${ normalizedPath } ` ,
prettier : `npx prettier --write ${ normalizedPath } ` ,
black : `black ${ normalizedPath } ` ,
gofmt : `gofmt -w ${ normalizedPath } ` ,
rustfmt : `rustfmt ${ normalizedPath } ` ,
"swift-format" : `swift-format format --in-place ${ normalizedPath } ` ,
2026-03-04 14:48:06 -08:00
}
2026-06-16 02:01:34 +08:00
2026-03-04 14:48:06 -08:00
return commands [ formatter ]
}