the_local 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -1
  3. data/CLAUDE.md +117 -0
  4. data/MIGRATING.md +478 -0
  5. data/PROVIDERS.md +62 -123
  6. data/README.md +20 -14
  7. data/lib/generators/the_local/provider_generator.rb +15 -90
  8. data/lib/the_local/agent.rb +3 -2
  9. data/lib/the_local/author.rb +43 -0
  10. data/lib/the_local/creators/develop.md +81 -0
  11. data/lib/the_local/creators/info.md +67 -0
  12. data/lib/the_local/creators/install.md +79 -0
  13. data/lib/the_local/disk_providers.rb +18 -10
  14. data/lib/the_local/format.rb +27 -0
  15. data/lib/the_local/front_matter.rb +26 -0
  16. data/lib/the_local/installer.rb +2 -2
  17. data/lib/the_local/interface.rb +26 -0
  18. data/lib/the_local/provider_check.rb +92 -0
  19. data/lib/the_local/rake.rb +14 -9
  20. data/lib/the_local/registry.rb +2 -30
  21. data/lib/the_local/trigger_writer.rb +1 -1
  22. data/lib/the_local/version.rb +1 -1
  23. data/lib/the_local.rb +1 -20
  24. data/the_local/agents/the_local-develop.md +48 -0
  25. data/the_local/agents/the_local-info.md +40 -0
  26. data/the_local/agents/the_local-install.md +49 -0
  27. data/the_local/interface.yml +21 -0
  28. metadata +16 -12
  29. data/lib/generators/the_local/templates/guide.md.tt +0 -41
  30. data/lib/generators/the_local/templates/reference.rb.tt +0 -17
  31. data/lib/generators/the_local/templates/the_local.rb.tt +0 -49
  32. data/lib/the_local/builder.rb +0 -57
  33. data/lib/the_local/reference/guide.md +0 -179
  34. data/lib/the_local/reference.rb +0 -16
  35. data/lib/the_local/the_local/agents/the_local-develop.md +0 -187
  36. data/lib/the_local/the_local/agents/the_local-info.md +0 -187
  37. data/lib/the_local/the_local/agents/the_local-install.md +0 -187
  38. data/lib/the_local/the_local.rb +0 -59
data/PROVIDERS.md CHANGED
@@ -6,134 +6,73 @@ of a project's direct dependencies into `.claude/agents/` — by reading each
6
6
  dependency's committed `.md` straight from its gem path on disk. This document
7
7
  covers the **provider** side: how a gem contributes those locals.
8
8
 
9
- A provider registers its agents with `TheLocal.register` behind a soft
10
- `require "the_local"` guard, so the gem keeps working when `the_local` is absent.
11
-
12
- ## Build at home, copy verbatim
13
-
14
- The agent *definition* (`the_local.rb` + `guide.md`) is the single source of
15
- truth. The provider **renders it to committed `.md` files** with a gem-side
16
- `the_local:build` task and commits those files to its own repo; the host install
17
- then **reads them straight from your gem's path on disk and copies them verbatim**
18
- into `.claude/agents/`. No provider code is loaded in the host and no register
19
- block runs there — the committed, shipped `.md` is the entire contract. If those
20
- files aren't committed and in the gemspec's `files`, the gem contributes nothing;
21
- if they are, it contributes everything, with no install-time wiring.
22
-
23
- So the rendered output depends only on the provider gem version — every app that
24
- installs the same version gets a byte-identical local, instead of the host
25
- re-rendering (and possibly drifting) from an in-memory definition. The committed
26
- `.md` is a reviewable build artifact: it lands in the gem's own PR. Keep it in
27
- sync with the definition by re-running `the_local:build` and committing the
28
- result whenever you change the guide or an agent's `body:`/`description:`.
29
-
30
- ## The common command interface
31
-
32
- `the_local` exists to give every gem the **same command interface to apps**, so
33
- a host agent always finds the same shape no matter which gem it's delegating to.
34
- A provider exposes three lifecycle facets:
35
-
36
- | Facet | Purpose | Typical tools |
37
- |---|---|---|
38
- | **`info`** | Read-only. Explains what the gem offers — its API and conventions. Makes no changes. | `Read` |
39
- | **`install`** | Adds the gem to a host and sets it up **correctly** — the exact, gem-specific steps. | `Bash, Read, Edit` |
40
- | **worker** | The proactive domain worker the host routes real work to. Named `develop` for libraries you build against, `operate` for CLIs you run. | per domain |
9
+ A provider commits a manifest, `the_local/interface.yml`, and the three locals
10
+ authored from it at `the_local/agents/<gem>-{info,install,develop}.md`. It ships
11
+ no Ruby of its own.
41
12
 
42
- `info` and `install` are universal; the worker's name varies by the gem's
43
- nature. Every agent embeds the provider's knowledge (`Reference.content`)
44
- verbatim — that knowledge is the single source of truth, so the locals never
45
- drift from the docs.
13
+ ## Declare the interface, author the locals
46
14
 
47
- ## Adopting it Rails-engine gems (generator)
15
+ **The manifest is the one judgment call, and a human makes it.** It names the
16
+ gem's public entry points and assigns each to the local that owns it:
48
17
 
49
- If the gem has Rails available in development (e.g. a mountable engine), scaffold
50
- the wiring with the generator:
18
+ ```yaml
19
+ scope: <one line: the user-visible work this gem owns>
51
20
 
52
- ```bash
53
- bin/rails g the_local:provider <gem_name> \
54
- --scope "one-line phrase describing the gem's domain" \
55
- [--prefix <filename-namespace>] \
56
- [--worker develop|operate]
57
- ```
21
+ install:
22
+ - bin/rails g demo:install
58
23
 
59
- It creates, and wires up:
24
+ develop:
25
+ - Demo.emit
60
26
 
27
+ sources:
28
+ - lib/generators/demo/install_generator.rb
29
+ - lib/demo/emitter.rb
61
30
  ```
62
- lib/<gem>/reference.rb # the Reference loader (single source of truth)
63
- lib/<gem>/reference/guide.md # the knowledge, with TODO markers to fill in
64
- lib/<gem>/the_local.rb # Companion.register! — info / install / <worker>
65
- lib/<gem>/the_local/agents/*.md # the rendered locals, built + committed
66
- Gemfile # + gem "the_local", github: … (soft, dev/test)
67
- lib/<gem>.rb # + require_relative "<gem>/the_local"
68
- Rakefile # + require "the_local/rake" (rake the_local:build)
69
- ```
70
31
 
71
- The generator builds the `.md` once on scaffold so they land in the diff for
72
- review. They are rendered from the TODO placeholder definition, so you rebuild
73
- them after filling in the real content (below).
74
-
75
- Then **fill in the scaffold** — this is the real work the generator can't do:
76
-
77
- 1. Write `reference/guide.md` as the complete user-facing API. The bar is that
78
- a host agent can do your gem's work *from the guide alone, without opening
79
- your source*: surface the literal interface (exact signatures — arguments,
80
- required vs optional, return) and a complete copy-paste recipe for the common
81
- task, not prose about them. Its **Install** section must be the exact, correct
82
- steps for *this* gem (for an engine: add the gem → `bundle install` → install
83
- + run migrations → wire concerns / initializers), not a generic placeholder.
84
- Replace every `TODO:` `rake the_local:build` refuses a guide that keeps one.
85
- 2. Tailor the three agent `body:` strings in `the_local.rb` to the gem.
86
- 3. **Rebuild and commit the locals.** The scaffold built `.md` from the TODO
87
- placeholders, so regenerate them from your real definition and commit them:
88
-
89
- ```bash
90
- rake the_local:build
91
- git add lib/<gem>/the_local/agents
92
- ```
93
-
94
- Rebuild whenever the guide or an agent's `body:`/`description:` changes — the
95
- host copies these bytes verbatim, so a stale commit ships stale locals.
96
- 4. Add a `companion_test` asserting the facets register and each embeds
97
- `Reference.content`, plus a **drift test** asserting each committed file
98
- equals its `agent.to_markdown` (so a forgotten rebuild fails CI). See
99
- `test/the_local/companion_test.rb` the_local is its own provider and uses
100
- exactly this wiring.
101
-
102
- ## Adopting it — non-Rails gems (manual)
103
-
104
- A plain gem has no `bin/rails`, so do the same things by hand. `the_local` is
105
- itself a non-Rails provider built this way mirror its own wiring
106
- (`lib/the_local/the_local.rb`, `lib/the_local/reference.rb`, the committed
107
- `lib/the_local/the_local/agents/`, and the `Rakefile`):
108
-
109
- 1. `lib/<gem>/reference.rb` a `Reference` module with `DIR`, `content`, and
110
- `read(name)` reading from `lib/<gem>/reference/`.
111
- 2. `lib/<gem>/reference/guide.md` the knowledge (single source of truth).
112
- 3. `lib/<gem>/the_local.rb` — a `Companion.register!` that calls `TheLocal.register`
113
- with an `agents_dir` (where the committed `.md` live) and declares the
114
- `info` / `install` / worker agents, followed by the guard:
115
-
116
- ```ruby
117
- TheLocal.register("<gem>", scope: "…",
118
- agents_dir: File.expand_path("the_local/agents", __dir__)) do |c|
119
- # c.agent "info", …
120
- end
121
- ```
122
-
123
- ```ruby
124
- begin
125
- require "the_local"
126
- <Gem>::Companion.register!
127
- rescue LoadError
128
- # the_local not installed — <gem> works standalone.
129
- end
130
- ```
131
- 4. `require_relative "<gem>/the_local"` from the gem's entrypoint (so your own
132
- `the_local:build` and standalone use load the register block — a host never
133
- needs it), and add `gem "the_local"` to the Gemfile (dev/test — an optional
134
- companion, not a hard dependency).
135
- 5. Add `require "the_local/rake"` to the `Rakefile`, then build, commit, and
136
- **ship** the rendered locals — `rake the_local:build && git add
137
- lib/<gem>/the_local/agents`, and make sure they're in the gemspec's `files`.
138
- These committed bytes are the whole contract — what the host reads from disk;
139
- rebuild and recommit on every change.
32
+ `rake the_local:author` then runs the_local's creators against that declaration
33
+ and the files under `sources:`, writing the three locals. The creators no longer
34
+ guess at what matters they document what you declared, verified against the
35
+ code as it is now rather than against a README that may be stale.
36
+
37
+ `rake the_local:check` enforces the contract in both directions: every declared
38
+ entry point is documented by its own local, nothing undeclared appears anywhere,
39
+ and nothing appears in a local it wasn't declared for.
40
+
41
+ ## What each local is for
42
+
43
+ The three never overlap. One entry point, one local.
44
+
45
+ | Local | Purpose | Tools |
46
+ |---|---|---|
47
+ | **`install`** | Hooking the gem into a consumer — the exact setup steps. | `Bash, Read, Edit` |
48
+ | **`develop`** | Using the gem — calling its entry points from consuming code. | `Read, Write, Edit, Grep` |
49
+ | **`info`** | Read-only catchall: what the gem is, and which of the other two to reach for. | `Read` |
50
+
51
+ They are step-by-step implementation guides, kept as short as the job allows.
52
+ Where a step takes a real decision, the local surfaces the question to the
53
+ developer rather than choosing. A local never sends its reader into the
54
+ provider's source.
55
+
56
+ ## The committed files are the contract
57
+
58
+ The host install **reads them straight from your gem's path on disk and copies
59
+ them verbatim** into `.claude/agents/` no provider code is loaded in the host.
60
+ If they aren't committed (and, for a packaged gem, in the gemspec's `files`), the
61
+ gem contributes nothing.
62
+
63
+ ## Adopting it
64
+
65
+ 1. **Wire the tooling.** In a Rails-engine gem, run `bin/rails g the_local:provider`;
66
+ it adds `gem "the_local"` to the Gemfile and `require "the_local/rake"` to the
67
+ Rakefile. A plain gem does those two edits by hand.
68
+ 2. **Declare the interface.** Write `the_local/interface.yml`. Authoring refuses
69
+ to run without it.
70
+ 3. **Author.** Run `rake the_local:author`.
71
+ 4. **Check and commit.** Fix what `rake the_local:check` reports, then
72
+ `git add the_local`. For a packaged gem, make sure `the_local/**/*` is in the
73
+ gemspec's `files`.
74
+ 5. **Keep it current.** When the gem's public interface changes, update the
75
+ manifest and re-author. An internal-only change needs nothing.
76
+
77
+ `the_local` is its own first provider and uses exactly this wiring;
78
+ `test/the_local/dogfood_test.rb` asserts its committed locals match its manifest.
data/README.md CHANGED
@@ -24,10 +24,14 @@ change, point at the repo instead:
24
24
  gem "the_local", github: "DYB-Development/the_local"
25
25
  ```
26
26
 
27
- The gem's core is Rails-free, but the primary documented workflow uses the
28
- `bin/rails g the_local:install` / `the_local:provider` generators, so those steps
29
- assume a Rails host. The `the_local:refresh` rake task and the register API work
30
- without Rails.
27
+ The gem's core is Rails-free. `bundle exec the_local install` and the
28
+ `the_local:check`/`the_local:refresh` rake tasks work without Rails; a Rails host
29
+ can equivalently use the `bin/rails g the_local:install` / `the_local:provider`
30
+ generators.
31
+
32
+ Once installed, the locals themselves know the rest — the `the_local-info`,
33
+ `the_local-install`, and `the_local-develop` agents explain, set up, and drive
34
+ the_local, so this README stays short on purpose.
31
35
 
32
36
  ## Usage
33
37
 
@@ -37,21 +41,23 @@ There are two sides.
37
41
  app's own) into `.claude/agents/`, and write the delegation trigger:
38
42
 
39
43
  ```bash
40
- bin/rails g the_local:install
44
+ bundle exec the_local install
41
45
  ```
42
46
 
43
- Install **copies each provider's committed `.md` verbatim** — the rendering
44
- happens in the provider gem at build time, not in the host so every app on the
45
- same gem version gets a byte-identical local. Re-run `the_local:refresh` (rake)
46
- after a `bundle install`/`update` to re-sync.
47
+ Install **copies each provider's committed `.md` verbatim** — no provider code is
48
+ loaded so every app on the same gem version gets a byte-identical local. Re-run
49
+ it (or `rake the_local:refresh`) after a `bundle install`/`update` to re-sync.
47
50
 
48
- **Provider gem** — contribute the locals an app installs. A gem registers its
49
- agents with `TheLocal.register` behind a soft guard, exposing the common command
50
- interface (`info` / `install` / worker), then renders them to committed `.md`
51
- with `rake the_local:build`. Scaffold it with:
51
+ **Provider gem** — contribute the locals an app installs. A gem declares its
52
+ public interface in `the_local/interface.yml` and commits the three locals
53
+ authored from it, `the_local/agents/<gem>-{info,install,develop}.md`; it ships no
54
+ Ruby. `rake the_local:author` writes those locals against the declaration and the
55
+ gem's current code, and `rake the_local:check` fails if they document more or less
56
+ than was declared — so the docs never drift from the source. Wire the tooling
57
+ with:
52
58
 
53
59
  ```bash
54
- bin/rails g the_local:provider <gem_name> --scope "the gem's domain"
60
+ bin/rails g the_local:provider
55
61
  ```
56
62
 
57
63
  See [PROVIDERS.md](PROVIDERS.md) for the full provider guide, including the
@@ -1,47 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators"
4
- require "the_local/builder"
5
4
 
6
5
  module TheLocal
7
6
  module Generators
8
- # `bin/rails g the_local:provider <gem_name>` — scaffolds the provider side
9
- # of the_local into a gem: a Reference loader, a knowledge guide, and a
10
- # Companion that registers the common command interface (info / install /
11
- # worker) via TheLocal.register behind a soft `require "the_local"` guard.
12
- #
13
- # The companion app side is `the_local:install`; this is its mirror for the
14
- # gems that *contribute* locals. See PROVIDERS.md.
15
7
  class ProviderGenerator < Rails::Generators::Base
16
8
  source_root File.expand_path("templates", __dir__)
17
9
 
18
10
  GEMFILE_LINE = %(gem "the_local")
19
11
  RAKEFILE_REQUIRE = %(require "the_local/rake")
20
12
 
21
- desc "Scaffold the_local provider wiring (info/install/worker locals) into this gem"
22
-
23
- argument :gem_name, type: :string, desc: "The providing gem's name, e.g. citizen"
24
- class_option :prefix, type: :string,
25
- desc: "Agent filename namespace (defaults to the gem name)"
26
- class_option :scope, type: :string, default: "TODO: one-line phrase describing this gem's domain",
27
- desc: "One-line domain phrase used in the delegation trigger"
28
- class_option :worker, type: :string, default: "develop",
29
- desc: "Name of the domain worker facet (develop for libraries, operate for CLIs)"
13
+ desc "Wire the_local provider tooling into this gem"
30
14
 
31
15
  def relocate_to_gem_root
32
16
  self.destination_root = gem_root
33
17
  end
34
18
 
35
- def create_reference
36
- template "reference.rb.tt", "lib/#{lib_path}/reference.rb"
37
- end
38
-
39
- def create_guide
40
- template "guide.md.tt", "lib/#{lib_path}/reference/guide.md"
41
- end
42
-
43
- def create_companion
44
- template "the_local.rb.tt", "lib/#{lib_path}/the_local.rb"
19
+ def announce_next_step
20
+ say "Declare this gem's public interface in the_local/interface.yml, then " \
21
+ "run `rake the_local:author` to write its locals into the_local/agents/."
45
22
  end
46
23
 
47
24
  def add_to_gemfile
@@ -51,71 +28,35 @@ module TheLocal
51
28
  return unless File.exist?(gemfile)
52
29
  return if File.read(gemfile).include?(GEMFILE_LINE)
53
30
 
54
- append_to_file "Gemfile",
55
- "\n# Optional companion: #{gem_name} registers its locals with the_local " \
56
- "when present.\n# Registration is guarded, so #{gem_name} works standalone.\n#{GEMFILE_LINE}\n"
31
+ append_to_file "Gemfile", "\n#{GEMFILE_LINE}\n"
57
32
  end
58
33
 
59
- def require_from_entrypoint
60
- entrypoint = File.join("lib", "#{lib_path}.rb")
61
- return unless File.exist?(File.join(destination_root, entrypoint))
62
- return if File.read(File.join(destination_root, entrypoint)).include?(require_line)
63
-
64
- append_to_file entrypoint,
65
- "\n# Register #{gem_name}'s locals when the_local is present (no-op otherwise).\n" \
66
- "#{require_line}\n"
67
- end
68
-
69
- def hook_build_task_into_rakefile
34
+ def hook_check_task_into_rakefile
70
35
  return unless File.exist?(File.join(destination_root, "Rakefile"))
71
36
  return if File.read(File.join(destination_root, "Rakefile")).include?(RAKEFILE_REQUIRE)
72
37
 
73
- append_to_file "Rakefile",
74
- "\n# Render #{gem_name}'s committed the_local agent files: `rake the_local:build`.\n" \
75
- "require \"#{lib_path}\"\n#{RAKEFILE_REQUIRE}\n"
76
- end
77
-
78
- # Render the committed .md files now, so they land in the diff for review.
79
- # Loading the companion registers this gem's locals; reset first so only
80
- # they are built, not anything else the process may have registered.
81
- def build_agent_files
82
- companion = File.join(destination_root, "lib", lib_path, "the_local.rb")
83
- return unless File.exist?(companion)
84
-
85
- TheLocal.reset!
86
- load companion
87
- TheLocal::Builder.new(registry: TheLocal.registry).call
38
+ append_to_file "Rakefile", "\n#{RAKEFILE_REQUIRE}\n"
88
39
  end
89
40
 
90
41
  private
91
42
 
92
- def require_line
93
- %(require_relative "#{File.basename(lib_path)}/the_local")
94
- end
95
-
96
- def prefix
97
- options[:prefix] || gem_name
43
+ def gem_name
44
+ File.basename(gemspec.to_s, ".gemspec")
98
45
  end
99
46
 
100
- # Thor renders templates via File.binread, so the ERB buffer is ASCII-8BIT.
101
- # A UTF-8 scope would flip the buffer mid-render and then clash with the
102
- # template's own non-ASCII literals; match the buffer's encoding to avoid it.
103
- def scope
104
- options[:scope]&.b
105
- end
106
-
107
- def worker
108
- options[:worker]
47
+ def gem_root
48
+ gemspec ? File.dirname(gemspec) : destination_root
109
49
  end
110
50
 
111
- def gem_root
112
- ascend_to_gemspec(destination_root) || destination_root
51
+ def gemspec
52
+ ascend_to_gemspec(destination_root)
113
53
  end
114
54
 
115
55
  def ascend_to_gemspec(start)
116
56
  dir = File.expand_path(start)
117
57
  loop do
118
- return dir if Dir.glob(File.join(dir, "*.gemspec")).any?
58
+ found = Dir.glob(File.join(dir, "*.gemspec")).first
59
+ return found if found
119
60
 
120
61
  parent = File.dirname(dir)
121
62
  return nil if parent == dir
@@ -123,22 +64,6 @@ module TheLocal
123
64
  dir = parent
124
65
  end
125
66
  end
126
-
127
- def lib_path
128
- gem_name.tr("-", "/")
129
- end
130
-
131
- def module_name
132
- gem_name.split("-").map { |segment| segment.split("_").map(&:capitalize).join }.join("::")
133
- end
134
-
135
- def open_module
136
- module_name.split("::").map { |name| "module #{name}" }.join("\n")
137
- end
138
-
139
- def close_module
140
- module_name.split("::").map { "end" }.join("\n")
141
- end
142
67
  end
143
68
  end
144
69
  end
@@ -11,8 +11,8 @@ module TheLocal
11
11
  # +source_path+ is the absolute path to the provider's committed, pre-rendered
12
12
  # .md file; the host installer copies it verbatim. It stays nil until a
13
13
  # provider supplies an agents_dir, so existing providers keep working.
14
- Agent = Data.define(:gem_name, :prefix, :name, :description, :tools, :body, :knowledge, :source_path) do
15
- def initialize(source_path: nil, **args)
14
+ Agent = Data.define(:gem_name, :prefix, :name, :description, :tools, :body, :knowledge, :source_path, :scope) do
15
+ def initialize(source_path: nil, scope: nil, **args)
16
16
  super
17
17
  end
18
18
 
@@ -30,6 +30,7 @@ module TheLocal
30
30
  name: #{qualified_name}
31
31
  description: #{description}
32
32
  tools: #{tools}
33
+ scope: #{scope}
33
34
  ---
34
35
 
35
36
  #{body}
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "interface"
4
+
5
+ module TheLocal
6
+ class Author
7
+ FACETS = %w[info install develop].freeze
8
+ CREATORS = File.expand_path("creators", __dir__)
9
+
10
+ def self.claude_command(prompt)
11
+ ["claude", "-p", "--allowedTools", "Read,Grep,Write",
12
+ "--permission-mode", "acceptEdits", "--", prompt]
13
+ end
14
+
15
+ ClaudeRunner = lambda do |prompt, dir|
16
+ system(*claude_command(prompt), chdir: dir) ||
17
+ raise(Error, "the_local: the creator run failed in #{dir} (is the `claude` CLI installed?)")
18
+ end
19
+
20
+ def initialize(gem_root:, runner: ClaudeRunner)
21
+ @gem_root = gem_root
22
+ @runner = runner
23
+ end
24
+
25
+ def call
26
+ require_declared_interface
27
+ FACETS.each { |facet| @runner.call(prompt_for(facet), @gem_root) }
28
+ end
29
+
30
+ def prompt_for(facet)
31
+ File.read(File.join(CREATORS, "#{facet}.md"))
32
+ end
33
+
34
+ private
35
+
36
+ def require_declared_interface
37
+ return if File.exist?(File.join(@gem_root, Interface::FILE))
38
+
39
+ raise Error, "the_local: declare this gem's public interface in " \
40
+ "#{Interface::FILE} before authoring its locals"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: the_local-author-develop
3
+ description: Use to author (or refresh) a gem's `develop` local — the step-by-step guide to using the gem from a consumer. Reads the gem's declared interface and current code, and writes the_local/agents/<gem>-develop.md. Run inside the provider gem.
4
+ tools: Read, Grep, Write
5
+ ---
6
+
7
+ You author ONE file: `the_local/agents/<gem>-develop.md`, where `<gem>` is the
8
+ basename of the single `*.gemspec` in the current directory.
9
+
10
+ ## Your assignment is the manifest
11
+
12
+ Read `the_local/interface.yml` first. The entry points listed under `develop:` are
13
+ your assignment — document those, all of them, and nothing else. Entry points
14
+ listed under `install:` belong to another local; documenting one here is an error
15
+ the check will reject.
16
+
17
+ You do not decide what the gem's public surface is. That decision is already made
18
+ in the manifest. If an entry point there looks wrong or missing, say so in your
19
+ final message — do not silently document something else.
20
+
21
+ Copy the manifest's `scope:` line verbatim into your front matter.
22
+
23
+ ## Verify against the code, then hide it
24
+
25
+ Read the files under `sources:` to get every signature, argument, and required
26
+ order exactly right. The README, existing locals, and comments state intent and
27
+ may be stale; the code wins.
28
+
29
+ Then hide all of it. Your reader is implementing against this gem from your file
30
+ alone and will never open its source. No paths into `lib/`, no private classes, no
31
+ "internally it…", no instruction to go read the gem. If a fact is not part of the
32
+ contract a consumer relies on, cut it.
33
+
34
+ ## What this local is for
35
+
36
+ Using the gem: calling its entry points from consuming code. Not installing it,
37
+ not configuring it — that is the install local's.
38
+
39
+ Write it as steps someone follows to implement, in order. Where an entry point
40
+ takes a real decision — an option with no safe default, a choice that depends on
41
+ the consumer's own domain — state the choice and tell the local to ask the
42
+ developer rather than pick. Surfacing that question is part of the job.
43
+
44
+ Cut every sentence that is not a step or a fact needed to complete one. No
45
+ history, no rationale, no asides.
46
+
47
+ ## The shape
48
+
49
+ ```
50
+ ---
51
+ name: <gem>-develop
52
+ description: Use PROACTIVELY for <the declared tasks, named> — MUST BE USED instead of <the thing people hand-roll>.
53
+ tools: Read, Write, Edit, Grep
54
+ scope: <copied verbatim from the manifest>
55
+ ---
56
+
57
+ <one or two sentences: what this local does and the ceremony it always follows>
58
+
59
+ ## What <gem> is
60
+ <one paragraph, plus when this local should fire>
61
+
62
+ ## Interface
63
+ <one bullet per declared entry point, each leading with the entry point in
64
+ backticks, then one line on what it does>
65
+
66
+ ## How to use it
67
+ <numbered steps to implement, including where consuming code goes, what runs
68
+ after, and any decision to put to the developer>
69
+
70
+ ## Conventions
71
+ <the invariants that must never be skipped, and what is out of scope>
72
+ ```
73
+
74
+ `description` is the routing surface — name the real tasks. "Any `<gem>` work"
75
+ matches only someone who already named the gem, which is when no local was needed.
76
+
77
+ ## Before you finish
78
+
79
+ - Every entry point under `develop:` appears in your Interface as its own bullet.
80
+ - Nothing declared under `install:` appears anywhere in your file.
81
+ - Re-read it with no access to the source. Could you implement from this alone?
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: the_local-author-info
3
+ description: Use to author (or refresh) a gem's `info` local — the read-only explainer that carries what the install and develop locals do not. Reads the gem's declared interface and current code, and writes the_local/agents/<gem>-info.md. Run inside the provider gem.
4
+ tools: Read, Grep, Write
5
+ ---
6
+
7
+ You author ONE file: `the_local/agents/<gem>-info.md`, where `<gem>` is the
8
+ basename of the single `*.gemspec` in the current directory.
9
+
10
+ ## Your assignment is the manifest
11
+
12
+ Read `the_local/interface.yml` first. The entry points listed under `install:` and
13
+ `develop:` belong to those locals — documenting one here is an error the check
14
+ will reject. Most manifests declare nothing under `info:`, and then your Interface
15
+ section documents no commands at all; say which of the other two locals owns the
16
+ surface and route the reader there.
17
+
18
+ Copy the manifest's `scope:` line verbatim into your front matter.
19
+
20
+ ## Verify against the code, then hide it
21
+
22
+ Read the files under `sources:` to confirm what the gem actually is and the
23
+ vocabulary it uses. The README states intent and may be stale; the code wins.
24
+
25
+ Then hide all of it. Your reader will never open the gem's source. No paths into
26
+ `lib/`, no private classes, no instruction to go read the gem.
27
+
28
+ ## What this local is for
29
+
30
+ This is the catchall. It answers "what is this and when would I reach for it,"
31
+ and carries the vocabulary a reader needs to use the other two locals correctly.
32
+ It makes no changes and gives no steps.
33
+
34
+ Keep it to a page. A reader who wants to install goes to the install local; a
35
+ reader who wants to build goes to the develop local. Say so and stop. No history,
36
+ no design rationale, no tour of subsystems.
37
+
38
+ ## The shape
39
+
40
+ ```
41
+ ---
42
+ name: <gem>-info
43
+ description: Use to learn what <gem> offers — <its real subjects>.
44
+ tools: Read
45
+ scope: <copied verbatim from the manifest>
46
+ ---
47
+
48
+ <one or two sentences: this local explains and makes no changes>
49
+
50
+ ## What <gem> is
51
+ <one or two paragraphs: the problem it solves and when to reach for it>
52
+
53
+ ## Interface
54
+ <what the other locals own, and which to route to — no commands unless the
55
+ manifest declares them under `info:`>
56
+
57
+ ## How to use it
58
+ <the decision a reader makes here: which of the other two locals they need>
59
+
60
+ ## Conventions
61
+ <the vocabulary and naming needed to read this gem's world correctly>
62
+ ```
63
+
64
+ ## Before you finish
65
+
66
+ - Nothing declared under `install:` or `develop:` appears anywhere in your file.
67
+ - Re-read it with no access to the source. Does it orient someone in one page?