Skip to content

eessmann/monk

Repository files navigation

Monk

Monk is a Haskell project that tries to translate Bash scripts into fish. It started as a fun excuse to learn more about shell parsing, typed ASTs, and all the weird corners where Bash and fish do not line up cleanly.

Monk is deliberately conservative: it translates what it understands, emits warnings for the parts that need a human to look again, and can fail fast in --strict mode when it would rather stop than try its best.

What It Does

Monk parses Bash with ShellCheck, lowers it into a typed fish IR, and renders fish source from there.

Today it handles a lot of ordinary shell code:

  • control flow such as if, while, for, and case
  • functions, arrays, variable assignments, and common special variables
  • pipelines, background jobs, and command substitution
  • redirections, here-strings, and a chunk of process substitution
  • recursive source translation for literal source paths

It also has a long tail of best-effort behavior.

What To Expect

If you run Monk on a script, the happy path is:

  1. it produces fish output
  2. it tells you where translation got lossy or approximate
  3. you review the result like generated migration code, not handwritten code

The current source of truth for exact vs best-effort behavior is docs/design/translator-audit.md.

Constructs that still deserve extra attention include:

  • subshell-heavy scripts
  • read edge cases and delimiter-sensitive behavior
  • set -e / pipefail interactions in compound shell logic
  • non-literal source
  • option-heavy trap, shopt, and coproc
  • Linux-only >(...) coverage

Quick Start

Build it from source:

git clone https://github.com/eessmann/monk.git
cd monk
cabal build

Translate a script:

monk script.sh > script.fish
monk script.sh --output script.fish
monk script.sh --strict
monk script.sh --recursive --sources separate

Useful flags:

  • --output FILE writes to a file instead of stdout
  • --strict turns best-effort warnings into failures where supported
  • --quiet-warnings suppresses warning output
  • --recursive follows literal source / .
  • --sources inline|separate controls how recursive source translation is emitted

Warnings and notes go to stderr.

Library Surface

The public modules are intentionally small:

  • Monk.Translation for parse + translate entry points
  • Monk.Translation.Types for the stable translation/diagnostics contract
  • Monk.AST for the public fish AST
  • Monk.Source for recursive source-graph helpers
  • Monk.Diagnostics for warning rendering and confidence summaries
  • Monk as a thin convenience re-export

Example:

import Monk.Translation

main :: IO ()
main = do
  result <- translateBashFile defaultConfig "script.sh"
  case result of
    Left err -> print err
    Right translation -> do
      putStrLn (toString (renderTranslation translation))
      print (translationWarnings translation)

Development

The normal local loop is:

cabal build
cabal test
MONK_INTEGRATION=1 cabal test
hlint .

There is also a bake-off runner for comparing Monk and Babelfish:

cabal run monk-bakeoff -- --compatible --no-benchmark --out-dir /tmp/monk-bakeoff

Bake-off prerequisites:

  • babelfish and fish are required
  • hyperfine is optional and only needed for benchmark runs
  • the runner now validates tool paths up front and reports actionable preflight errors or benchmark-skip notes

Docs

  • docs/design/translator-audit.md: fidelity matrix and evidence backlog
  • docs/design/translator-todo.md: active translator backlog
  • docs/design/architecture.md: module layout and subsystem boundaries
  • docs/migration-guide.md: manual cleanup patterns after translation
  • docs/babelfish-comparison.md: current bake-off workflow and comparison notes

About

Bash to Fish translator

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors