the_local 0.1.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16b120e16f6b7ca6130882854eaafb3d7d5c70ae35e2fbab17e094f4cb365056
4
- data.tar.gz: 32ef6ba59eac385d577fc5d068477ffd465e14130dadf09b47b7b9166bf07b89
3
+ metadata.gz: 16bc7ddfcc1ddab88976b10b156791a4e9a1985265d472517db9c56816d8022e
4
+ data.tar.gz: 57c2ec4821bd1165037311dad0720cbcf1a753770052e0f88ae5a879f4e8c2f7
5
5
  SHA512:
6
- metadata.gz: e4fb273d9d3646d17a47f76b6c21048c38d62c2bf40ad491d609b9873e759e9063a47bdfc0f1eb1ccbf7cd33acea2757411b862aa5ec19ad5659d0fbbf3ba69f
7
- data.tar.gz: 3fa1ff5e3989868bd713e7b21b1b253d866e9bc060b02294b1ea09f37b4056b8c0a2b4df9c009fcfa8198041ab301feab9985a1368daff5c6c561a1c072bf3b5
6
+ metadata.gz: bf6c8dfab319c69dd82da9654cee64085c0aca38253ad29bdd37794887ff8b35136c5c1f5288e4beab1688d767b4f7f53f02ca115c7d0abcc40b3d34eeac2e2f
7
+ data.tar.gz: 33a1549f7efedbcf20f3350f0c38224f03acf2865de2b7524037ba4c13889bf65b0ff75c2eff1d47a0ac6a0b4b57bdf57efcff4bd17b91a8c6a5bd1748258b8c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-07-01
4
+
5
+ - Installing no longer writes a `develop_process_rules.md` into host apps. The
6
+ `ProcessDocWriter`/`ProcessRules` machinery and its bundled doc are removed, so
7
+ `Sync`/`refresh` only install locals and the delegation trigger — a gem's
8
+ process conventions belong in its own guide, not propagated as a shared file.
9
+ - A gem no longer installs its own locals into its own repo: `refresh` excludes
10
+ the gem being run from the direct-dependency set, so a provider working on
11
+ itself doesn't copy its locals back into `.claude/agents/`.
12
+
13
+ ## [0.2.0] - 2026-06-20
14
+
15
+ - `rake the_local:build` now refuses a guide that still holds line-leading
16
+ `TODO:` placeholders or is missing a canonical section, so a provider can't
17
+ ship a local that hasn't surfaced its gem's real interface (and would send
18
+ host agents digging into source).
19
+ - Guides follow one canonical shape across every provider — **Interface**
20
+ (exact signatures), **Recipe** (copy-paste common task), **Install**,
21
+ **Conventions** — enforced at build and enumerated in the develop local's
22
+ authoring spec, so the consuming agent meets the same structure everywhere.
23
+ the_local's own guide models it with the `register` / `c.agent` signatures.
24
+ - Scaffolded facet bodies (`info` / `install` / worker) are now a standard role
25
+ that defers to the guide and forbids reading source, identical across gems —
26
+ so the consuming agent gets consistent behavior, with gem-specifics in the
27
+ guide rather than hand-written per-provider bodies.
28
+ - Install instructions and the provider generator now use the published
29
+ `gem "the_local"` instead of a `github:` git source, since the gem is on
30
+ RubyGems.
31
+
3
32
  ## [0.1.0] - 2026-06-02
4
33
 
5
34
  - Initial release.
data/PROVIDERS.md CHANGED
@@ -74,10 +74,14 @@ them after filling in the real content (below).
74
74
 
75
75
  Then **fill in the scaffold** — this is the real work the generator can't do:
76
76
 
77
- 1. Write `reference/guide.md` as the complete user-facing API. Its **Install**
78
- section must be the exact, correct steps for *this* gem (for an engine:
79
- add the gem `bundle install` install + run migrations → wire concerns /
80
- initializers), not a generic placeholder.
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.
81
85
  2. Tailor the three agent `body:` strings in `the_local.rb` to the gem.
82
86
  3. **Rebuild and commit the locals.** The scaffold built `.md` from the TODO
83
87
  placeholders, so regenerate them from your real definition and commit them:
@@ -126,8 +130,8 @@ itself a non-Rails provider built this way — mirror its own wiring
126
130
  ```
127
131
  4. `require_relative "<gem>/the_local"` from the gem's entrypoint (so your own
128
132
  `the_local:build` and standalone use load the register block — a host never
129
- needs it), and add `gem "the_local", github: "DYB-Development/the_local"` to
130
- the Gemfile (dev/test — an optional companion, not a hard dependency).
133
+ needs it), and add `gem "the_local"` to the Gemfile (dev/test — an optional
134
+ companion, not a hard dependency).
131
135
  5. Add `require "the_local/rake"` to the `Rakefile`, then build, commit, and
132
136
  **ship** the rendered locals — `rake the_local:build && git add
133
137
  lib/<gem>/the_local/agents`, and make sure they're in the gemspec's `files`.
@@ -15,7 +15,7 @@ module TheLocal
15
15
  class ProviderGenerator < Rails::Generators::Base
16
16
  source_root File.expand_path("templates", __dir__)
17
17
 
18
- GEMFILE_LINE = %(gem "the_local", github: "DYB-Development/the_local")
18
+ GEMFILE_LINE = %(gem "the_local")
19
19
  RAKEFILE_REQUIRE = %(require "the_local/rake")
20
20
 
21
21
  desc "Scaffold the_local provider wiring (info/install/worker locals) into this gem"
@@ -3,13 +3,29 @@
3
3
  > **DO NOT** explore the <%= gem_name %> gem source code. This reference is the
4
4
  > complete user-facing API, embedded verbatim into every <%= gem_name %> local so
5
5
  > their guidance never drifts. Keep it the single source of truth.
6
+ >
7
+ > The bar for everything below: a host agent must be able to do <%= gem_name %>
8
+ > work from this guide alone — exact signatures and a complete, copy-paste
9
+ > recipe — without ever opening <%= gem_name %>'s source. If a signature or step
10
+ > is missing, the agent will dig into source, which is the cost the_local exists
11
+ > to remove. Replace every TODO; `rake the_local:build` refuses a guide that
12
+ > still has one.
6
13
 
7
14
  TODO: One paragraph — what <%= gem_name %> is and the problem it solves.
8
15
 
9
- ### What it offers
16
+ ### Interface
10
17
 
11
- TODO: The public API — the methods/classes/DSL a host actually calls, with tiny
12
- examples. This is what the `info` and `<%= worker %>` locals answer from.
18
+ TODO: The literal public API a host calls every method/class/DSL entry with
19
+ its exact signature: arguments, which are required vs optional, and what it
20
+ returns. Give the signatures themselves, each with a one-line example, not prose
21
+ about them. This is what the `info` and `<%= worker %>` locals answer from, so an
22
+ agent never has to read source to recall how a call is shaped.
23
+
24
+ ### Recipe
25
+
26
+ TODO: A complete, copy-paste implementation of the most common <%= gem_name %>
27
+ task — the whole thing, not a fragment — so the `<%= worker %>` local reproduces
28
+ it identically every time instead of re-deriving it from source.
13
29
 
14
30
  ### Install
15
31
 
@@ -19,7 +35,7 @@ actually installed (e.g. for a Rails engine: add the gem, `bundle install`,
19
35
  install + run migrations, wire any concerns/initializers). Don't leave it
20
36
  generic.
21
37
 
22
- ### <%= module_name %> conventions
38
+ ### Conventions
23
39
 
24
40
  TODO: The conventions the `<%= worker %>` local must enforce when doing
25
41
  <%= gem_name %> work, so usage stays consistent across the host.
@@ -15,23 +15,26 @@ require_relative "reference"
15
15
  c.agent "info",
16
16
  description: "Use to learn what <%= gem_name %> offers — its API and conventions.",
17
17
  tools: "Read",
18
- body: "You explain what <%= gem_name %> does and how to use it, answering from the " \
19
- "reference. You make no changes. TODO: tailor this body to <%= gem_name %>.",
18
+ body: "You explain what <%= gem_name %> does and how to use it, answering only from " \
19
+ "your reference. You make no changes, and you never read <%= gem_name %>'s " \
20
+ "source — the reference is the complete interface.",
20
21
  knowledge: <%= module_name %>::Reference.content
21
22
 
22
23
  c.agent "install",
23
24
  description: "Use to add <%= gem_name %> to a project and set it up correctly.",
24
25
  tools: "Bash, Read, Edit",
25
- body: "You add <%= gem_name %> to the project and complete its setup, following the " \
26
- "reference's install section exactly. TODO: tailor this body to <%= gem_name %>.",
26
+ body: "You add <%= gem_name %> to the project and complete its setup by following your " \
27
+ "reference's Install section exactly, step by step. You do not invent steps it " \
28
+ "does not list, and you never read <%= gem_name %>'s source.",
27
29
  knowledge: <%= module_name %>::Reference.content
28
30
 
29
31
  c.agent "<%= worker %>",
30
32
  description: "Use PROACTIVELY for any <%= gem_name %> work. MUST BE USED instead of " \
31
33
  "hand-rolling it. TODO: name the concrete tasks this local owns.",
32
34
  tools: "Read, Write, Edit, Grep",
33
- body: "You do <%= gem_name %> work following the reference's conventions. TODO: tailor " \
34
- "this body to <%= gem_name %>.",
35
+ body: "You do <%= gem_name %> work by following the Interface, Recipe, and Conventions " \
36
+ "in your reference exactly, so usage stays consistent across the host. You " \
37
+ "implement from the reference, never from <%= gem_name %>'s source.",
35
38
  knowledge: <%= module_name %>::Reference.content
36
39
  end
37
40
  end
@@ -9,11 +9,22 @@ module TheLocal
9
9
  # the_local:build rake task a provider runs. Agents that declared no agents_dir
10
10
  # (and so have no source_path) are skipped: there is nowhere to write them.
11
11
  class Builder
12
- def initialize(registry:)
12
+ # A real placeholder is a line-leading "TODO:"; an inline mention of the
13
+ # marker (a guide documenting the convention) is left alone.
14
+ PLACEHOLDER = /^\s*TODO:/
15
+
16
+ # The canonical sections every guide must carry, so a consuming agent meets
17
+ # the same shape in every gem's local. Matched as header prefixes.
18
+ REQUIRED_SECTIONS = ["### Interface", "### Recipe", "### Install", "### Conventions"].freeze
19
+
20
+ def initialize(registry:, validate: false)
13
21
  @registry = registry
22
+ @validate = validate
14
23
  end
15
24
 
16
25
  def call
26
+ validate! if @validate
27
+
17
28
  buildable_agents.map do |agent|
18
29
  FileUtils.mkdir_p(File.dirname(agent.source_path))
19
30
  File.write(agent.source_path, agent.to_markdown)
@@ -23,6 +34,22 @@ module TheLocal
23
34
 
24
35
  private
25
36
 
37
+ def validate!
38
+ problems = buildable_agents.flat_map { |agent| problems_for(agent) }
39
+ return if problems.empty?
40
+
41
+ raise Error, "the_local: incomplete guide(s):\n- #{problems.join("\n- ")}"
42
+ end
43
+
44
+ def problems_for(agent)
45
+ markdown = agent.to_markdown
46
+ problems = []
47
+ problems << "#{agent.qualified_name}: TODO: placeholders remain" if markdown.match?(PLACEHOLDER)
48
+ missing = REQUIRED_SECTIONS.reject { |section| markdown.include?(section) }
49
+ problems << "#{agent.qualified_name}: missing #{missing.join(", ")}" if missing.any?
50
+ problems
51
+ end
52
+
26
53
  def buildable_agents
27
54
  @registry.agents.select(&:source_path)
28
55
  end
@@ -11,7 +11,7 @@ require "the_local/builder"
11
11
  namespace :the_local do
12
12
  desc "Render this provider's committed agent files from its registered definitions"
13
13
  task :build do
14
- written = TheLocal::Builder.new(registry: TheLocal.registry).call
14
+ written = TheLocal::Builder.new(registry: TheLocal.registry, validate: true).call
15
15
  puts "the_local: built #{written.length} agent file(s)"
16
16
  end
17
17
 
@@ -37,9 +37,7 @@ the aggregated set from every directly-depended provider into a consuming app's
37
37
 
38
38
  ### Install (in any gem or app)
39
39
 
40
- 1. Add the gem to the host's `Gemfile` (until it is on RubyGems, use a git
41
- source: `gem "the_local", github: "DYB-Development/the_local"`), then
42
- `bundle install`.
40
+ 1. Add `gem "the_local"` to the host's `Gemfile`, then `bundle install`.
43
41
  2. Run `bundle exec the_local install`. This syncs every direct provider's
44
42
  committed locals into `.claude/agents/` and writes the delegation trigger
45
43
  into `CLAUDE.md`/`AGENTS.md`. It needs no Rails — a plain gem installs the
@@ -60,10 +58,18 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
60
58
  registers the standard interface; hooks `the_local:build` into the `Rakefile`;
61
59
  requires the companion from the gem entrypoint; and builds the committed
62
60
  `.md` for review.
63
- 2. Write `guide.md` in this formatit is the single source of truth and is
64
- embedded verbatim into every local. Document *your own* gem only: what it
65
- does, how to install it, the conventions to enforce. Name companion gems but
66
- do not explain their internals.
61
+ 2. Write `guide.md` to the canonical shape — the same sections in every
62
+ provider, so the consuming agent meets one structure everywhere and
63
+ `rake the_local:build` rejects a guide missing one:
64
+ - **Interface** every public call's *exact signature* (arguments, required
65
+ vs optional, return) as real signatures in a code block, not prose.
66
+ - **Recipe** — a complete copy-paste implementation of the common task.
67
+ - **Install** — the exact setup steps for *this* gem.
68
+ - **Conventions** — what the worker enforces to keep usage consistent.
69
+
70
+ The bar: a host agent does your gem's work from the guide alone, without ever
71
+ opening your source. Document your own gem only; name companion gems but do
72
+ not explain their internals.
67
73
  3. Tailor the register block bodies and `scope` to your gem; the standard
68
74
  interface is `info` (read-only explainer), `install` (sets the gem up in a
69
75
  host), and a domain worker (`develop` for libraries, `operate` for CLIs).
@@ -75,7 +81,26 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
75
81
  nothing, and if they are, you contribute everything. A drift test asserting
76
82
  each committed file equals its `agent.to_markdown` keeps the artifact honest.
77
83
 
78
- ### TheLocal.register
84
+ ### Interface
85
+
86
+ The complete public surface — every entry point with its exact signature, so a
87
+ local answers from here instead of reading source.
88
+
89
+ **Register locals (provider Ruby, behind a soft `require "the_local"` guard):**
90
+
91
+ ```ruby
92
+ TheLocal.register(gem_name, prefix: gem_name, scope: nil, agents_dir: nil) { |c| … }
93
+ # Registers a provider and yields a Collector. gem_name is positional and
94
+ # filters to a host's direct dependencies; prefix is the filename namespace
95
+ # (defaults to gem_name); scope is the one-line delegation phrase; agents_dir
96
+ # is the absolute path to the committed .md files (each agent records its
97
+ # source_path there for the installer to copy verbatim).
98
+
99
+ c.agent(name, description:, tools:, body:, knowledge: nil)
100
+ # Declares one local. name is positional; description, tools, body are
101
+ # required; knowledge — a String or Array of Strings, usually
102
+ # MyGem::Reference.content — is optional and appended below the body.
103
+ ```
79
104
 
80
105
  ```ruby
81
106
  TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
@@ -88,11 +113,62 @@ TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
88
113
  end
89
114
  ```
90
115
 
91
- - `gem_name` (first arg) filters to a host's direct dependencies.
92
- - `prefix` is the agent filename namespace; defaults to the gem name.
93
- - `scope` is a one-line domain phrase used to generate the delegation trigger.
94
- - `agents_dir` is the absolute path to the committed `.md` files; each agent
95
- records its `source_path` there so the installer can copy it verbatim.
116
+ **Build (provider Rakefile, after `require "the_local/rake"`):**
117
+
118
+ - `rake the_local:build` renders each registered agent to its committed
119
+ `lib/<gem>/the_local/agents/<prefix>-<name>.md`. Refuses to render a guide that
120
+ still holds a `TODO:` placeholder or is missing a canonical section.
121
+ - `rake the_local:install` — installs/refreshes this project's own locals.
122
+
123
+ **Host (consuming app or gem):**
124
+
125
+ - `bundle exec the_local install` — CLI; syncs direct providers' locals into
126
+ `.claude/agents/` and writes the delegation trigger. No Rails required.
127
+ - `bin/rails g the_local:install` and `rake the_local:refresh` — Rails equivalents.
128
+ - `bin/rails g the_local:provider <gem_name> [--prefix P] [--scope "…"] [--worker develop|operate]`
129
+ — scaffolds the provider wiring (Reference loader, guide, companion).
130
+
131
+ ### Recipe
132
+
133
+ Turn a gem into a provider — the complete companion, copy-paste and rename:
134
+
135
+ ```ruby
136
+ # lib/my_gem/the_local.rb
137
+ require_relative "reference"
138
+
139
+ module MyGem
140
+ module Companion
141
+ def self.register!
142
+ TheLocal.register("my_gem", scope: "one-line domain phrase",
143
+ agents_dir: File.expand_path("the_local/agents", __dir__)) do |c|
144
+ c.agent "info",
145
+ description: "Use to learn what my_gem offers.",
146
+ tools: "Read",
147
+ body: "You explain what my_gem does, answering only from your reference. " \
148
+ "You make no changes and never read my_gem's source.",
149
+ knowledge: MyGem::Reference.content
150
+
151
+ c.agent "develop",
152
+ description: "Use PROACTIVELY for any my_gem work.",
153
+ tools: "Read, Write, Edit, Grep",
154
+ body: "You do my_gem work by following your reference's Interface, Recipe, " \
155
+ "and Conventions exactly. You implement from the reference, never source.",
156
+ knowledge: MyGem::Reference.content
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ begin
163
+ require "the_local"
164
+ MyGem::Companion.register!
165
+ rescue LoadError
166
+ # the_local not installed — my_gem works standalone.
167
+ end
168
+ ```
169
+
170
+ Then write `reference/guide.md` to the canonical shape, `rake the_local:build`,
171
+ and commit `lib/my_gem/the_local/agents/*.md`.
96
172
 
97
173
  ### Conventions
98
174
 
@@ -16,7 +16,7 @@ module TheLocal
16
16
  Sync.new(
17
17
  registry: TheLocal.registry,
18
18
  destination: destination,
19
- direct_dependencies: definition.dependencies.map(&:name),
19
+ direct_dependencies: definition.dependencies.map(&:name) - [own_gem_name(definition, destination)],
20
20
  bundled_gems: definition.specs.map(&:name)
21
21
  ).call
22
22
  end
@@ -24,5 +24,10 @@ module TheLocal
24
24
  def self.specs_from(definition)
25
25
  definition.specs.map { |spec| { name: spec.name, path: spec.full_gem_path } }
26
26
  end
27
+
28
+ def self.own_gem_name(definition, destination)
29
+ here = File.expand_path(destination)
30
+ definition.specs.find { |spec| File.expand_path(spec.full_gem_path) == here }&.name
31
+ end
27
32
  end
28
33
  end
@@ -21,7 +21,6 @@ module TheLocal
21
21
  )
22
22
  Installer.new(registry: @registry, destination: @destination, allowed_gems: allowed).call
23
23
  TriggerWriter.new(registry: @registry, destination: @destination, allowed_gems: allowed).call
24
- ProcessDocWriter.new(destination: @destination).call
25
24
  allowed
26
25
  end
27
26
  end
@@ -45,9 +45,7 @@ the aggregated set from every directly-depended provider into a consuming app's
45
45
 
46
46
  ### Install (in any gem or app)
47
47
 
48
- 1. Add the gem to the host's `Gemfile` (until it is on RubyGems, use a git
49
- source: `gem "the_local", github: "DYB-Development/the_local"`), then
50
- `bundle install`.
48
+ 1. Add `gem "the_local"` to the host's `Gemfile`, then `bundle install`.
51
49
  2. Run `bundle exec the_local install`. This syncs every direct provider's
52
50
  committed locals into `.claude/agents/` and writes the delegation trigger
53
51
  into `CLAUDE.md`/`AGENTS.md`. It needs no Rails — a plain gem installs the
@@ -68,10 +66,18 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
68
66
  registers the standard interface; hooks `the_local:build` into the `Rakefile`;
69
67
  requires the companion from the gem entrypoint; and builds the committed
70
68
  `.md` for review.
71
- 2. Write `guide.md` in this formatit is the single source of truth and is
72
- embedded verbatim into every local. Document *your own* gem only: what it
73
- does, how to install it, the conventions to enforce. Name companion gems but
74
- do not explain their internals.
69
+ 2. Write `guide.md` to the canonical shape — the same sections in every
70
+ provider, so the consuming agent meets one structure everywhere and
71
+ `rake the_local:build` rejects a guide missing one:
72
+ - **Interface** every public call's *exact signature* (arguments, required
73
+ vs optional, return) as real signatures in a code block, not prose.
74
+ - **Recipe** — a complete copy-paste implementation of the common task.
75
+ - **Install** — the exact setup steps for *this* gem.
76
+ - **Conventions** — what the worker enforces to keep usage consistent.
77
+
78
+ The bar: a host agent does your gem's work from the guide alone, without ever
79
+ opening your source. Document your own gem only; name companion gems but do
80
+ not explain their internals.
75
81
  3. Tailor the register block bodies and `scope` to your gem; the standard
76
82
  interface is `info` (read-only explainer), `install` (sets the gem up in a
77
83
  host), and a domain worker (`develop` for libraries, `operate` for CLIs).
@@ -83,7 +89,26 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
83
89
  nothing, and if they are, you contribute everything. A drift test asserting
84
90
  each committed file equals its `agent.to_markdown` keeps the artifact honest.
85
91
 
86
- ### TheLocal.register
92
+ ### Interface
93
+
94
+ The complete public surface — every entry point with its exact signature, so a
95
+ local answers from here instead of reading source.
96
+
97
+ **Register locals (provider Ruby, behind a soft `require "the_local"` guard):**
98
+
99
+ ```ruby
100
+ TheLocal.register(gem_name, prefix: gem_name, scope: nil, agents_dir: nil) { |c| … }
101
+ # Registers a provider and yields a Collector. gem_name is positional and
102
+ # filters to a host's direct dependencies; prefix is the filename namespace
103
+ # (defaults to gem_name); scope is the one-line delegation phrase; agents_dir
104
+ # is the absolute path to the committed .md files (each agent records its
105
+ # source_path there for the installer to copy verbatim).
106
+
107
+ c.agent(name, description:, tools:, body:, knowledge: nil)
108
+ # Declares one local. name is positional; description, tools, body are
109
+ # required; knowledge — a String or Array of Strings, usually
110
+ # MyGem::Reference.content — is optional and appended below the body.
111
+ ```
87
112
 
88
113
  ```ruby
89
114
  TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
@@ -96,11 +121,62 @@ TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
96
121
  end
97
122
  ```
98
123
 
99
- - `gem_name` (first arg) filters to a host's direct dependencies.
100
- - `prefix` is the agent filename namespace; defaults to the gem name.
101
- - `scope` is a one-line domain phrase used to generate the delegation trigger.
102
- - `agents_dir` is the absolute path to the committed `.md` files; each agent
103
- records its `source_path` there so the installer can copy it verbatim.
124
+ **Build (provider Rakefile, after `require "the_local/rake"`):**
125
+
126
+ - `rake the_local:build` renders each registered agent to its committed
127
+ `lib/<gem>/the_local/agents/<prefix>-<name>.md`. Refuses to render a guide that
128
+ still holds a `TODO:` placeholder or is missing a canonical section.
129
+ - `rake the_local:install` — installs/refreshes this project's own locals.
130
+
131
+ **Host (consuming app or gem):**
132
+
133
+ - `bundle exec the_local install` — CLI; syncs direct providers' locals into
134
+ `.claude/agents/` and writes the delegation trigger. No Rails required.
135
+ - `bin/rails g the_local:install` and `rake the_local:refresh` — Rails equivalents.
136
+ - `bin/rails g the_local:provider <gem_name> [--prefix P] [--scope "…"] [--worker develop|operate]`
137
+ — scaffolds the provider wiring (Reference loader, guide, companion).
138
+
139
+ ### Recipe
140
+
141
+ Turn a gem into a provider — the complete companion, copy-paste and rename:
142
+
143
+ ```ruby
144
+ # lib/my_gem/the_local.rb
145
+ require_relative "reference"
146
+
147
+ module MyGem
148
+ module Companion
149
+ def self.register!
150
+ TheLocal.register("my_gem", scope: "one-line domain phrase",
151
+ agents_dir: File.expand_path("the_local/agents", __dir__)) do |c|
152
+ c.agent "info",
153
+ description: "Use to learn what my_gem offers.",
154
+ tools: "Read",
155
+ body: "You explain what my_gem does, answering only from your reference. " \
156
+ "You make no changes and never read my_gem's source.",
157
+ knowledge: MyGem::Reference.content
158
+
159
+ c.agent "develop",
160
+ description: "Use PROACTIVELY for any my_gem work.",
161
+ tools: "Read, Write, Edit, Grep",
162
+ body: "You do my_gem work by following your reference's Interface, Recipe, " \
163
+ "and Conventions exactly. You implement from the reference, never source.",
164
+ knowledge: MyGem::Reference.content
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ begin
171
+ require "the_local"
172
+ MyGem::Companion.register!
173
+ rescue LoadError
174
+ # the_local not installed — my_gem works standalone.
175
+ end
176
+ ```
177
+
178
+ Then write `reference/guide.md` to the canonical shape, `rake the_local:build`,
179
+ and commit `lib/my_gem/the_local/agents/*.md`.
104
180
 
105
181
  ### Conventions
106
182
 
@@ -45,9 +45,7 @@ the aggregated set from every directly-depended provider into a consuming app's
45
45
 
46
46
  ### Install (in any gem or app)
47
47
 
48
- 1. Add the gem to the host's `Gemfile` (until it is on RubyGems, use a git
49
- source: `gem "the_local", github: "DYB-Development/the_local"`), then
50
- `bundle install`.
48
+ 1. Add `gem "the_local"` to the host's `Gemfile`, then `bundle install`.
51
49
  2. Run `bundle exec the_local install`. This syncs every direct provider's
52
50
  committed locals into `.claude/agents/` and writes the delegation trigger
53
51
  into `CLAUDE.md`/`AGENTS.md`. It needs no Rails — a plain gem installs the
@@ -68,10 +66,18 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
68
66
  registers the standard interface; hooks `the_local:build` into the `Rakefile`;
69
67
  requires the companion from the gem entrypoint; and builds the committed
70
68
  `.md` for review.
71
- 2. Write `guide.md` in this formatit is the single source of truth and is
72
- embedded verbatim into every local. Document *your own* gem only: what it
73
- does, how to install it, the conventions to enforce. Name companion gems but
74
- do not explain their internals.
69
+ 2. Write `guide.md` to the canonical shape — the same sections in every
70
+ provider, so the consuming agent meets one structure everywhere and
71
+ `rake the_local:build` rejects a guide missing one:
72
+ - **Interface** every public call's *exact signature* (arguments, required
73
+ vs optional, return) as real signatures in a code block, not prose.
74
+ - **Recipe** — a complete copy-paste implementation of the common task.
75
+ - **Install** — the exact setup steps for *this* gem.
76
+ - **Conventions** — what the worker enforces to keep usage consistent.
77
+
78
+ The bar: a host agent does your gem's work from the guide alone, without ever
79
+ opening your source. Document your own gem only; name companion gems but do
80
+ not explain their internals.
75
81
  3. Tailor the register block bodies and `scope` to your gem; the standard
76
82
  interface is `info` (read-only explainer), `install` (sets the gem up in a
77
83
  host), and a domain worker (`develop` for libraries, `operate` for CLIs).
@@ -83,7 +89,26 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
83
89
  nothing, and if they are, you contribute everything. A drift test asserting
84
90
  each committed file equals its `agent.to_markdown` keeps the artifact honest.
85
91
 
86
- ### TheLocal.register
92
+ ### Interface
93
+
94
+ The complete public surface — every entry point with its exact signature, so a
95
+ local answers from here instead of reading source.
96
+
97
+ **Register locals (provider Ruby, behind a soft `require "the_local"` guard):**
98
+
99
+ ```ruby
100
+ TheLocal.register(gem_name, prefix: gem_name, scope: nil, agents_dir: nil) { |c| … }
101
+ # Registers a provider and yields a Collector. gem_name is positional and
102
+ # filters to a host's direct dependencies; prefix is the filename namespace
103
+ # (defaults to gem_name); scope is the one-line delegation phrase; agents_dir
104
+ # is the absolute path to the committed .md files (each agent records its
105
+ # source_path there for the installer to copy verbatim).
106
+
107
+ c.agent(name, description:, tools:, body:, knowledge: nil)
108
+ # Declares one local. name is positional; description, tools, body are
109
+ # required; knowledge — a String or Array of Strings, usually
110
+ # MyGem::Reference.content — is optional and appended below the body.
111
+ ```
87
112
 
88
113
  ```ruby
89
114
  TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
@@ -96,11 +121,62 @@ TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
96
121
  end
97
122
  ```
98
123
 
99
- - `gem_name` (first arg) filters to a host's direct dependencies.
100
- - `prefix` is the agent filename namespace; defaults to the gem name.
101
- - `scope` is a one-line domain phrase used to generate the delegation trigger.
102
- - `agents_dir` is the absolute path to the committed `.md` files; each agent
103
- records its `source_path` there so the installer can copy it verbatim.
124
+ **Build (provider Rakefile, after `require "the_local/rake"`):**
125
+
126
+ - `rake the_local:build` renders each registered agent to its committed
127
+ `lib/<gem>/the_local/agents/<prefix>-<name>.md`. Refuses to render a guide that
128
+ still holds a `TODO:` placeholder or is missing a canonical section.
129
+ - `rake the_local:install` — installs/refreshes this project's own locals.
130
+
131
+ **Host (consuming app or gem):**
132
+
133
+ - `bundle exec the_local install` — CLI; syncs direct providers' locals into
134
+ `.claude/agents/` and writes the delegation trigger. No Rails required.
135
+ - `bin/rails g the_local:install` and `rake the_local:refresh` — Rails equivalents.
136
+ - `bin/rails g the_local:provider <gem_name> [--prefix P] [--scope "…"] [--worker develop|operate]`
137
+ — scaffolds the provider wiring (Reference loader, guide, companion).
138
+
139
+ ### Recipe
140
+
141
+ Turn a gem into a provider — the complete companion, copy-paste and rename:
142
+
143
+ ```ruby
144
+ # lib/my_gem/the_local.rb
145
+ require_relative "reference"
146
+
147
+ module MyGem
148
+ module Companion
149
+ def self.register!
150
+ TheLocal.register("my_gem", scope: "one-line domain phrase",
151
+ agents_dir: File.expand_path("the_local/agents", __dir__)) do |c|
152
+ c.agent "info",
153
+ description: "Use to learn what my_gem offers.",
154
+ tools: "Read",
155
+ body: "You explain what my_gem does, answering only from your reference. " \
156
+ "You make no changes and never read my_gem's source.",
157
+ knowledge: MyGem::Reference.content
158
+
159
+ c.agent "develop",
160
+ description: "Use PROACTIVELY for any my_gem work.",
161
+ tools: "Read, Write, Edit, Grep",
162
+ body: "You do my_gem work by following your reference's Interface, Recipe, " \
163
+ "and Conventions exactly. You implement from the reference, never source.",
164
+ knowledge: MyGem::Reference.content
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ begin
171
+ require "the_local"
172
+ MyGem::Companion.register!
173
+ rescue LoadError
174
+ # the_local not installed — my_gem works standalone.
175
+ end
176
+ ```
177
+
178
+ Then write `reference/guide.md` to the canonical shape, `rake the_local:build`,
179
+ and commit `lib/my_gem/the_local/agents/*.md`.
104
180
 
105
181
  ### Conventions
106
182
 
@@ -45,9 +45,7 @@ the aggregated set from every directly-depended provider into a consuming app's
45
45
 
46
46
  ### Install (in any gem or app)
47
47
 
48
- 1. Add the gem to the host's `Gemfile` (until it is on RubyGems, use a git
49
- source: `gem "the_local", github: "DYB-Development/the_local"`), then
50
- `bundle install`.
48
+ 1. Add `gem "the_local"` to the host's `Gemfile`, then `bundle install`.
51
49
  2. Run `bundle exec the_local install`. This syncs every direct provider's
52
50
  committed locals into `.claude/agents/` and writes the delegation trigger
53
51
  into `CLAUDE.md`/`AGENTS.md`. It needs no Rails — a plain gem installs the
@@ -68,10 +66,18 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
68
66
  registers the standard interface; hooks `the_local:build` into the `Rakefile`;
69
67
  requires the companion from the gem entrypoint; and builds the committed
70
68
  `.md` for review.
71
- 2. Write `guide.md` in this formatit is the single source of truth and is
72
- embedded verbatim into every local. Document *your own* gem only: what it
73
- does, how to install it, the conventions to enforce. Name companion gems but
74
- do not explain their internals.
69
+ 2. Write `guide.md` to the canonical shape — the same sections in every
70
+ provider, so the consuming agent meets one structure everywhere and
71
+ `rake the_local:build` rejects a guide missing one:
72
+ - **Interface** every public call's *exact signature* (arguments, required
73
+ vs optional, return) as real signatures in a code block, not prose.
74
+ - **Recipe** — a complete copy-paste implementation of the common task.
75
+ - **Install** — the exact setup steps for *this* gem.
76
+ - **Conventions** — what the worker enforces to keep usage consistent.
77
+
78
+ The bar: a host agent does your gem's work from the guide alone, without ever
79
+ opening your source. Document your own gem only; name companion gems but do
80
+ not explain their internals.
75
81
  3. Tailor the register block bodies and `scope` to your gem; the standard
76
82
  interface is `info` (read-only explainer), `install` (sets the gem up in a
77
83
  host), and a domain worker (`develop` for libraries, `operate` for CLIs).
@@ -83,7 +89,26 @@ its Rakefile also gets `rake the_local:install`. All three share one engine.
83
89
  nothing, and if they are, you contribute everything. A drift test asserting
84
90
  each committed file equals its `agent.to_markdown` keeps the artifact honest.
85
91
 
86
- ### TheLocal.register
92
+ ### Interface
93
+
94
+ The complete public surface — every entry point with its exact signature, so a
95
+ local answers from here instead of reading source.
96
+
97
+ **Register locals (provider Ruby, behind a soft `require "the_local"` guard):**
98
+
99
+ ```ruby
100
+ TheLocal.register(gem_name, prefix: gem_name, scope: nil, agents_dir: nil) { |c| … }
101
+ # Registers a provider and yields a Collector. gem_name is positional and
102
+ # filters to a host's direct dependencies; prefix is the filename namespace
103
+ # (defaults to gem_name); scope is the one-line delegation phrase; agents_dir
104
+ # is the absolute path to the committed .md files (each agent records its
105
+ # source_path there for the installer to copy verbatim).
106
+
107
+ c.agent(name, description:, tools:, body:, knowledge: nil)
108
+ # Declares one local. name is positional; description, tools, body are
109
+ # required; knowledge — a String or Array of Strings, usually
110
+ # MyGem::Reference.content — is optional and appended below the body.
111
+ ```
87
112
 
88
113
  ```ruby
89
114
  TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
@@ -96,11 +121,62 @@ TheLocal.register("my_gem", prefix: "my_gem", scope: "one-line domain phrase",
96
121
  end
97
122
  ```
98
123
 
99
- - `gem_name` (first arg) filters to a host's direct dependencies.
100
- - `prefix` is the agent filename namespace; defaults to the gem name.
101
- - `scope` is a one-line domain phrase used to generate the delegation trigger.
102
- - `agents_dir` is the absolute path to the committed `.md` files; each agent
103
- records its `source_path` there so the installer can copy it verbatim.
124
+ **Build (provider Rakefile, after `require "the_local/rake"`):**
125
+
126
+ - `rake the_local:build` renders each registered agent to its committed
127
+ `lib/<gem>/the_local/agents/<prefix>-<name>.md`. Refuses to render a guide that
128
+ still holds a `TODO:` placeholder or is missing a canonical section.
129
+ - `rake the_local:install` — installs/refreshes this project's own locals.
130
+
131
+ **Host (consuming app or gem):**
132
+
133
+ - `bundle exec the_local install` — CLI; syncs direct providers' locals into
134
+ `.claude/agents/` and writes the delegation trigger. No Rails required.
135
+ - `bin/rails g the_local:install` and `rake the_local:refresh` — Rails equivalents.
136
+ - `bin/rails g the_local:provider <gem_name> [--prefix P] [--scope "…"] [--worker develop|operate]`
137
+ — scaffolds the provider wiring (Reference loader, guide, companion).
138
+
139
+ ### Recipe
140
+
141
+ Turn a gem into a provider — the complete companion, copy-paste and rename:
142
+
143
+ ```ruby
144
+ # lib/my_gem/the_local.rb
145
+ require_relative "reference"
146
+
147
+ module MyGem
148
+ module Companion
149
+ def self.register!
150
+ TheLocal.register("my_gem", scope: "one-line domain phrase",
151
+ agents_dir: File.expand_path("the_local/agents", __dir__)) do |c|
152
+ c.agent "info",
153
+ description: "Use to learn what my_gem offers.",
154
+ tools: "Read",
155
+ body: "You explain what my_gem does, answering only from your reference. " \
156
+ "You make no changes and never read my_gem's source.",
157
+ knowledge: MyGem::Reference.content
158
+
159
+ c.agent "develop",
160
+ description: "Use PROACTIVELY for any my_gem work.",
161
+ tools: "Read, Write, Edit, Grep",
162
+ body: "You do my_gem work by following your reference's Interface, Recipe, " \
163
+ "and Conventions exactly. You implement from the reference, never source.",
164
+ knowledge: MyGem::Reference.content
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ begin
171
+ require "the_local"
172
+ MyGem::Companion.register!
173
+ rescue LoadError
174
+ # the_local not installed — my_gem works standalone.
175
+ end
176
+ ```
177
+
178
+ Then write `reference/guide.md` to the canonical shape, `rake the_local:build`,
179
+ and commit `lib/my_gem/the_local/agents/*.md`.
104
180
 
105
181
  ### Conventions
106
182
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TheLocal
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/the_local.rb CHANGED
@@ -5,7 +5,6 @@ require_relative "the_local/agent"
5
5
  require_relative "the_local/registry"
6
6
  require_relative "the_local/installer"
7
7
  require_relative "the_local/trigger_writer"
8
- require_relative "the_local/process_doc_writer"
9
8
  require_relative "the_local/scope"
10
9
  require_relative "the_local/sync"
11
10
  require_relative "the_local/refresh"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: the_local
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tylercschneider
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-11 00:00:00.000000000 Z
11
+ date: 2026-07-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: the_local lets any gem or app declare Claude Code subagents ("locals")
14
14
  that know its conventions, and installs the aggregated set from every installed
@@ -38,9 +38,6 @@ files:
38
38
  - lib/the_local/cli.rb
39
39
  - lib/the_local/disk_providers.rb
40
40
  - lib/the_local/installer.rb
41
- - lib/the_local/process_doc_writer.rb
42
- - lib/the_local/process_rules.rb
43
- - lib/the_local/process_rules/develop_process_rules.md
44
41
  - lib/the_local/railtie.rb
45
42
  - lib/the_local/rake.rb
46
43
  - lib/the_local/reference.rb
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "process_rules"
4
-
5
- module TheLocal
6
- # Writes the canonical develop-process rules into a host's CLAUDE.md as a
7
- # managed block, read at the start of every session so the host agent always
8
- # follows one source of truth. Re-propagated on every install/refresh. Uses
9
- # its own markers so it coexists with the delegation trigger in the same file.
10
- class ProcessDocWriter
11
- BEGIN_MARKER = "<!-- the_local:process:begin -->"
12
- END_MARKER = "<!-- the_local:process:end -->"
13
- RULES_FILENAME = "develop_process_rules.md"
14
-
15
- def initialize(destination:, filename: "CLAUDE.md")
16
- @destination = destination
17
- @filename = filename
18
- end
19
-
20
- def call
21
- File.write(File.join(@destination, RULES_FILENAME), "#{ProcessRules.content}\n")
22
- path = File.join(@destination, @filename)
23
- existing = File.exist?(path) ? File.read(path) : ""
24
- File.write(path, "#{merge(existing)}\n")
25
- end
26
-
27
- def block
28
- <<~MARKDOWN.chomp
29
- #{BEGIN_MARKER}
30
- Read and follow this develop process for all work in this project. It is
31
- also written verbatim to `#{RULES_FILENAME}` — reference that file directly.
32
-
33
- #{ProcessRules.content}
34
- #{END_MARKER}
35
- MARKDOWN
36
- end
37
-
38
- private
39
-
40
- def merge(existing)
41
- section = /#{Regexp.escape(BEGIN_MARKER)}.*?#{Regexp.escape(END_MARKER)}/m
42
- return existing.sub(section, block) if existing.match?(section)
43
- return block if existing.strip.empty?
44
-
45
- "#{existing.chomp}\n\n#{block}"
46
- end
47
- end
48
- end
@@ -1,97 +0,0 @@
1
- # Develop Process
2
-
3
- The standard process for writing code across all projects. Default to these rules
4
- unless a project explicitly overrides them.
5
-
6
- ---
7
-
8
- ## Diverging from this process
9
-
10
- Read this process before starting work and follow it — it is the default for
11
- every session. If a task genuinely calls for breaking one of these rules, do not
12
- silently deviate: **PAUSE and ask for a one-time exception**, naming the rule and
13
- why it should be set aside here. An exception is granted for that instance only —
14
- it needs no doc or notes update — and then you continue. Do not treat a granted
15
- exception as a standing change to the process.
16
-
17
- ---
18
-
19
- ## Test-Driven Development
20
-
21
- TDD is the default for everything. Work one tiny cycle at a time:
22
-
23
- 1. **Write one test that asserts one thing.**
24
- 2. **Run it and watch it fail** — for the right reason. A test you never saw fail
25
- proves nothing.
26
- 3. **Write the minimum code to make it pass.**
27
- 4. **Run the test and watch it pass.**
28
- 5. **Commit.**
29
- 6. Repeat with the next test.
30
-
31
- One assertion per test. One test per commit cycle. No batching multiple behaviors
32
- into a single test or a single commit.
33
-
34
- ---
35
-
36
- ## Commits
37
-
38
- - A commit is normally **two files: the test file and the code file.**
39
- - When implementing or updating an interface (e.g. a new controller endpoint) a
40
- commit may touch more files (route + controller + view) — that is the minimal
41
- coherent unit for that interface, and it is allowed.
42
- - Keep each commit focused on the one behavior the test describes.
43
-
44
- ---
45
-
46
- ## What to Test
47
-
48
- - **Test our own code only.**
49
- - **Never test third-party code** — not a gem, not an API, not a framework. The
50
- only test that may reference a dependency is one that asserts *our system is
51
- correctly wired to it* (the integration seam), never the dependency's own
52
- behavior.
53
- - **Never test another interface inside a unit test.** A test covers one interface.
54
- The single exception is the smoke integration test described below.
55
-
56
- ---
57
-
58
- ## Smoke Integration Test
59
-
60
- When implementing an interface, write **one smoke integration test** that exercises
61
- the interface end to end and proves the pieces are connected. This is the one place
62
- where touching more than the unit under test is expected and correct.
63
-
64
- ---
65
-
66
- ## Pull Requests
67
-
68
- - **Always work on a feature branch and open a PR.** Confirm the target branch
69
- before any git operation (`git branch --show-current`).
70
- - **Keep PRs small and manageable** — typically **no more than 8–10 files.**
71
- - Keep the focus of a PR narrow. One concern per PR.
72
- - **All tests pass before opening the PR.**
73
- - **The linter and every other CI check pass before opening the PR.**
74
- - Never start a new PR until the previous one is merged.
75
-
76
- ---
77
-
78
- ## Code Quality
79
-
80
- - Follow Clean Code principles: small functions, clear names, no surprises.
81
- - Follow SOLID principles. Readable by a human first.
82
- - Keep it simple — no abstraction until a real need calls for it.
83
- - Explicitly require libraries rather than assuming autoload.
84
-
85
- ## Comments
86
-
87
- - **Write self-documenting code, not comments.** Code should be clean and readable
88
- on its own. Names — of classes, methods, variables, and partials — carry the intent.
89
- - **A comment is a smell.** If you feel a comment is needed, the code is either built
90
- wrong or needs refactoring (a clearer name, a smaller method, an extracted object or
91
- partial) so the intent is obvious without prose. Follow SOLID and this resolves itself.
92
- - Do not leave explanatory headers on classes/methods, inline "what this does" notes,
93
- or section banners. Delete them and let the structure speak.
94
- - Narrow exceptions, kept rare: a genuinely non-obvious *why* (a workaround for an
95
- external bug, a legal/security constraint) and machine-readable annotations the
96
- tooling requires (e.g. `rubocop:disable`). Prefer refactoring over a "why" comment
97
- whenever you can.
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TheLocal
4
- # Loads the canonical develop-process rules the_local propagates into every
5
- # host, so a host agent reads and follows one source of truth.
6
- module ProcessRules
7
- DIR = File.expand_path("process_rules", __dir__)
8
-
9
- def self.content
10
- read("develop_process_rules.md")
11
- end
12
-
13
- def self.read(name)
14
- File.read(File.join(DIR, name)).chomp
15
- end
16
- end
17
- end