0.0
The project is in a healthy, maintained state
A worked example of the Terret extension story: a third-party gem that ships one tool (`fortune`, one line from a vendored list) and declares itself a bundle in a line of gemspec metadata, so a profile can stack it by name. Stdlib-only beyond terret-core. The running example of docs/cookbook/adding-a-tool.md, kept in lockstep with that page.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

terret-fortune

A worked example of the Terret extension story: a third-party gem that ships one tool and joins an agent's boot by shipping normally. Install it, name it in a profile, permit the tool, and a Terret agent can call fortune — one short, pithy line from a vendored list.

This gem is the running example of Terret's docs/cookbook/adding-a-tool.md and docs/cookbook/adding-a-bundle.md, kept in lockstep with those pages: what they describe generically, this repository is concretely.

What it is

fortune is the smallest honest tool there is: a pure read with no side effect. Its metadata says so — mutating: false, approval: :never, concurrency: :parallel — and those three fields are what the loop's tool barrier and the approvals gate act on, so they are the truth about what calling it does, not a convenience.

terret-fortune/
├── terret-fortune.gemspec      # declares the gem a bundle (metadata["terret"])
├── config/
│   └── bundle.yml              # one row mounting Terret::Fortune::Tool
├── lib/
│   └── terret/
│       ├── fortune.rb          # entry: requires terret-core, then the service
│       └── fortune/
│           ├── tool.rb         # the Hames service that registers `fortune`
│           └── fortunes.txt    # the vendored list
└── test/
    └── fortune_test.rb         # boots the service, drives the real tool pipeline

The extension story, end to end

1. Write the tool

A tool provider is an ordinary Hames service: it injects ctx[:tools] and registers a Definition in start. lib/terret/fortune/tool.rb is the whole of it — the same shape gems/terret-tools-std uses for the standard roster.

2. Declare the gem a bundle

One line of gemspec metadata makes the gem discoverable. The value is the string path to the bundle file — RubyGems validates every metadata value as a String, so a nested hash does not build at all:

# terret-fortune.gemspec
s.metadata = { "terret" => "config/bundle.yml" }
s.add_dependency "terret-core", "~> 0.1"   # the code the row mounts

The bundle file is an ordered list of rows plus the requires its constants need:

# config/bundle.yml
name: terret-fortune
requires:
  - terret/fortune
rows:
  - id: fortune
    plugin: Terret::Fortune::Tool

3. Install it

gem install terret-fortune
# or, in a Gemfile:
#   gem "terret-fortune"

gem install is the whole registration mechanism. Terret's discovery walks every gemspec it can see, reads the terret metadata key, and parses the file it points at — so a third-party gem becomes discoverable by shipping normally. Nothing to register, no directory to drop a file into.

4. Stack it in a profile

A profile names bundles by gem name, in stack order, terret (terret-base) always layer one:

# ~/.terret/profiles/headless/profile.yml
bundles:
  - terret          # terret-base, always first
  - terret-fortune  # this gem

5. Permit the tool

This is the step people forget: mounting the tool does not make it callable. Terret's allow list is deny-by-default, and the base floor names exactly the standard roster and nothing else. fortune is denied until a profile permits it — and because a patch replaces a row's config wholesale, you restate the whole list, not just the new name:

# ~/.terret/profiles/headless/patch.yml
rows:
  - id: allow_list
    config:
      patterns:
        - Read
        - Write
        - Edit
        - Glob
        - Grep
        - Bash
        - fortune          # the new tool, now permitted
        # ... restate the rest of the roster you still want

6. Boot

ctx = Terret.boot(profile: "headless")

That resolves the layers, discovers terret-fortune, requires its code, mounts its row, and registers the tool. From here the agent can call fortune.

Running the tests

# Against a published terret-core:
bundle install
bundle exec rake test

# Against a local Terret monorepo checkout (no publish needed):
TERRET_CHECKOUT=/path/to/terret bundle exec rake test

License

MIT. See LICENSE.