I’m liking outlining in Jesse Grossjean’s latest app, Bike, for my outlining needs. It’s a simple outliner that can save the content of its outlines to Bike files, OPML documents, or plain text. And its native document format is plain HTML that’s easy to work with.

As an aside, Jesse just released Shortcut actions for Bike, making it possible to do some automation of Bike and Bike documents. I haven’t played around with it much yet, but if you’re into Shortcuts, check out what’s available.

One thing that Bike lacks is an easy way to convert Markdown lists to Bike outlines. It can actually read indented plain text just fine, but the list markers are included in the node text, and blank lines become empty nodes instead of being compressed. Running a list through a Markdown processor and saving as .bike can often create an invalid file, as Bike requires every list item to contain a paragraph tag, not bare text.

I initially played around with running Markdown through a Markdown processor and then manipulating the output, but ultimately went with a much simpler version that compresses newlines and removes list markers, putting the resulting indented text in your clipboard. Pasting the result into a Bike document should almost always yield the expected result.

Save the script below as md_to_bike in your PATH and run chmod a+x path/to/md_to_bike. It can be called as a pipe or on a file. To call it as a pipe, run cat myfile.md | md_to_bike. To call it on a file, just provide the path to the file as an argument: mmd_to_bike path/to/myfile.md. The results will be placed in your clipboard, ready to paste into a Bike document.

#!/usr/bin/env ruby
# frozen_string_literal: true

# This script takes simple Markdown lists and converts them for use in
# Bike (https://www.hogbaysoftware.com/bike/), a Mac outliner app from
# Hog Bay Software.
# It doesn't handle nested code blocks or any non-list Markdown. It just
# turns simple lists into text Bike will recognize when it's pasted into
# a Bike document. Multiple paragraphs in a list item become additional
# nodes at the same level.

require 'shellwords'
require 'fcntl'

stdin = $stdin.read.strip if $stdin.stat.size.positive? || $stdin.fcntl(Fcntl::F_GETFL, 0).zero?

if !stdin && ARGV.count != 1
  exe = File.basename(__FILE__)
  puts "#{exe} requires one argument"
  puts "#{exe} [markdown file]"
  Process.exit 1

source = ARGV[0]

# Read input file
content = stdin ? stdin : IO.read(source).strip
# Remove spaces from empty lines
content.gsub!(/^\s*\n/, "\n")
# Replace consecutive empty lines with a single newline
content.gsub!(/\n+/, "\n")

# Replace 2 or 4 spaces with tabs (based on detected indentation)
m = content.match(/^ {2,4}/) # find first indentation
indentation = m ? m[0].length : 1 # determine whether first indent is 2 or 4 spaces
content.gsub!(/^( {#{indentation}})+(?=\S)/) do |m| # Replace leading spaces with tabs
  mtch = Regexp.last_match(0)
  mtch ? "\t" * (mtch.length / indentation) : m # tab * detected indent / detected indentation

# Replace markdown list markers
content.gsub!(/(\s*)[+*\-] /, '\1')

# Copy result to clibpoard
`echo #{Shellwords.escape(content)} | pbcopy`

puts "Results in clipboard. Paste into a Bike document."

Hope that’s of use to somebody.