tina4ruby 3.13.59 → 3.13.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tina4/ai.rb +132 -24
  3. data/lib/tina4/version.rb +1 -1
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 742f59a4dfdfb2143ac6d4e74ed1e86272fc16f4f662f8de614bb12e158bec7d
4
- data.tar.gz: 2ce4f2c6a43921a447cf7635e68a6ca1d71b73356be2d314c643153b6e564f76
3
+ metadata.gz: b1e099ade99e2e7b93ffa80eadd566e2b262535d25f4ce5df7d1f6d5af419996
4
+ data.tar.gz: 13aa7c39804bdf300f953737eafa9e4b2e3792a6f0847659f01b9258836cdfbe
5
5
  SHA512:
6
- metadata.gz: ddd0e09d651341124f902627bcde230879dbfd821c4d498ea2dff1cf1978c55fbea56e9aaa9bd964eedbdef0f31a4ecfe2d163d3e6159d35a1ad07fdd14bcc68
7
- data.tar.gz: 5c26a0d49d3cb9310b1324f3fc80063dded7284aad978ba6522bb54419e89f5ff98e81949045ec18c32b60ac72a186faca5924483b127920d98f2ce58e882b35
6
+ metadata.gz: 3f461d0c0d2d7ade91bd3267f3d327d26539c56a835427195265aced24a82b38eeef46ed1854a815014655552f6648f4b74d79f504c80c2d02faa53a3e9ba443
7
+ data.tar.gz: a30f293aa8af8948b09a1c4a9a70e9da4a116e6ddd6f9de3467405685ba0c4fa4fb4e66f1e6b0337cba3c6f7eba5af0a624846026337adf92a66ebd2ffc1865c
data/lib/tina4/ai.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "net/http"
5
+ require "uri"
4
6
 
5
7
  module Tina4
6
8
  # Tina4 AI -- Install AI coding assistant context files.
@@ -23,7 +25,120 @@ module Tina4
23
25
  { name: "codex", description: "OpenAI Codex", context_file: "AGENTS.md", config_dir: nil }
24
26
  ].freeze
25
27
 
28
+ # ── Skill files (the SKILL.md system) ─────────────────────────────────
29
+ # `tina4 ai` installs the actual skills -- not just a CLAUDE.md pointer to
30
+ # them -- into BOTH the project (.claude/skills, so they travel with the
31
+ # repo) AND the user's global ~/.claude/skills (so they're available in
32
+ # every project). A Ruby project needs the ruby developer skill plus the
33
+ # two shared skills. As of 3.13.59 the developer skill is split per
34
+ # language: the ruby dev skill ships from the tina4-ruby release tag, while
35
+ # tina4-js + tina4-maintainer are the canonical shared copies served from
36
+ # tina4-python. Mirrors the canonical install-skills.sh.
37
+ DEV_SKILL = "tina4-developer-ruby"
38
+ DEV_REFS = %w[
39
+ auth-and-services.md data-and-orm.md deployment.md
40
+ routes-and-api.md templates-and-frontend.md realtime.md
41
+ ].freeze
42
+
43
+ # skill name => { repo:, refs: [reference files] }
44
+ SKILLS = {
45
+ DEV_SKILL => { repo: "tina4-ruby", refs: DEV_REFS },
46
+ "tina4-js" => {
47
+ repo: "tina4-python",
48
+ refs: %w[html-and-components.md signals-and-reactivity.md persistence.md rtc.md]
49
+ },
50
+ "tina4-maintainer" => {
51
+ repo: "tina4-python",
52
+ refs: %w[cli-and-deployment.md frond-and-frontend.md routing-and-orm.md subsystems.md]
53
+ }
54
+ }.freeze
55
+
26
56
  class << self
57
+ # Release tag to pull skills from -- the installed framework version,
58
+ # overridable with TINA4_SKILLS_REF (e.g. to test a branch / tag).
59
+ #
60
+ # @return [String]
61
+ def skills_ref
62
+ ref = ENV["TINA4_SKILLS_REF"]
63
+ return ref if ref && !ref.strip.empty?
64
+ return Tina4::VERSION if defined?(Tina4::VERSION) && Tina4::VERSION
65
+ "main"
66
+ rescue StandardError
67
+ "main"
68
+ end
69
+
70
+ # Fetch the bytes at `url`, following up to `limit` redirects. Returns
71
+ # nil on any network/HTTP failure so the caller can skip gracefully.
72
+ #
73
+ # @param url [String]
74
+ # @param limit [Integer] max redirects to follow
75
+ # @return [String, nil]
76
+ def fetch_bytes(url, limit = 5)
77
+ return nil if limit <= 0
78
+
79
+ uri = URI.parse(url)
80
+ http = Net::HTTP.new(uri.host, uri.port)
81
+ http.use_ssl = (uri.scheme == "https")
82
+ http.open_timeout = 15
83
+ http.read_timeout = 15
84
+
85
+ response = http.get(uri.request_uri)
86
+ case response
87
+ when Net::HTTPSuccess
88
+ response.body
89
+ when Net::HTTPRedirection
90
+ location = response["location"]
91
+ location ? fetch_bytes(location, limit - 1) : nil
92
+ end
93
+ rescue StandardError
94
+ nil
95
+ end
96
+
97
+ # Install the Tina4 SKILL.md skills into the project AND the user's
98
+ # global ~/.claude/skills, network-fetched from the release tag matching
99
+ # this framework version. Returns the skills fully installed.
100
+ # Network-dependent -- a fetch failure skips that skill gracefully.
101
+ #
102
+ # @param root [String] project root directory
103
+ # @param targets [Array<String>, nil] .claude/skills dirs to write to;
104
+ # defaults to the project's + the user's global skills dir
105
+ # @return [Array<String>] skill names that installed cleanly
106
+ def install_skills(root = ".", targets = nil)
107
+ ref = skills_ref
108
+ if targets.nil?
109
+ targets = [
110
+ File.join(File.expand_path(root), ".claude", "skills"),
111
+ File.join(Dir.home, ".claude", "skills")
112
+ ]
113
+ end
114
+
115
+ installed = []
116
+ SKILLS.each do |skill, meta|
117
+ base = "https://raw.githubusercontent.com/tina4stack/#{meta[:repo]}/#{ref}/.claude/skills"
118
+ skill_ok = true
119
+
120
+ targets.each do |dest|
121
+ skill_dir = File.join(dest, skill)
122
+ FileUtils.mkdir_p(File.join(skill_dir, "references"))
123
+
124
+ data = fetch_bytes("#{base}/#{skill}/SKILL.md")
125
+ if data.nil?
126
+ skill_ok = false
127
+ next
128
+ end
129
+ File.binwrite(File.join(skill_dir, "SKILL.md"), data)
130
+
131
+ meta[:refs].each do |r|
132
+ rd = fetch_bytes("#{base}/#{skill}/references/#{r}")
133
+ File.binwrite(File.join(skill_dir, "references", r), rd) unless rd.nil?
134
+ end
135
+ end
136
+
137
+ installed << skill if skill_ok
138
+ end
139
+ installed
140
+ end
141
+
27
142
  # Check if a tool's context file already exists.
28
143
  #
29
144
  # @param root [String] project root directory
@@ -158,7 +273,7 @@ module Tina4
158
273
  body = if context_file.downcase.end_with?(".md")
159
274
  "## Tina4 Skills\n\n" \
160
275
  "When working on this Tina4 project, these skills give the assistant project-aware behaviour:\n\n" \
161
- "- **tina4-developer** -- Read `.claude/skills/tina4-developer/SKILL.md` before building features.\n" \
276
+ "- **#{DEV_SKILL}** -- Read `.claude/skills/#{DEV_SKILL}/SKILL.md` before building features.\n" \
162
277
  "- **tina4-js** -- Read `.claude/skills/tina4-js/SKILL.md` for frontend work.\n" \
163
278
  "- **tina4-maintainer** -- Read `.claude/skills/tina4-maintainer/SKILL.md` for framework-level changes.\n\n" \
164
279
  "If Tina4 behaves differently from what these skills describe, that is a bug in the skill. " \
@@ -167,7 +282,7 @@ module Tina4
167
282
  "See https://tina4.com for full docs."
168
283
  else
169
284
  "Tina4 Skills -- read these files before working on this project:\n" \
170
- " .claude/skills/tina4-developer/SKILL.md (feature development)\n" \
285
+ " .claude/skills/#{DEV_SKILL}/SKILL.md (feature development)\n" \
171
286
  " .claude/skills/tina4-js/SKILL.md (frontend / tina4-js)\n" \
172
287
  " .claude/skills/tina4-maintainer/SKILL.md (framework-level changes)\n" \
173
288
  "Found a skill that disagrees with how Tina4 actually behaves? Tell the developer,\n" \
@@ -277,35 +392,16 @@ module Tina4
277
392
  created
278
393
  end
279
394
 
280
- # Copy Claude Code skill files from the framework's templates.
395
+ # Install Claude Code skills + commands for a project.
281
396
  #
282
397
  # @param root [String] absolute project root path
283
398
  # @return [Array<String>] list of created/updated relative file paths
284
399
  def install_claude_skills(root)
285
400
  created = []
286
401
 
287
- # Determine the framework root (where lib/tina4/ lives)
402
+ # Copy claude-commands if they exist (a local template shipped in the
403
+ # gem, not a network fetch).
288
404
  framework_root = File.expand_path("../../..", __FILE__)
289
-
290
- # Copy skill directories from .claude/skills/ in the framework to the project
291
- framework_skills_dir = File.join(framework_root, ".claude", "skills")
292
- if Dir.exist?(framework_skills_dir)
293
- target_skills_dir = File.join(root, ".claude", "skills")
294
- FileUtils.mkdir_p(target_skills_dir)
295
- Dir.children(framework_skills_dir).each do |entry|
296
- skill_dir = File.join(framework_skills_dir, entry)
297
- next unless File.directory?(skill_dir)
298
-
299
- target_dir = File.join(target_skills_dir, entry)
300
- FileUtils.rm_rf(target_dir) if Dir.exist?(target_dir)
301
- FileUtils.cp_r(skill_dir, target_dir)
302
- rel = target_dir.sub("#{root}/", "")
303
- created << rel
304
- puts " \e[32m✓\e[0m Updated #{rel}"
305
- end
306
- end
307
-
308
- # Copy claude-commands if they exist
309
405
  commands_source = File.join(framework_root, "templates", "ai", "claude-commands")
310
406
  if Dir.exist?(commands_source)
311
407
  commands_dir = File.join(root, ".claude", "commands")
@@ -318,6 +414,18 @@ module Tina4
318
414
  end
319
415
  end
320
416
 
417
+ # Install the SKILL.md skills into the project AND the user's global
418
+ # ~/.claude/skills, network-fetched from the release tag matching this
419
+ # framework version. (The previous code copied from
420
+ # framework_root/.claude/skills, which exists only in a dev/editable
421
+ # checkout -- NOT in an installed gem, where .claude/skills sits outside
422
+ # lib/ and is never packaged. So installed users got zero skill files,
423
+ # only a CLAUDE.md pointer to skills that were never on disk.)
424
+ install_skills(root).each do |skill|
425
+ created << ".claude/skills/#{skill}/"
426
+ puts " \e[32m✓\e[0m Installed .claude/skills/#{skill} (project + global)"
427
+ end
428
+
321
429
  created
322
430
  end
323
431
 
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.13.59"
4
+ VERSION = "3.13.60"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.59
4
+ version: 3.13.60
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team