tina4ruby 3.13.7 → 3.13.9
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 +114 -3
- 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: c247ddb7851f9f5e0f8cfb8a7a74a4e7690cd9fe606d5d32f7603aa9547aff1d
|
|
4
|
+
data.tar.gz: 75d979c50632935abe4c4864d3c0f28065b3ad87e2f13a81db999f04379fcfb8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed1956eb82a7ce8242fcbb6c95b14613d72a6931c62bda8bf2e6944f7f77f61e838f40f0a2aeac031f8392d4230407129e2d0edd958c2ab4db2ecc5831fb0570
|
|
7
|
+
data.tar.gz: a8fed6c6985d6f717c083c977ef26df48ddf55af67816e4595918ef4183a5d78c3cea7c98e0849a8b1407cf5ef34f67b69840de0465d4b086561c2fa2ab3e1ac
|
data/lib/tina4/ai.rb
CHANGED
|
@@ -135,6 +135,118 @@ module Tina4
|
|
|
135
135
|
# @param tool [Hash] tool entry from AI_TOOLS
|
|
136
136
|
# @param context [String] generated context content
|
|
137
137
|
# @return [Array<String>] list of created/updated relative file paths
|
|
138
|
+
# ── v3.13.9: non-destructive context-file writer ─────────────────
|
|
139
|
+
#
|
|
140
|
+
# Pre-v3.13.9 the installer wrote a full developer guide to
|
|
141
|
+
# CLAUDE.md (and the other context files) on every run, clobbering
|
|
142
|
+
# whatever the user had put there. Now it writes only a marker-
|
|
143
|
+
# bracketed Tina4 skill block -- pointing the assistant at
|
|
144
|
+
# .claude/skills/tina4-*/SKILL.md -- and leaves the rest alone.
|
|
145
|
+
|
|
146
|
+
# Return [start, end] markers for the given context file.
|
|
147
|
+
def markers_for(context_file)
|
|
148
|
+
if context_file.downcase.end_with?(".md")
|
|
149
|
+
["<!-- tina4-skills:start -->", "<!-- tina4-skills:end -->"]
|
|
150
|
+
else
|
|
151
|
+
["# tina4-skills:start", "# tina4-skills:end"]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Return the marker-bracketed Tina4 skill registration block.
|
|
156
|
+
def skill_block(context_file)
|
|
157
|
+
start, finish = markers_for(context_file)
|
|
158
|
+
body = if context_file.downcase.end_with?(".md")
|
|
159
|
+
"## Tina4 Skills\n\n" \
|
|
160
|
+
"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" \
|
|
162
|
+
"- **tina4-js** -- Read `.claude/skills/tina4-js/SKILL.md` for frontend work.\n" \
|
|
163
|
+
"- **tina4-maintainer** -- Read `.claude/skills/tina4-maintainer/SKILL.md` for framework-level changes.\n\n" \
|
|
164
|
+
"See https://tina4.com for full docs."
|
|
165
|
+
else
|
|
166
|
+
"Tina4 Skills -- read these files before working on this project:\n" \
|
|
167
|
+
" .claude/skills/tina4-developer/SKILL.md (feature development)\n" \
|
|
168
|
+
" .claude/skills/tina4-js/SKILL.md (frontend / tina4-js)\n" \
|
|
169
|
+
" .claude/skills/tina4-maintainer/SKILL.md (framework-level changes)\n" \
|
|
170
|
+
"Docs: https://tina4.com"
|
|
171
|
+
end
|
|
172
|
+
"#{start}\n#{body}\n#{finish}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# True iff both start and end markers appear in order.
|
|
176
|
+
def has_markers?(existing, start, finish)
|
|
177
|
+
s_idx = existing.index(start)
|
|
178
|
+
return false unless s_idx
|
|
179
|
+
!existing.index(finish, s_idx + start.length).nil?
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Replace the bracketed block in `existing` with `block`.
|
|
183
|
+
def replace_marker_block(existing, block, start, finish)
|
|
184
|
+
s_idx = existing.index(start)
|
|
185
|
+
return existing.rstrip + "\n\n" + block + "\n" unless s_idx
|
|
186
|
+
e_idx = existing.index(finish, s_idx + start.length)
|
|
187
|
+
return existing.rstrip + "\n\n" + block + "\n" unless e_idx
|
|
188
|
+
before = existing[0...s_idx].rstrip
|
|
189
|
+
after = existing[(e_idx + finish.length)..].sub(/\A\n+/, "")
|
|
190
|
+
glue_before = before.empty? ? "" : "\n\n"
|
|
191
|
+
glue_after = after.empty? ? "\n" : "\n" + after
|
|
192
|
+
"#{before}#{glue_before}#{block}#{glue_after}"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Headers the pre-v3.13.9 installer wrote at the top of CLAUDE.md.
|
|
196
|
+
OLD_FRAMEWORK_HEADERS = [
|
|
197
|
+
"# Tina4 Python",
|
|
198
|
+
"# Tina4 PHP",
|
|
199
|
+
"# Tina4 Ruby",
|
|
200
|
+
"# CLAUDE.md -- AI Developer Guide for tina4-nodejs",
|
|
201
|
+
"# CLAUDE.md — AI Developer Guide for tina4-nodejs",
|
|
202
|
+
].freeze
|
|
203
|
+
|
|
204
|
+
def looks_like_old_framework_install?(existing)
|
|
205
|
+
head = existing.lstrip[0, 400] || ""
|
|
206
|
+
OLD_FRAMEWORK_HEADERS.any? { |h| head.start_with?(h) }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Write the context file non-destructively. Returns a human-readable
|
|
210
|
+
# action verb for the caller's log line.
|
|
211
|
+
#
|
|
212
|
+
# Four branches:
|
|
213
|
+
# 1. Doesn't exist -> write framework guide + skill block
|
|
214
|
+
# 2. Has markers -> refresh just the skill block (idempotent)
|
|
215
|
+
# 3. Old header -> migrate: replace old dump with new guide + block
|
|
216
|
+
# 4. User content -> append the skill block, preserve everything else
|
|
217
|
+
def write_or_merge(context_path, context_file, framework_guide)
|
|
218
|
+
# Force UTF-8 — CLAUDE.md and the framework guide both contain
|
|
219
|
+
# non-ASCII (em-dashes, ✓, etc.). Without this, File.read may
|
|
220
|
+
# return ASCII-8BIT and string concat raises CompatibilityError.
|
|
221
|
+
block = skill_block(context_file).dup.force_encoding("UTF-8")
|
|
222
|
+
guide = framework_guide.dup.force_encoding("UTF-8")
|
|
223
|
+
start, finish = markers_for(context_file)
|
|
224
|
+
|
|
225
|
+
unless File.exist?(context_path)
|
|
226
|
+
File.write(context_path, guide.rstrip + "\n\n" + block + "\n", encoding: "UTF-8")
|
|
227
|
+
return "Installed"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
existing = File.read(context_path, encoding: "UTF-8")
|
|
231
|
+
|
|
232
|
+
if has_markers?(existing, start, finish)
|
|
233
|
+
File.write(context_path, replace_marker_block(existing, block, start, finish), encoding: "UTF-8")
|
|
234
|
+
return "Refreshed skill block in"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
if looks_like_old_framework_install?(existing)
|
|
238
|
+
head = existing.lstrip
|
|
239
|
+
preamble = existing[0, existing.length - head.length] || ""
|
|
240
|
+
new_content = (preamble.strip.empty? ? "" : preamble.rstrip + "\n\n") +
|
|
241
|
+
guide.rstrip + "\n\n" + block + "\n"
|
|
242
|
+
File.write(context_path, new_content, encoding: "UTF-8")
|
|
243
|
+
return "Migrated (replaced old framework dump in)"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
File.write(context_path, existing.rstrip + "\n\n" + block + "\n", encoding: "UTF-8")
|
|
247
|
+
"Appended skill block to"
|
|
248
|
+
end
|
|
249
|
+
|
|
138
250
|
def install_for_tool(root, tool, context)
|
|
139
251
|
created = []
|
|
140
252
|
context_path = File.join(root, tool[:context_file])
|
|
@@ -145,9 +257,8 @@ module Tina4
|
|
|
145
257
|
end
|
|
146
258
|
FileUtils.mkdir_p(File.dirname(context_path))
|
|
147
259
|
|
|
148
|
-
#
|
|
149
|
-
action =
|
|
150
|
-
File.write(context_path, context)
|
|
260
|
+
# v3.13.9: non-destructive write -- see write_or_merge below.
|
|
261
|
+
action = write_or_merge(context_path, tool[:context_file], context)
|
|
151
262
|
rel = context_path.sub("#{root}/", "")
|
|
152
263
|
created << rel
|
|
153
264
|
puts " \e[32m✓\e[0m #{action} #{rel}"
|
data/lib/tina4/version.rb
CHANGED