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.
- checksums.yaml +4 -4
- data/lib/tina4/ai.rb +132 -24
- data/lib/tina4/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1e099ade99e2e7b93ffa80eadd566e2b262535d25f4ce5df7d1f6d5af419996
|
|
4
|
+
data.tar.gz: 13aa7c39804bdf300f953737eafa9e4b2e3792a6f0847659f01b9258836cdfbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
"- **
|
|
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/
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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