packages feed

scripths-0.5.1.0: README.md

# scripths

GHCi scripts for standalone execution and Markdown documentation.

**scripths** lets you write `.ghci` scripts with dependency management and run them as standalone programs — or embed executable Haskell code blocks in Markdown files and evaluate them notebook-style with captured output.

## Features

- **Standalone `.ghci` execution** — Run GHCi scripts directly from the command line, with automatic dependency.
- **Compiled or intepreted** - you can either compile or interpret cells. Heavier functions can be compiled so they move fast and interactive parts can be kept interpreted.
- **Cabal metadata directives** — Declare `build-depends`, `default-extensions`, `ghc-options`, and local `packages` inline using `-- cabal:` comments.
- **Markdown notebooks** — Execute Haskell code blocks inside Markdown files and render the output back into the document as block quotes. Re-run in place with `-i` — output is replaced cleanly, with no accumulating blank lines.
- **Inline errors** — A block that fails to compile renders its GHC error beneath the block instead of producing silent empty output.
- **Smart GHCi rendering** — Multi-line definitions are automatically wrapped in `:{`/`:}` blocks, and IO binds / Template Haskell splices are handled correctly as individual statements.
- **Compile-time TH reads your tree** — A splice that reads a file at compile time (e.g. `$(declareTable "./data/x.db" …)`) resolves `./relative` paths against the directory you ran `scripths` from, not the internal build directory.
- **Version tag** — Files record the scripths that wrote them on their first line; running a file authored by a newer scripths than your binary prints a warning rather than failing.

## Installation

```bash
cabal install scripths
```

## CLI Usage

```
scripths [-o FILE | --output=FILE] [-i | --in-place] [-p DIR | --package DIR]... [--no-local-project] [-h | --help] [-v | --version] <script>
```

When `-o` / `--output` is provided for Markdown files, the result is written to that path. Otherwise it is printed to stdout. With `-i` / `--in-place` the notebook is rewritten in place: any previously rendered output is stripped and replaced, and re-running is idempotent (it does not accumulate blank lines). `-i` is only valid for `.md` / `.markdown` notebooks and cannot be combined with `-o`.

The file extension determines the mode. `.ghci` / `hs` files are parsed and executed as a standalone GHCi script. `.md` / `.markdown` files are processed as a notebook with captured output. Run `scripths --help` for the full option and directive list, or `scripths --version` to print the version.

## Version tag

Each file can carry a first-line tag recording the scripths version that authored it, so you can tell whether your binary is current enough to parse it. The spelling matches each file type's comment syntax:

```
-- scripths: 0.4.1.0          # .ghci / .hs scripts
<!-- scripths: 0.4.1.0 -->    # .md / .markdown notebooks (invisible when rendered)
```

When scripths writes a notebook (`-o` / `--in-place`) it stamps or refreshes this tag at the top of the document; output streamed to stdout is left untouched. When you run a file whose tag declares a **newer** scripths than your binary, scripths warns on stderr and proceeds anyway (the syntax may not fully parse). The tag is recognised after a leading `#!` shebang (scripts) or YAML frontmatter block (notebooks).

Requires GHC and cabal-install on your PATH.

## Quick Start

### Running a GHCi script

Create a file `example.ghci`:

```haskell
double :: Int -> Int
double = (*2)

-- cabal: build-depends: text
:set -XOverloadedStrings
import qualified Data.Text as T

T.take 10 "hello"

double 5
```

Run it:

```bash
scripths example.ghci
```

### Running a Markdown notebook

Create a file `notebook.md`:

````markdown
# My Analysis

Some introductory prose.

```haskell
print (5 + 5)
```

Define some values:

```haskell
x = 10
y = 20
```

We can then add the values in the next block and
the output is embeded below.

```haskell
x + y
```
````

Run it and write the results to a new file:

```bash
scripths -o output.md notebook.md
```

Each Haskell code block is evaluated in order, and its output is inserted into the Markdown as a block quote beneath the code fence (tagged with a `<!-- scripths:mime … -->` marker so a later run can find and replace it). If a block fails to compile, its GHC error is rendered inline beneath that block rather than vanishing.

> **Tip:** each code block should end in a single bare expression so it is auto-printed. A block that ends in a `<-` bind (or several `;`-joined statements) prints nothing — repeat the bound name on a final line to print it:
>
> ````markdown
> ```haskell
> x <- pure 42
> x
> ```
> ````

## Cabal Metadata Directives

You can declare dependencies, language extensions, and GHC options directly inside your scripts using `-- cabal:` comments:

```
-- cabal: build-depends: text, containers
-- cabal: default-extensions: OverloadedStrings, TypeApplications
-- cabal: ghc-options: -Wall
-- cabal: packages: ../sibling-pkg
-- cabal: source-repository-package: https://github.com/owner/repo v1.2.3
```

The recognised keys are `build-depends`, `default-extensions`, `ghc-options`, `packages` (extra local package directories, relative to the script), and `source-repository-package`. An unrecognised key is reported as a warning.

`OverloadedStrings` is enabled in every scripths repl by default, so string literals work directly as `Text` / `ByteString`; add any further extensions with `default-extensions`.

## The `-- compile` directive

A notebook cell (or script) can be marked **compiled**:

```
-- compile
```

or, naming the generated module explicitly:

```
-- compile: Training.Core
```

The directive declares that the cell's contents are *module-level
declarations* destined for a generated Haskell module rather than the GHCi
prompt — a host environment (such as [Sabela](https://github.com/mchav/sabela))
writes those declarations into a module, loads it with `:load` under
`-fobject-code -O2`, and gets native compiled code instead of interpreted
bytecode. Cells sharing a module name merge into one module; bare
`-- compile` cells share a default module.

What scripths provides:

- `ScriptHs.Parser` recognises the directive (`scriptCompile` on
  `ScriptFile`) and offers `parseScriptNumbered`, a line-number-preserving
  parse.
- `ScriptHs.Compiled` validates that a cell is declarations-only
  (`checkCompilable`) and renders the generated module
  (`renderCompiledModule`), preceding every unit with a
  `{-# LINE n "sabela-cell-<id>" #-}` pragma so GHC diagnostics map back to
  the originating cell and line (`linePragmaTag` / `parseLinePragmaTag`).

Under the **scripths CLI** the directive is currently a graceful no-op: it
parses as an ordinary comment and the cell runs interpreted, producing the
same results (just slower). Standalone compiled execution in the CLI may
come later.

## Local packages

By default `build-depends` resolve from Hackage. To build a script or notebook
against a **local checkout** instead — e.g. to document a library version that
only exists on a branch — there are two mechanisms.

### The enclosing project (zero-config)

If a script/notebook lives inside a cabal project and a `build-depends` names that
project's package (or one of its sub-libraries, `pkg:sublib`), scripths builds
against the local working tree automatically — so documenting your own repo just
works:

```bash
scripths -o docs/MANUAL.md docs/base/MANUAL.md
```

Pass `--no-local-project` to suppress this and resolve every `build-depends` from
Hackage instead — e.g. to verify the doc's examples against the *released*
version of your package rather than the working tree. Explicit `--package` dirs
are unaffected.

### `--package DIR`

Point at another local checkout (a sibling package, or a sub-library project).
`DIR` must be a package root (contain a `.cabal`); it is resolved to an absolute
path at the invocation site, so nothing machine-specific is committed to the
shared document. Repeatable.

```bash
scripths --package ../granite -o out.md in.md
```

The package's name is read from its `.cabal` and added to the script's
`build-depends` automatically, so its modules are importable without also listing
it in a `-- cabal: build-depends:` line.

### `packages:` directive (committed into the document)

To depend on one or more local packages *from inside the document itself* — for
example a second, non-enclosing package in the same repo — list their directories
(relative to the script) with a `packages` directive:

```
-- cabal: packages: ../sibling-pkg, ../another-local
```

Like `--package`, each named package's name is added to `build-depends`
automatically. Unlike `--package`, the dependency travels with the document. Paths
are resolved relative to the script file.

### Pinned git packages (`source-repository-package`)

To pin a **pushed** commit/tag reproducibly (and commit the pin into the shared
document), declare a `source-repository-package` directive as
`<location> <ref> [subdir]`:

```
-- cabal: source-repository-package: https://github.com/owner/repo v1.2.3
-- cabal: source-repository-package: https://github.com/owner/mono abc123 sub/dir
```

scripths writes these as `source-repository-package` stanzas in the generated
`cabal.project`. Use a local checkout (above) for an **unpushed** branch, and a git
pin for a **pushed**, reproducible reference.