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 +4 -4
- data/CHANGELOG.md +29 -0
- data/PROVIDERS.md +10 -6
- data/lib/generators/the_local/provider_generator.rb +1 -1
- data/lib/generators/the_local/templates/guide.md.tt +20 -4
- data/lib/generators/the_local/templates/the_local.rb.tt +9 -6
- data/lib/the_local/builder.rb +28 -1
- data/lib/the_local/rake.rb +1 -1
- data/lib/the_local/reference/guide.md +89 -13
- data/lib/the_local/refresh.rb +6 -1
- data/lib/the_local/sync.rb +0 -1
- data/lib/the_local/the_local/agents/the_local-develop.md +89 -13
- data/lib/the_local/the_local/agents/the_local-info.md +89 -13
- data/lib/the_local/the_local/agents/the_local-install.md +89 -13
- data/lib/the_local/version.rb +1 -1
- data/lib/the_local.rb +0 -1
- metadata +2 -5
- data/lib/the_local/process_doc_writer.rb +0 -48
- data/lib/the_local/process_rules/develop_process_rules.md +0 -97
- data/lib/the_local/process_rules.rb +0 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16bc7ddfcc1ddab88976b10b156791a4e9a1985265d472517db9c56816d8022e
|
|
4
|
+
data.tar.gz: 57c2ec4821bd1165037311dad0720cbcf1a753770052e0f88ae5a879f4e8c2f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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"
|
|
130
|
-
|
|
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"
|
|
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
|
-
###
|
|
16
|
+
### Interface
|
|
10
17
|
|
|
11
|
-
TODO: The public API —
|
|
12
|
-
|
|
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
|
-
###
|
|
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
|
|
19
|
-
"reference. You make no changes
|
|
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
|
|
26
|
-
"reference's
|
|
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
|
|
34
|
-
"
|
|
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
|
data/lib/the_local/builder.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/the_local/rake.rb
CHANGED
|
@@ -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
|
|
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`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
- `
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
data/lib/the_local/refresh.rb
CHANGED
|
@@ -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
|
data/lib/the_local/sync.rb
CHANGED
|
@@ -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
|
|
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`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
- `
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
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`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
- `
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
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`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
- `
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
data/lib/the_local/version.rb
CHANGED
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.
|
|
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-
|
|
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
|