-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup
More file actions
executable file
·298 lines (258 loc) · 9.46 KB
/
Copy pathsetup
File metadata and controls
executable file
·298 lines (258 loc) · 9.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#!/usr/bin/env bash
# i18nstack setup — install i18n CLI tools + register skills/commands for Claude Code, Grok, and Codex
set -e
STACK_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILLS_PARENT="$(dirname "$STACK_DIR")"
AGENT_HOME="$(dirname "$SKILLS_PARENT")"
AGENT_NAME="$(basename "$AGENT_HOME")"
AGENT_ID="${AGENT_NAME#.}" # .claude -> claude, .grok -> grok
TOOLS=(i18n-convert i18n-pseudo i18n-validate)
HEADER_MARKER="<!-- grok-claude-sync:"
MARKER_FILE=".grok-claude-source.json"
# --- CLI tools ---------------------------------------------------------------
missing=()
for t in "${TOOLS[@]}"; do
command -v "$t" >/dev/null 2>&1 || missing+=("$t")
done
if [ ${#missing[@]} -gt 0 ]; then
pkgs=()
for t in "${missing[@]}"; do pkgs+=("@i18n-agent/$t"); done
if command -v npm >/dev/null 2>&1; then
echo "Installing CLI tools via npm: ${missing[*]}"
npm install -g "${pkgs[@]}" || true
elif command -v brew >/dev/null 2>&1; then
echo "Installing CLI tools via Homebrew: ${missing[*]}"
brew tap i18n-agent/tap && brew install "${missing[@]}" || true
else
echo "⚠ Neither npm nor Homebrew found — skipping CLI install."
fi
still_missing=()
for t in "${missing[@]}"; do
command -v "$t" >/dev/null 2>&1 || still_missing+=("$t")
done
if [ ${#still_missing[@]} -gt 0 ]; then
echo "⚠ Not installed: ${still_missing[*]}"
echo " Install manually: npm install -g @i18n-agent/<tool>"
echo " or: brew tap i18n-agent/tap && brew install <tool>"
fi
fi
# --- agent registration helpers ----------------------------------------------
grok_compat_header() {
local source="$1"
local kind="$2"
cat <<EOF
${HEADER_MARKER} ${source} -->
## Grok Compatibility
Installed by i18nstack \`setup\`. Source: \`${source}\`.
When following this ${kind} in Grok:
1. Tool mapping from Claude Code:
- \`Read\` -> \`read_file\`
- \`Edit\`, \`Write\`, \`MultiEdit\` -> \`search_replace\` (and write tools as needed)
- \`Glob\`, \`Grep\`, \`LS\` -> \`list_dir\`, \`grep_search\`
- \`Bash\` -> \`bash\`
- \`TodoWrite\` -> \`todo_write\`
- \`Task\`, \`Agent\`, named subagents -> \`task\` tool (use \`--subagents\` for parallel child sessions)
- \`Skill(name)\` / \`\$name\` -> \`/name\` or read that skill's \`SKILL.md\`
- \`WebFetch\`, \`WebSearch\` -> \`web_fetch\`, \`web_search\`
2. Placeholders: \`\$ARGUMENTS\` = all invocation args; \`\$1\` through \`\$9\` = positional args.
3. Ignore Claude-only \`allowed-tools\` frontmatter when it conflicts with session permissions.
4. Preserve safety instructions. Ask before destructive database, production, credential, deploy, or data-loss operations.
---
EOF
}
strip_grok_header() {
local file="$1"
python3 - "$file" <<'PY'
import sys
from pathlib import Path
text = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace")
marker = "<!-- grok-claude-sync:"
if text.startswith(marker):
parts = text.split("---\n\n", 1)
if len(parts) == 2 and parts[1].lstrip().startswith("## Grok Compatibility"):
body = parts[1].split("\n---\n\n", 1)
if len(body) == 2:
text = body[1]
print(text, end="")
PY
}
write_marker() {
local target="$1"
local kind="$2"
local source="$3"
python3 - "$target" "$kind" "$source" <<'PY'
import json, sys
from datetime import datetime, timezone
from pathlib import Path
target, kind, source = sys.argv[1:4]
marker = {
"generator": "i18nstack/setup",
"kind": kind,
"source": source,
"updatedAt": datetime.now(timezone.utc).isoformat(),
}
path = Path(target) / ".grok-claude-source.json"
path.write_text(json.dumps(marker, indent=2, sort_keys=True) + "\n", encoding="utf-8")
PY
}
ensure_stack_entrypoint() {
local source="$STACK_DIR/i18nstack/SKILL.md"
local target="$STACK_DIR/SKILL.md"
local relative_source="i18nstack/SKILL.md"
if [ ! -f "$source" ]; then
echo "⚠ i18nstack umbrella skill missing: $source"
return 1
fi
# skills.sh discovery requires the umbrella skill to live in its own
# directory in the repository. Direct agent installs, however, use the
# repository root as the i18nstack package anchor and expect SKILL.md there.
# Keep the root entrypoint generated and gitignored so both layouts work.
if [ -L "$target" ] || [ ! -e "$target" ]; then
ln -snf "$relative_source" "$target"
elif ! cmp -s "$source" "$target"; then
echo "⚠ $target exists and differs from $source — refusing to overwrite it"
return 1
fi
}
ensure_stack_anchor() {
local agent_home="$1"
local skills_dir="$agent_home/skills"
local anchor="$skills_dir/i18nstack"
mkdir -p "$skills_dir"
if [ "$STACK_DIR" = "$anchor" ] || [ "$anchor" -ef "$STACK_DIR" ]; then
return 0
fi
if [ -L "$anchor" ] || [ ! -e "$anchor" ]; then
ln -snf "$STACK_DIR" "$anchor"
return 0
fi
echo "⚠ $anchor exists and is not managed by i18nstack — skipping $agent_home registration"
return 1
}
register_claude() {
local agent_home="$1"
local skills_dir="$agent_home/skills"
local commands_dir="$agent_home/commands"
local linked=() skipped=() linked_cmds=()
ensure_stack_anchor "$agent_home" || return 0
mkdir -p "$commands_dir"
for skill_dir in "$STACK_DIR"/*/; do
skill_name="$(basename "$skill_dir")"
[[ "$skill_name" == "skills" || "$skill_name" == "i18nstack" ]] && continue
[ -f "$skill_dir/SKILL.md" ] || continue
target="$skills_dir/$skill_name"
if [ -L "$target" ] || [ ! -e "$target" ]; then
ln -snf "i18nstack/$skill_name" "$target"
linked+=("$skill_name")
else
skipped+=("$skill_name")
fi
done
for cmd_file in "$STACK_DIR"/commands/*.md; do
[ -f "$cmd_file" ] || continue
cmd_name="$(basename "$cmd_file")"
target="$commands_dir/$cmd_name"
if [ -L "$target" ] || [ ! -e "$target" ]; then
ln -snf "../skills/i18nstack/commands/$cmd_name" "$target"
linked_cmds+=("$cmd_name")
else
skipped+=("commands/$cmd_name")
fi
done
echo "Claude Code ($agent_home):"
echo " linked skills: ${#linked[@]}"
echo " linked commands: ${#linked_cmds[@]}"
if [ ${#skipped[@]} -gt 0 ]; then
echo " skipped (already exists): ${skipped[*]}"
fi
}
register_grok() {
local agent_home="$1"
local skills_dir="$agent_home/skills"
local commands_dir="$agent_home/commands"
local linked=() skipped=() linked_cmds=()
ensure_stack_anchor "$agent_home" || return 0
mkdir -p "$commands_dir"
for skill_dir in "$STACK_DIR"/*/; do
skill_name="$(basename "$skill_dir")"
[[ "$skill_name" == "skills" || "$skill_name" == "i18nstack" ]] && continue
[ -f "$skill_dir/SKILL.md" ] || continue
target="$skills_dir/$skill_name"
if [ -L "$target" ] || [ ! -e "$target" ]; then
ln -snf "i18nstack/$skill_name" "$target"
write_marker "$target" "skill-symlink" "$skill_dir/SKILL.md"
linked+=("$skill_name")
else
skipped+=("$skill_name")
fi
done
for cmd_file in "$STACK_DIR"/commands/*.md; do
[ -f "$cmd_file" ] || continue
cmd_name="$(basename "$cmd_file")"
target="$commands_dir/$cmd_name"
if [ -L "$target" ] || [ ! -e "$target" ]; then
{
grok_compat_header "$cmd_file" "command"
strip_grok_header "$cmd_file"
} >"$target"
linked_cmds+=("$cmd_name")
else
skipped+=("commands/$cmd_name")
fi
done
echo "Grok ($agent_home):"
echo " linked skills: ${#linked[@]}"
echo " installed commands: ${#linked_cmds[@]}"
if [ ${#skipped[@]} -gt 0 ]; then
echo " skipped (already exists): ${skipped[*]}"
fi
}
register_codex() {
local script="$STACK_DIR/scripts/register-codex.py"
if [ ! -f "$script" ]; then
echo "⚠ Codex registration script missing: $script"
return 0
fi
python3 "$script" "$STACK_DIR"
}
# --- register with supported agents ------------------------------------------
ensure_stack_entrypoint
in_canonical_location=0
stack_name="$(basename "$STACK_DIR")"
parent_name="$(basename "$SKILLS_PARENT")"
agent_dir_name="$(basename "$(dirname "$SKILLS_PARENT")")"
if [ "$stack_name" = "i18nstack" ]; then
if [ "$parent_name" = "skills" ] && { [ "$agent_dir_name" = ".claude" ] || [ "$agent_dir_name" = ".grok" ] || [ "$agent_dir_name" = ".agents" ]; }; then
in_canonical_location=1
elif [ "$parent_name" = ".codex" ] || [ "$SKILLS_PARENT" = "$HOME/.codex" ]; then
in_canonical_location=1
fi
fi
if [ "$in_canonical_location" -eq 1 ]; then
echo ""
if [ -d "$HOME/.claude" ] || [ "$AGENT_ID" = "claude" ]; then
register_claude "$HOME/.claude"
fi
if [ -d "$HOME/.grok" ] || [ "$AGENT_ID" = "grok" ]; then
register_grok "$HOME/.grok"
fi
if [ -d "$HOME/.codex" ] || [ -d "$HOME/.agents" ] || [ "$AGENT_ID" = "codex" ] || [ "$AGENT_ID" = "agents" ]; then
register_codex
fi
echo ""
echo "i18nstack ready."
else
echo ""
echo "i18nstack ready. (skipped skill registration — clone into a supported agent path first)"
echo " Claude: git clone --depth 1 https://github.com/i18n-agent/i18nstack.git ~/.claude/skills/i18nstack && ~/.claude/skills/i18nstack/setup"
echo " Grok: git clone --depth 1 https://github.com/i18n-agent/i18nstack.git ~/.grok/skills/i18nstack && ~/.grok/skills/i18nstack/setup"
echo " Codex: git clone --depth 1 https://github.com/i18n-agent/i18nstack.git ~/.codex/i18nstack && ~/.codex/i18nstack/setup"
fi
if [ -x "$STACK_DIR/scripts/sync-skills-catalog.sh" ]; then
"$STACK_DIR/scripts/sync-skills-catalog.sh" >/dev/null
fi
for t in "${TOOLS[@]}"; do
if command -v "$t" >/dev/null 2>&1; then
echo " $t: $("$t" --version 2>/dev/null | head -1)"
fi
done