senren-ui 0.1.4 → 0.1.5

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: 82671c3e673cf8cf9d922e83685d72bf5448b9acb1073596a9643f3da6fc9c15
4
- data.tar.gz: 11691be1d0c35c8137aab34d0cb7ede42dc2b7a9f9d7f4cfa9b7a8b8acb1ba3e
3
+ metadata.gz: e233d7736e61036693bb895d99e0fd11a37eb9e183979fa9ea4f1d17d1686354
4
+ data.tar.gz: dbe764908e51d5007e876c412e2cdffeac6d2b36ac10b8b16187f376ef1e24f0
5
5
  SHA512:
6
- metadata.gz: 52d5185842e8b35d4d3c6b2b9e3ec0015e1e09e436a830434f32a4f7fe64d846ba301c90d0229243a672cbfcc140e5104de5aef610ee8d33bc666414a08d83ee
7
- data.tar.gz: eaf517da0a216f79d74fa6bbd25873f4f510b283a4fcb0b948f60a0dc9a6966c9e09e7cfd4d55987eaefc7d0600fd8bc90170b1b278a9afb8ca71fec875d34bf
6
+ metadata.gz: 4a9bd5abdf7f39b9afd87d94e263b256c4e8fd226d5505bee5e1c7cd031e44fe917442b17479718fe39d23909ceee0f94e064073a7717b6309508471db18ce8b
7
+ data.tar.gz: a40ab2909ac0f30900fe8e2aace6eba439820ac83cd2ccd37c6037bfcf372225c02d1268a721c3b38a4c243872c35b833ead558621a65a8399b9e8ea632d96b8
data/CHANGELOG.md CHANGED
@@ -7,6 +7,35 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  v0.x is a pre-stable line: minor bumps may break things; patch bumps are
8
8
  bug fixes only.
9
9
 
10
+ ## [0.1.5] — 2026-05-03
11
+
12
+ ### Added
13
+
14
+ - Multi-agent instruction sync system (`AgentRulesWriter`). A single
15
+ source-of-truth file (`.senren/agent-rules.md`) plus marker-managed
16
+ adapter files for Codex (`AGENTS.md`), Claude (`CLAUDE.md`),
17
+ Copilot (`.github/copilot-instructions.md`), and Cursor
18
+ (`.cursor/rules/senren.mdc`).
19
+ - New rake task `senren:agents:sync`.
20
+ - Plan 014 and Plan 015 documentation.
21
+
22
+ ### Changed
23
+
24
+ - `LlmsWriter` is now a thin backward-compatible wrapper that delegates
25
+ to `AgentRulesWriter`. No more `public/llms*.txt` generation.
26
+ - `senren:llms:generate` kept as deprecated alias.
27
+ - `Doctor` checks now validate agent instruction files instead of
28
+ `public/llms*.txt`.
29
+ - Doctor `run!` refactored into `runtime_checks` + `installation_checks`.
30
+ - Install generator no longer creates `public/` directory.
31
+
32
+ ### Fixed
33
+
34
+ - Deprecated `senren:llms:generate` task now passes `registry:` kwarg
35
+ consistently with all other call sites.
36
+ - Release checklist items updated to reflect agent sync system.
37
+ - Test assertion style standardized on Minitest-native `refute`.
38
+
10
39
  ## [0.1.4] — 2026-05-02
11
40
 
12
41
  ### Fixed
data/README.md CHANGED
@@ -14,8 +14,7 @@ skill system and a source-copy install model inspired by shadcn/ui.
14
14
  - A registry of well-tested ViewComponents and Stimulus controllers.
15
15
  - A centralized `.senren/skill.md` file so AI coding agents understand
16
16
  every installed component, its dependencies, and its anti-patterns.
17
- - An `llms.txt` / `llms-full.txt` generator so AI agents can discover
18
- Senren without scraping your codebase.
17
+ - A multi-agent instruction sync for Codex, Claude, Copilot, and Cursor.
19
18
 
20
19
  ## What Senren is not
21
20
 
@@ -52,7 +51,7 @@ bin/rails generate senren:component picker --no-client # without Stimulus
52
51
  bin/rails senren:add dialog --client # install interactive official component
53
52
  bin/rails senren:add button # install static official component
54
53
  bin/rails senren:skill:sync # rebuild .senren/skill.md
55
- bin/rails senren:llms:generate # rebuild public/llms*.txt
54
+ bin/rails senren:agents:sync # rebuild .senren/agent-rules + adapters
56
55
  bin/rails senren:doctor # check installation health
57
56
  ```
58
57
 
@@ -96,12 +95,17 @@ After install, your app contains:
96
95
  - `.senren/registry.yml` — mirror of the gem-side registry.
97
96
  - `.senren/installed_components.yml` — ledger of installed components.
98
97
  - `.senren/conventions.md` — Senren conventions for humans and agents.
99
- - `public/llms.txt`, `public/llms-full.txt` — discoverable AI summary.
98
+ - `.senren/agent-rules.md` — source of truth for generated agent rules.
99
+ - `AGENTS.md`, `CLAUDE.md`, `.github/copilot-instructions.md`,
100
+ `.cursor/rules/senren.mdc` — marker-managed adapter files for each agent.
100
101
 
101
102
  The skill file uses `<!-- senren:skill:start -->` / `:end` markers; only
102
103
  the region between them is rewritten by the generator, so any notes you
103
104
  add outside the markers are preserved.
104
105
 
106
+ Agent adapter files are also marker-managed, so Senren updates only its own
107
+ generated block and preserves your existing instructions outside that block.
108
+
105
109
  ## Component list
106
110
 
107
111
  See `registry/components.yml` for the canonical list. v0.1 ships:
@@ -27,10 +27,6 @@ module Senren
27
27
  empty_directory 'app/assets/stylesheets'
28
28
  end
29
29
 
30
- def create_public_dir
31
- empty_directory 'public'
32
- end
33
-
34
30
  def copy_base_files
35
31
  template 'base_component.rb.tt', 'app/components/senren/base_component.rb'
36
32
  template 'senren.css.tt', 'app/assets/stylesheets/senren.css'
@@ -47,14 +43,15 @@ module Senren
47
43
  Senren::Rails::SkillWriter.new(paths: host_paths).sync!
48
44
  end
49
45
 
50
- def write_llms_files
51
- say_status :senren, 'writing public/llms.txt and public/llms-full.txt'
52
- Senren::Rails::LlmsWriter.new(paths: host_paths).generate!
46
+ def write_agent_files
47
+ say_status :senren, 'syncing Codex/Cursor/Claude/Copilot instruction files'
48
+ Senren::Rails::AgentRulesWriter.new(paths: host_paths).sync!
53
49
  end
54
50
 
55
51
  def print_next_steps
56
52
  say "\nSenren installed."
57
53
  say 'Next: bin/rails senren:add button card badge alert dialog'
54
+ say 'Then: bin/rails senren:agents:sync'
58
55
  end
59
56
 
60
57
  private
@@ -38,8 +38,11 @@ and obey it strictly.
38
38
  | `.senren/registry.yml` | Generator (mirror of gem registry) |
39
39
  | `.senren/installed_components.yml` | Generator (ledger) |
40
40
  | `.senren/conventions.md` | This file - safe to edit |
41
- | `public/llms.txt` | Generator (do not edit) |
42
- | `public/llms-full.txt` | Generator (do not edit) |
41
+ | `.senren/agent-rules.md` | Generator (do not edit) |
42
+ | `AGENTS.md` | Generated region only (between markers) |
43
+ | `CLAUDE.md` | Generated region only (between markers) |
44
+ | `.github/copilot-instructions.md` | Generated region only (between markers) |
45
+ | `.cursor/rules/senren.mdc` | Generated region only (between markers) |
43
46
 
44
47
  ## Adding a component
45
48
 
@@ -64,3 +67,10 @@ Senren never overwrites those files unless you pass `--force`.
64
67
 
65
68
  You can write app-specific notes outside those markers. They are preserved
66
69
  across `bin/rails senren:skill:sync`.
70
+
71
+ ## Agent instruction maintenance
72
+
73
+ Senren writes a shared source file at `.senren/agent-rules.md` and syncs
74
+ adapter files for Codex/Cursor/Claude/Copilot. For adapter files, only the
75
+ generated block between markers is managed by Senren; your custom notes
76
+ outside markers are preserved.
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Senren
6
+ module Rails
7
+ # Generates a single source-of-truth rules file plus adapter files for
8
+ # Copilot, Cursor, Claude, and Codex.
9
+ #
10
+ # Adapter files are marker-managed to avoid overwriting existing project
11
+ # instructions outside Senren's generated block.
12
+ class AgentRulesWriter
13
+ START_MARKER = '<!-- senren:agent:start -->'
14
+ END_MARKER = '<!-- senren:agent:end -->'
15
+
16
+ attr_reader :registry, :paths
17
+
18
+ def initialize(registry: Registry.load!, paths: HostPaths.new)
19
+ @registry = registry
20
+ @paths = paths
21
+ end
22
+
23
+ def sync!
24
+ paths.ensure_agent_dirs!
25
+ files = []
26
+ files << write_full_file(paths.agent_rules_file, render_source_rules)
27
+ files << write_adapter_file(paths.codex_agents_md, render_codex_adapter)
28
+ files << write_adapter_file(paths.claude_md, render_claude_adapter)
29
+ files << write_adapter_file(paths.copilot_instructions, render_copilot_adapter)
30
+ files << write_adapter_file(paths.cursor_rule_file, render_cursor_adapter, prefix: cursor_frontmatter)
31
+ files
32
+ end
33
+
34
+ private
35
+
36
+ def installed_names
37
+ path = paths.installed_components
38
+ return [] unless path.exist?
39
+
40
+ ledger = YAML.safe_load_file(path) || {}
41
+ Array(ledger['installed']).filter_map { |entry| registry.find(entry['name']) }.map(&:name).sort
42
+ end
43
+
44
+ def render_source_rules
45
+ names = installed_names
46
+ <<~MD
47
+ # Senren Agent Rules
48
+
49
+ Generated by senren-ui. Do not edit by hand.
50
+ Run `bin/rails senren:agents:sync` to regenerate.
51
+
52
+ This file is the Senren source of truth for AI coding agents in this app.
53
+
54
+ ## Hard Rules
55
+
56
+ - Use Senren components before writing custom HTML.
57
+ - Use ViewComponent for reusable UI.
58
+ - Use Turbo for server state.
59
+ - Use Stimulus only for local behavior.
60
+ - Do not introduce React, Vue, Alpine, or external state frameworks.
61
+ - Use semantic Tailwind tokens; do not hard-code color families.
62
+
63
+ ## Important Files
64
+
65
+ - `.senren/skill.md` - centralized component guidance
66
+ - `.senren/conventions.md` - project conventions
67
+ - `.senren/registry.yml` - component registry mirror
68
+ - `.senren/installed_components.yml` - local install ledger
69
+
70
+ ## Installed Components (#{names.size})
71
+
72
+ #{format_names(names)}
73
+ MD
74
+ end
75
+
76
+ def render_codex_adapter
77
+ <<~MD
78
+ ## Senren UI
79
+
80
+ Follow `.senren/agent-rules.md` as the source of truth.
81
+
82
+ - Prefer Senren components before custom HTML.
83
+ - Keep reusable UI in ViewComponent.
84
+ - Use Turbo for server state, Stimulus for local behavior.
85
+ - Do not add React, Vue, Alpine, or external state frameworks.
86
+ - Use semantic Tailwind tokens.
87
+ MD
88
+ end
89
+
90
+ def render_claude_adapter
91
+ <<~MD
92
+ ## Senren UI
93
+
94
+ @.senren/agent-rules.md
95
+
96
+ Apply Senren conventions and component-first rules from the imported file.
97
+ MD
98
+ end
99
+
100
+ def render_copilot_adapter
101
+ <<~MD
102
+ ## Senren UI
103
+
104
+ Use `.senren/agent-rules.md` as the source of truth for this repository.
105
+
106
+ - Prefer Senren components before custom HTML.
107
+ - Reusable UI must use ViewComponent.
108
+ - Turbo handles server state; Stimulus handles local behavior.
109
+ - Do not introduce React, Vue, Alpine, or external state frameworks.
110
+ - Use semantic Tailwind tokens.
111
+ MD
112
+ end
113
+
114
+ def render_cursor_adapter
115
+ <<~MD
116
+ ## Senren UI
117
+
118
+ Follow `.senren/agent-rules.md` as the source of truth.
119
+
120
+ - Prefer Senren components before custom HTML.
121
+ - Reusable UI uses ViewComponent.
122
+ - Turbo for server state, Stimulus for local behavior.
123
+ - No React/Vue/Alpine/external state frameworks.
124
+ - Use semantic Tailwind tokens.
125
+ MD
126
+ end
127
+
128
+ def cursor_frontmatter
129
+ <<~MDC
130
+ ---
131
+ description: Senren UI repository conventions
132
+ alwaysApply: true
133
+ ---
134
+ MDC
135
+ end
136
+
137
+ def format_names(names)
138
+ return '_No components installed yet._' if names.empty?
139
+
140
+ names.map { |name| "- #{name}" }.join("\n")
141
+ end
142
+
143
+ def write_full_file(path, content)
144
+ atomic_write(path, content)
145
+ path
146
+ end
147
+
148
+ def write_adapter_file(path, generated, prefix: '')
149
+ existing = path.exist? ? path.read : prefix.to_s
150
+ updated = inject(existing, generated)
151
+ atomic_write(path, updated)
152
+ path
153
+ end
154
+
155
+ def inject(existing, generated)
156
+ if existing.include?(START_MARKER) && existing.include?(END_MARKER)
157
+ before = existing.split(START_MARKER, 2).first
158
+ tail = existing.split(START_MARKER, 2).last
159
+ after = tail.split(END_MARKER, 2).last
160
+ "#{before}#{START_MARKER}\n\n#{generated.rstrip}\n\n#{END_MARKER}#{after}"
161
+ else
162
+ body = existing.rstrip
163
+ prefix = body.empty? ? '' : "#{body}\n\n"
164
+ "#{prefix}#{START_MARKER}\n\n#{generated.rstrip}\n\n#{END_MARKER}\n"
165
+ end
166
+ end
167
+
168
+ def atomic_write(path, content)
169
+ tmp = "#{path}.tmp"
170
+ File.write(tmp, content)
171
+ File.rename(tmp, path)
172
+ end
173
+ end
174
+ end
175
+ end
@@ -17,19 +17,7 @@ module Senren
17
17
  end
18
18
 
19
19
  def run!
20
- results = []
21
- results << check('ViewComponent gem available') { defined?(::ViewComponent) }
22
- results << check('TailwindCSS stylesheet present') { paths.stylesheet_path.exist? }
23
- results << check('Stimulus directory present') { paths.stimulus_dir.directory? }
24
- results << check('Turbo gem available') { defined?(::Turbo) || gem_loadable?('turbo-rails') }
25
- results << check('.senren directory exists') { paths.senren_dir.directory? }
26
- results << check('.senren/skill.md exists') { paths.skill_file.file? }
27
- results << check('.senren/registry.yml exists') { paths.registry_mirror.file? }
28
- results << check('.senren/installed_components.yml exists') { paths.installed_components.file? }
29
- results << check('public/llms.txt exists') { paths.llms_short.file? }
30
- results << check('public/llms-full.txt exists') { paths.llms_full.file? }
31
- results << check('app/components/senren exists') { paths.components_dir.directory? }
32
- results << check('app/javascript/controllers/senren exists') { paths.stimulus_dir.directory? }
20
+ results = runtime_checks + installation_checks
33
21
  installed = installed_count
34
22
  results << Result.new("#{installed} component(s) installed", installed >= 0, nil)
35
23
 
@@ -57,6 +45,31 @@ module Senren
57
45
  false
58
46
  end
59
47
 
48
+ def runtime_checks
49
+ [
50
+ check('ViewComponent gem available') { defined?(::ViewComponent) },
51
+ check('TailwindCSS stylesheet present') { paths.stylesheet_path.exist? },
52
+ check('Stimulus directory present') { paths.stimulus_dir.directory? },
53
+ check('Turbo gem available') { defined?(::Turbo) || gem_loadable?('turbo-rails') }
54
+ ]
55
+ end
56
+
57
+ def installation_checks
58
+ [
59
+ check('.senren directory exists') { paths.senren_dir.directory? },
60
+ check('.senren/skill.md exists') { paths.skill_file.file? },
61
+ check('.senren/registry.yml exists') { paths.registry_mirror.file? },
62
+ check('.senren/installed_components.yml exists') { paths.installed_components.file? },
63
+ check('.senren/agent-rules.md exists') { paths.agent_rules_file.file? },
64
+ check('AGENTS.md exists') { paths.codex_agents_md.file? },
65
+ check('CLAUDE.md exists') { paths.claude_md.file? },
66
+ check('.github/copilot-instructions.md exists') { paths.copilot_instructions.file? },
67
+ check('.cursor/rules/senren.mdc exists') { paths.cursor_rule_file.file? },
68
+ check('app/components/senren exists') { paths.components_dir.directory? },
69
+ check('app/javascript/controllers/senren exists') { paths.stimulus_dir.directory? }
70
+ ]
71
+ end
72
+
60
73
  def installed_count
61
74
  return 0 unless paths.installed_components.file?
62
75
 
@@ -16,6 +16,7 @@ module Senren
16
16
  def registry_mirror = senren_dir.join('registry.yml')
17
17
  def installed_components = senren_dir.join('installed_components.yml')
18
18
  def conventions_file = senren_dir.join('conventions.md')
19
+ def agent_rules_file = senren_dir.join('agent-rules.md')
19
20
 
20
21
  def components_dir = root.join('app', 'components', 'senren')
21
22
  def base_component_path = components_dir.join('base_component.rb')
@@ -24,12 +25,20 @@ module Senren
24
25
 
25
26
  def stimulus_dir = root.join('app', 'javascript', 'controllers', 'senren')
26
27
 
27
- def llms_short = root.join('public', 'llms.txt')
28
- def llms_full = root.join('public', 'llms-full.txt')
28
+ def github_dir = root.join('.github')
29
+ def copilot_instructions = github_dir.join('copilot-instructions.md')
30
+ def cursor_rules_dir = root.join('.cursor', 'rules')
31
+ def cursor_rule_file = cursor_rules_dir.join('senren.mdc')
32
+ def claude_md = root.join('CLAUDE.md')
33
+ def codex_agents_md = root.join('AGENTS.md')
29
34
 
30
35
  def ensure_dirs!
31
36
  [senren_dir, components_dir, stimulus_dir,
32
- stylesheet_path.dirname, llms_short.dirname].each(&:mkpath)
37
+ stylesheet_path.dirname, github_dir, cursor_rules_dir].each(&:mkpath)
38
+ end
39
+
40
+ def ensure_agent_dirs!
41
+ [senren_dir, github_dir, cursor_rules_dir].each(&:mkpath)
33
42
  end
34
43
  end
35
44
  end
@@ -5,7 +5,7 @@ require 'fileutils'
5
5
  module Senren
6
6
  module Rails
7
7
  # Idempotent installer that lays down the .senren directory, base
8
- # component, stylesheet, and initial llms files. Reused by the install
8
+ # component, stylesheet, and agent instruction files. Reused by the install
9
9
  # generator and by ad-hoc rake task entry points.
10
10
  class Installer
11
11
  attr_reader :paths, :stdout
@@ -20,7 +20,7 @@ module Senren
20
20
  install_static_files(force: force)
21
21
  mirror_registry
22
22
  SkillWriter.new(paths: paths).sync!
23
- LlmsWriter.new(paths: paths).generate!
23
+ AgentRulesWriter.new(paths: paths).sync!
24
24
  print_next_steps
25
25
  true
26
26
  end
@@ -73,6 +73,7 @@ module Senren
73
73
 
74
74
  bin/rails senren:add button card badge alert
75
75
  bin/rails senren:add dialog dropdown_menu
76
+ bin/rails senren:agents:sync
76
77
  bin/rails senren:doctor
77
78
 
78
79
  Read .senren/skill.md and .senren/conventions.md to get oriented.
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
-
5
3
  module Senren
6
4
  module Rails
7
- # Generates public/llms.txt and public/llms-full.txt for AI discoverability.
8
- # The entire content of both files is owned by this writer; do not edit by hand.
5
+ # Backward-compatible wrapper around AgentRulesWriter.
6
+ #
7
+ # Legacy llms generation now maps to agent rules synchronization and no longer
8
+ # writes public llms files.
9
9
  class LlmsWriter
10
10
  attr_reader :registry, :paths
11
11
 
@@ -15,134 +15,7 @@ module Senren
15
15
  end
16
16
 
17
17
  def generate!
18
- paths.llms_short.parent.mkpath
19
- paths.llms_full.parent.mkpath
20
- atomic_write(paths.llms_short, render_short)
21
- atomic_write(paths.llms_full, render_full)
22
- [paths.llms_short, paths.llms_full]
23
- end
24
-
25
- private
26
-
27
- def installed
28
- path = paths.installed_components
29
- return [] unless path.exist?
30
-
31
- ledger = YAML.safe_load_file(path) || {}
32
- Array(ledger['installed']).filter_map { |e| registry.find(e['name']) }
33
- end
34
-
35
- def render_short
36
- names = installed.map(&:name).sort
37
- <<~TXT
38
- # Senren UI
39
-
40
- Generated by senren-ui. Do not edit by hand. Run
41
- `bin/rails senren:llms:generate` to regenerate.
42
-
43
- Senren UI is the local Rails UI system used by this application.
44
- Use `.senren/skill.md` as the primary AI Agent guide.
45
-
46
- ## Hard Rules
47
-
48
- - Use Senren components before writing custom HTML.
49
- - Use ViewComponent for reusable UI.
50
- - Use Turbo for server state.
51
- - Use Stimulus only for local behavior.
52
- - Do not introduce React, Vue, Alpine, or any external state framework.
53
- - Do not hard-code colors; use semantic Tailwind tokens.
54
-
55
- ## Important Files
56
-
57
- - `.senren/skill.md` - centralized AI agent guide
58
- - `.senren/registry.yml` - mirror of installable components
59
- - `.senren/installed_components.yml` - what is currently installed
60
- - `.senren/conventions.md` - Senren conventions for humans and agents
61
-
62
- ## Installed Components (#{names.size})
63
-
64
- #{names.map { |n| "- #{n}" }.join("\n")}
65
- TXT
66
- end
67
-
68
- def render_full
69
- comps = installed.sort_by(&:name)
70
-
71
- out = []
72
- out << '# Senren UI - Full Snapshot'
73
- out << ''
74
- out << 'Generated by senren-ui. Do not edit by hand. Run'
75
- out << '`bin/rails senren:llms:generate` to regenerate.'
76
- out << ''
77
- out << '## Hard Rules'
78
- out << ''
79
- out << '- Use Senren components before writing custom HTML.'
80
- out << '- Server-rendered HTML first; ViewComponent for reusable UI.'
81
- out << '- Hotwire (Turbo + Stimulus) is the only client runtime.'
82
- out << '- TailwindCSS with semantic tokens (`bg-background`, `text-foreground`, ...).'
83
- out << '- Do not introduce React, Vue, Alpine, or external state frameworks.'
84
- out << '- Components copied into `app/components/senren/` are owned by this app; edit them directly.'
85
- out << ''
86
- out << '## Installed Component Inventory'
87
- out << ''
88
-
89
- registry.groups.each do |group|
90
- group_comps = comps.select { |c| c.category == group['id'] }
91
- next if group_comps.empty?
92
-
93
- out << "### #{group['title']}"
94
- out << ''
95
- out << group['description'].to_s
96
- out << ''
97
- group_comps.each { |c| out << render_component(c) }
98
- end
99
-
100
- out << '## Recipes'
101
- out << ''
102
- registry.recipes.each do |id, recipe|
103
- out << "### #{id}"
104
- out << ''
105
- out << recipe['description'].to_s
106
- out << ''
107
- out << 'Components:'
108
- recipe['components'].each { |c| out << "- #{c}" }
109
- out << ''
110
- end
111
-
112
- out.join("\n")
113
- end
114
-
115
- def render_component(comp)
116
- ruby_class = "Senren::#{comp.name.split('_').map { |w| w[0].upcase + w[1..] }.join}Component"
117
- s = []
118
- s << "#### #{comp.name}#{' (stub)' if comp.stub?}"
119
- s << ''
120
- s << "Category: #{comp.category}. " \
121
- "Client: #{comp.client? ? "yes (#{comp.controller})" : 'no'}. " \
122
- "Variants: #{comp.variants.empty? ? 'none' : comp.variants.join(', ')}."
123
- s << ''
124
- s << "Use for: #{comp.use_for.join('; ')}." unless comp.use_for.empty?
125
- s << "Avoid: #{comp.avoid.join('; ')}." unless comp.avoid.empty?
126
- s << ''
127
- s << 'Rails usage:'
128
- s << ''
129
- s << '```erb'
130
- s << if comp.variants.any?
131
- "<%= render #{ruby_class}.new(variant: :#{comp.variants.first}) do %>"
132
- else
133
- "<%= render #{ruby_class}.new do %>"
134
- end
135
- s << ' ...'
136
- s << '<% end %>'
137
- s << '```'
138
- s << ''
139
- s.join("\n")
140
- end
141
-
142
- def atomic_write(path, content)
143
- tmp = "#{path}.tmp"
144
- File.write(tmp, content)
145
- File.rename(tmp, path)
18
+ AgentRulesWriter.new(registry: registry, paths: paths).sync!
146
19
  end
147
20
  end
148
21
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Senren
4
4
  module Rails
5
- VERSION = '0.1.4'
5
+ VERSION = '0.1.5'
6
6
  end
7
7
  end
data/lib/senren/rails.rb CHANGED
@@ -9,6 +9,7 @@ module Senren
9
9
  autoload :Registry, 'senren/rails/registry'
10
10
  autoload :ComponentCopier, 'senren/rails/component_copier'
11
11
  autoload :SkillWriter, 'senren/rails/skill_writer'
12
+ autoload :AgentRulesWriter, 'senren/rails/agent_rules_writer'
12
13
  autoload :LlmsWriter, 'senren/rails/llms_writer'
13
14
  autoload :Installer, 'senren/rails/installer'
14
15
  autoload :Doctor, 'senren/rails/doctor'
@@ -16,27 +16,39 @@ namespace :senren do
16
16
  installed = copier.install(names, client_override: options[:client_override], force: options[:force])
17
17
 
18
18
  Senren::Rails::SkillWriter.new(registry: registry, paths: paths).sync!
19
- Senren::Rails::LlmsWriter.new(registry: registry, paths: paths).generate!
19
+ Senren::Rails::AgentRulesWriter.new(registry: registry, paths: paths).sync!
20
20
 
21
21
  puts "Installed: #{installed.join(', ')}"
22
22
  end
23
23
 
24
24
  namespace :skill do
25
- desc 'Rebuild .senren/skill.md from the registry and installed_components ledger.'
25
+ desc 'Rebuild .senren/skill.md and refresh agent instruction adapters.'
26
26
  task sync: :environment do
27
27
  paths = Senren::Rails::HostPaths.new
28
28
  registry = Senren::Rails::Registry.load!
29
29
  file = Senren::Rails::SkillWriter.new(registry: registry, paths: paths).sync!
30
30
  puts "Wrote #{file}"
31
+ Senren::Rails::AgentRulesWriter.new(registry: registry, paths: paths).sync!
32
+ end
33
+ end
34
+
35
+ namespace :agents do
36
+ desc 'Regenerate .senren/agent-rules.md and adapter instruction files.'
37
+ task sync: :environment do
38
+ paths = Senren::Rails::HostPaths.new
39
+ registry = Senren::Rails::Registry.load!
40
+ files = Senren::Rails::AgentRulesWriter.new(registry: registry, paths: paths).sync!
41
+ files.each { |f| puts "Wrote #{f}" }
31
42
  end
32
43
  end
33
44
 
34
45
  namespace :llms do
35
- desc 'Regenerate public/llms.txt and public/llms-full.txt.'
46
+ desc 'Deprecated alias for senren:agents:sync.'
36
47
  task generate: :environment do
48
+ puts 'senren:llms:generate is deprecated. Running senren:agents:sync instead.'
37
49
  paths = Senren::Rails::HostPaths.new
38
50
  registry = Senren::Rails::Registry.load!
39
- files = Senren::Rails::LlmsWriter.new(registry: registry, paths: paths).generate!
51
+ files = Senren::Rails::AgentRulesWriter.new(registry: registry, paths: paths).sync!
40
52
  files.each { |f| puts "Wrote #{f}" }
41
53
  end
42
54
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: senren-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - vutt
@@ -66,6 +66,7 @@ files:
66
66
  - lib/generators/senren/install/templates/installed_components.yml.tt
67
67
  - lib/generators/senren/install/templates/senren.css.tt
68
68
  - lib/senren/rails.rb
69
+ - lib/senren/rails/agent_rules_writer.rb
69
70
  - lib/senren/rails/component_copier.rb
70
71
  - lib/senren/rails/doctor.rb
71
72
  - lib/senren/rails/engine.rb