@kuandotdev/indicator 0.1.1 → 0.1.3

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 (80) hide show
  1. package/README.md +2 -7
  2. package/package.json +4 -1
  3. package/project/.cursor/rules/agent-docs.mdc +16 -0
  4. package/project/.cursor/rules/api-credential-storage.mdc +16 -0
  5. package/project/.cursor/rules/pinescript-v6.mdc +16 -0
  6. package/project/.cursor/rules/stock-model-forecast.mdc +16 -0
  7. package/project/.cursor/rules/system-prompt-injection.mdc +16 -0
  8. package/project/.cursor/rules/system-prompt-updater.mdc +16 -0
  9. package/project/.cursor/rules/tradingview-stock-data.mdc +16 -0
  10. package/project/.env.example +44 -0
  11. package/project/.npm-packaged-project +1 -0
  12. package/project/.pi/APPEND_SYSTEM.md +338 -0
  13. package/project/.pi/settings.json +8 -0
  14. package/project/AGENTS.md +538 -0
  15. package/project/CLAUDE.md +538 -0
  16. package/project/GEMINI.md +538 -0
  17. package/project/Makefile +488 -0
  18. package/project/README.md +419 -0
  19. package/project/conda-env-active.sh +98 -0
  20. package/project/conda-env-deactive.sh +42 -0
  21. package/project/docs/agent-install.md +446 -0
  22. package/project/docs/agent-skill-directory.md +222 -0
  23. package/project/docs/integration.html +271 -0
  24. package/project/packages/indicator/README.md +39 -0
  25. package/project/packages/indicator/package.json +40 -0
  26. package/project/packages/indicator/scripts/build-project-snapshot.js +57 -0
  27. package/project/packages/indicator/src/cli.js +368 -0
  28. package/project/packages/tradingview-stock-data-skill/README.md +112 -0
  29. package/project/packages/tradingview-stock-data-skill/extensions/stock-prompt-injector.ts +121 -0
  30. package/project/packages/tradingview-stock-data-skill/package.json +35 -0
  31. package/project/packages/tradingview-stock-data-skill/scripts/postinstall.sh +73 -0
  32. package/project/packages/tradingview-stock-data-skill/skills/tradingview-stock-data/SKILL.md +241 -0
  33. package/project/pyproject.toml +68 -0
  34. package/project/screenshots/.gitkeep +0 -0
  35. package/project/scripts/indicators/example_rsi_bands.pine +27 -0
  36. package/project/scripts/indicators/tsla_levels.pine +57 -0
  37. package/project/skills/agent-docs/SKILL.md +56 -0
  38. package/project/skills/api-credential-storage/SKILL.md +83 -0
  39. package/project/skills/api-credential-storage/scripts/upsert_env.py +151 -0
  40. package/project/skills/pinescript-v6/SKILL.md +129 -0
  41. package/project/skills/pinescript-v6/reference/built-ins.md +219 -0
  42. package/project/skills/pinescript-v6/reference/templates/alert-webhook.pine +76 -0
  43. package/project/skills/pinescript-v6/reference/templates/indicator.pine +48 -0
  44. package/project/skills/pinescript-v6/reference/templates/strategy.pine +50 -0
  45. package/project/skills/pinescript-v6/reference/v5-to-v6-migration.md +102 -0
  46. package/project/skills/pinescript-v6/reference/v6-language.md +202 -0
  47. package/project/skills/stock-model-forecast/SKILL.md +192 -0
  48. package/project/skills/system-prompt-injection/CUSTOM_SYSTEM_PROMPT.md +333 -0
  49. package/project/skills/system-prompt-injection/DEFAULT_SYSTEM_PROMPT.md +327 -0
  50. package/project/skills/system-prompt-injection/SKILL.md +90 -0
  51. package/project/skills/system-prompt-injection/SYSTEM_PROMPT.md +23 -0
  52. package/project/skills/system-prompt-updater/SKILL.md +82 -0
  53. package/project/skills/system-prompt-updater/scripts/system_prompt_update.sh +106 -0
  54. package/project/skills/tradingview-stock-data/SKILL.md +272 -0
  55. package/project/src/tv_indicator/__init__.py +0 -0
  56. package/project/src/tv_indicator/browser/__init__.py +0 -0
  57. package/project/src/tv_indicator/browser/automation.py +541 -0
  58. package/project/src/tv_indicator/browser/selectors.py +70 -0
  59. package/project/src/tv_indicator/cli/__init__.py +0 -0
  60. package/project/src/tv_indicator/cli/browser_cmds.py +92 -0
  61. package/project/src/tv_indicator/cli/data_cmds.py +178 -0
  62. package/project/src/tv_indicator/cli/main.py +56 -0
  63. package/project/src/tv_indicator/cli/model_cmds.py +255 -0
  64. package/project/src/tv_indicator/cli/pine_cmds.py +140 -0
  65. package/project/src/tv_indicator/config.py +98 -0
  66. package/project/src/tv_indicator/data/__init__.py +0 -0
  67. package/project/src/tv_indicator/data/client.py +187 -0
  68. package/project/src/tv_indicator/data/screener.py +268 -0
  69. package/project/src/tv_indicator/mcp/__init__.py +0 -0
  70. package/project/src/tv_indicator/mcp/agent_server.py +398 -0
  71. package/project/src/tv_indicator/mcp/browser_server.py +133 -0
  72. package/project/src/tv_indicator/mcp/data_server.py +239 -0
  73. package/project/src/tv_indicator/model/__init__.py +19 -0
  74. package/project/src/tv_indicator/model/forecast.py +693 -0
  75. package/project/tools/import_agent_tools.sh +503 -0
  76. package/project/tools/install_skills.sh +673 -0
  77. package/project/tools/interactive_install.sh +917 -0
  78. package/project/tools/progress.sh +114 -0
  79. package/project/tools/uninstall_agent_tools.sh +373 -0
  80. package/src/cli.js +22 -25
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env bash
2
+ # import_agent_tools.sh — detect/configure the target agent platform(s) and wire
3
+ # the TradingView/market-analysis tools, skills, prompt files, package extension,
4
+ # and MCP config needed by those agents.
5
+ #
6
+ # Usage:
7
+ # tools/import_agent_tools.sh # auto-detect; fallback all
8
+ # tools/import_agent_tools.sh --platform pi # pi only
9
+ # tools/import_agent_tools.sh --platform claude-code # Claude Code only
10
+ # tools/import_agent_tools.sh --platform claude-desktop
11
+ # tools/import_agent_tools.sh --platform pi,claude-code,codex
12
+ # tools/import_agent_tools.sh --platform all
13
+ # tools/import_agent_tools.sh --dry-run
14
+
15
+ set -euo pipefail
16
+
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
19
+ CONDA_ENV="${CONDA_ENV:-tv-indicator}"
20
+ PLATFORM="auto"
21
+ DRY_RUN=0
22
+ GLOBAL_CLAUDE_SKILLS=0
23
+ WRITE_PI_MCP=1
24
+ WRITE_CLAUDE_DESKTOP_MCP=1
25
+ WRITE_CLAUDE_CODE_MCP=1
26
+ WRITE_CODEX_MCP=1
27
+ WRITE_GEMINI_MCP=1
28
+ WRITE_CURSOR_MCP=1
29
+
30
+ usage() {
31
+ sed -n '1,18p' "$0" | sed 's/^# \{0,1\}//'
32
+ cat <<'EOF'
33
+
34
+ Options:
35
+ --platform <names> auto | all | pi | claude-desktop | claude-code | claude | codex | gemini | cursor
36
+ Supports comma/space-separated lists.
37
+ --dry-run Print actions without writing
38
+ --global-claude-skills Also symlink skills into ~/.claude/skills
39
+ --no-pi-mcp Do not generate .pi/config.toml MCP entries
40
+ --no-claude-desktop-mcp Do not update Claude Desktop MCP config
41
+ --no-claude-code-mcp Do not write .mcp.json for Claude Code
42
+ --no-codex-mcp Do not update ~/.codex/config.toml MCP config
43
+ --no-gemini-mcp Do not update ~/.gemini/settings.json MCP config
44
+ --no-cursor-mcp Do not write .cursor/mcp.json MCP config
45
+ -h, --help Show help
46
+
47
+ What it imports:
48
+ pi -> .pi/APPEND_SYSTEM.md, .pi/skills, package extension, .pi/config.toml MCP
49
+ claude-desktop -> Claude Desktop claude_desktop_config.json MCP servers
50
+ claude-code -> CLAUDE.md, .claude/skills, .mcp.json MCP servers
51
+ codex -> AGENTS.md, ~/.codex/config.toml MCP servers
52
+ gemini -> GEMINI.md, ~/.gemini/settings.json MCP servers
53
+ cursor -> .cursor/rules/*.mdc, .cursor/mcp.json MCP servers
54
+ all -> all of the above
55
+ EOF
56
+ }
57
+
58
+ while [[ $# -gt 0 ]]; do
59
+ case "$1" in
60
+ --platform)
61
+ PLATFORM="${2:-}"
62
+ [[ -n "$PLATFORM" ]] || { echo "--platform requires a value" >&2; exit 2; }
63
+ shift 2
64
+ ;;
65
+ --dry-run) DRY_RUN=1; shift ;;
66
+ --global-claude-skills) GLOBAL_CLAUDE_SKILLS=1; shift ;;
67
+ --no-pi-mcp) WRITE_PI_MCP=0; shift ;;
68
+ --no-claude-desktop-mcp) WRITE_CLAUDE_DESKTOP_MCP=0; shift ;;
69
+ --no-claude-code-mcp) WRITE_CLAUDE_CODE_MCP=0; shift ;;
70
+ --no-codex-mcp) WRITE_CODEX_MCP=0; shift ;;
71
+ --no-gemini-mcp) WRITE_GEMINI_MCP=0; shift ;;
72
+ --no-cursor-mcp) WRITE_CURSOR_MCP=0; shift ;;
73
+ -h|--help) usage; exit 0 ;;
74
+ *) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;;
75
+ esac
76
+ done
77
+
78
+ cd "$PROJECT_ROOT"
79
+
80
+ log() { printf '%s\n' "$*"; }
81
+ warn() { printf 'WARN: %s\n' "$*" >&2; }
82
+ have_cmd() { command -v "$1" >/dev/null 2>&1; }
83
+
84
+ mcp_command_path() {
85
+ local exe="$1"
86
+ local resolved=""
87
+ if have_cmd conda; then
88
+ resolved="$(conda run -n "$CONDA_ENV" which "$exe" 2>/dev/null || true)"
89
+ fi
90
+ if [[ -z "$resolved" ]]; then
91
+ resolved="$(command -v "$exe" 2>/dev/null || true)"
92
+ fi
93
+ if [[ -z "$resolved" ]]; then
94
+ resolved="$exe"
95
+ fi
96
+ printf '%s\n' "$resolved"
97
+ }
98
+
99
+ mcp_servers_json() {
100
+ local data_cmd browser_cmd agent_cmd
101
+ data_cmd="$(mcp_command_path training-view-mcp-data)"
102
+ browser_cmd="$(mcp_command_path training-view-mcp-browser)"
103
+ agent_cmd="$(mcp_command_path training-view-mcp-agent)"
104
+
105
+ python3 - "$data_cmd" "$browser_cmd" "$agent_cmd" <<'PY'
106
+ import json, sys
107
+ print(json.dumps({
108
+ "training-view-data": {"command": sys.argv[1]},
109
+ "training-view-browser": {"command": sys.argv[2], "env": {"TV_BROWSER_HEADLESS": "true"}},
110
+ "training-view-agent": {"command": sys.argv[3]},
111
+ }, indent=2))
112
+ PY
113
+ }
114
+
115
+ write_mcp_json_config() {
116
+ local path="$1"
117
+ local label="$2"
118
+ local servers_json
119
+ servers_json="$(mcp_servers_json)"
120
+
121
+ if [[ "$DRY_RUN" == "1" ]]; then
122
+ log "[dry-run] merge TradingView MCP servers (training-view-data, -browser, -agent) into $label: $path"
123
+ printf '%s\n' "$servers_json"
124
+ return
125
+ fi
126
+
127
+ mkdir -p "$(dirname "$path")"
128
+ python3 - "$path" "$servers_json" "$label" <<'PY'
129
+ import json
130
+ import sys
131
+ from pathlib import Path
132
+
133
+ path = Path(sys.argv[1]).expanduser()
134
+ servers = json.loads(sys.argv[2])
135
+ label = sys.argv[3]
136
+
137
+ if path.exists() and path.read_text(encoding="utf-8").strip():
138
+ try:
139
+ data = json.loads(path.read_text(encoding="utf-8"))
140
+ except json.JSONDecodeError:
141
+ backup = path.with_suffix(path.suffix + ".bak")
142
+ backup.write_text(path.read_text(encoding="utf-8"), encoding="utf-8")
143
+ print(f"Existing {label} config was invalid JSON; backed up to {backup}")
144
+ data = {}
145
+ else:
146
+ data = {}
147
+
148
+ mcp = data.get("mcpServers")
149
+ if not isinstance(mcp, dict):
150
+ mcp = {}
151
+
152
+ # Evict legacy tv-* names so re-importing after the rename doesn't
153
+ # leave stale duplicates alongside the new training-view-* entries.
154
+ for legacy in ("tv-data", "tv-browser", "tv-agent"):
155
+ mcp.pop(legacy, None)
156
+
157
+ for name, cfg in servers.items():
158
+ mcp[name] = cfg
159
+
160
+ data["mcpServers"] = mcp
161
+ path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
162
+ print(f"Wrote {label} MCP config: {path}")
163
+ PY
164
+ }
165
+
166
+ claude_desktop_config_path() {
167
+ case "$(uname -s 2>/dev/null || echo unknown)" in
168
+ Darwin) printf '%s\n' "$HOME/Library/Application Support/Claude/claude_desktop_config.json" ;;
169
+ Linux) printf '%s\n' "$HOME/.config/Claude/claude_desktop_config.json" ;;
170
+ MINGW*|MSYS*|CYGWIN*) printf '%s\n' "${APPDATA:-$HOME/AppData/Roaming}/Claude/claude_desktop_config.json" ;;
171
+ *) printf '%s\n' "$HOME/.config/Claude/claude_desktop_config.json" ;;
172
+ esac
173
+ }
174
+
175
+ write_toml_mcp_block_config() {
176
+ local path="$1"
177
+ local label="$2"
178
+ local prefix="$3"
179
+ local data_cmd browser_cmd agent_cmd
180
+ data_cmd="$(mcp_command_path training-view-mcp-data)"
181
+ browser_cmd="$(mcp_command_path training-view-mcp-browser)"
182
+ agent_cmd="$(mcp_command_path training-view-mcp-agent)"
183
+
184
+ local block
185
+ block="# BEGIN tv-indicator agent tools (managed by tools/import_agent_tools.sh)
186
+ [$prefix.training-view-data]
187
+ command = \"$data_cmd\"
188
+
189
+ [$prefix.training-view-browser]
190
+ command = \"$browser_cmd\"
191
+ env = { TV_BROWSER_HEADLESS = \"true\" }
192
+
193
+ [$prefix.training-view-agent]
194
+ command = \"$agent_cmd\"
195
+ # END tv-indicator agent tools
196
+ "
197
+
198
+ if [[ "$DRY_RUN" == "1" ]]; then
199
+ log "[dry-run] write/update managed MCP block in $label: $path"
200
+ printf '%s\n' "$block"
201
+ return
202
+ fi
203
+
204
+ mkdir -p "$(dirname "$path")"
205
+ python3 - "$path" "$block" "$prefix" "$label" <<'PY'
206
+ import sys
207
+ from pathlib import Path
208
+
209
+ path = Path(sys.argv[1]).expanduser()
210
+ block = sys.argv[2]
211
+ prefix = sys.argv[3]
212
+ label = sys.argv[4]
213
+ start = "# BEGIN tv-indicator agent tools"
214
+ end = "# END tv-indicator agent tools"
215
+ text = path.read_text(encoding="utf-8") if path.exists() else ""
216
+
217
+ if start not in text:
218
+ for section in (
219
+ f"[{prefix}.training-view-data]",
220
+ f"[{prefix}.training-view-browser]",
221
+ f"[{prefix}.training-view-agent]",
222
+ ):
223
+ if section in text:
224
+ print(f"Skipping {label} MCP write because {section} already exists outside managed block")
225
+ sys.exit(0)
226
+
227
+ if start in text and end in text:
228
+ before = text.split(start, 1)[0].rstrip()
229
+ after = text.split(end, 1)[1].lstrip()
230
+ new_text = (before + "\n\n" if before else "") + block + ("\n" + after if after else "")
231
+ else:
232
+ new_text = (text.rstrip() + "\n\n" if text.strip() else "") + block
233
+
234
+ path.write_text(new_text, encoding="utf-8")
235
+ print(f"Wrote {label} MCP config: {path}")
236
+ PY
237
+ }
238
+
239
+ json_update_pi_settings() {
240
+ local settings="$PROJECT_ROOT/.pi/settings.json"
241
+ local package_source="../packages/tradingview-stock-data-skill"
242
+
243
+ if [[ "$DRY_RUN" == "1" ]]; then
244
+ log "[dry-run] ensure .pi/settings.json loads package extension only: $package_source (skills disabled to avoid duplicate skill conflicts)"
245
+ return
246
+ fi
247
+
248
+ mkdir -p "$PROJECT_ROOT/.pi"
249
+ python3 - "$settings" "$package_source" <<'PY'
250
+ import json
251
+ import sys
252
+ from pathlib import Path
253
+
254
+ settings_path = Path(sys.argv[1])
255
+ source = sys.argv[2]
256
+ if settings_path.exists() and settings_path.read_text(encoding="utf-8").strip():
257
+ try:
258
+ data = json.loads(settings_path.read_text(encoding="utf-8"))
259
+ except json.JSONDecodeError:
260
+ backup = settings_path.with_suffix(settings_path.suffix + ".bak")
261
+ backup.write_text(settings_path.read_text(encoding="utf-8"), encoding="utf-8")
262
+ print(f"Existing settings JSON was invalid; backed up to {backup}")
263
+ data = {}
264
+ else:
265
+ data = {}
266
+
267
+ packages = data.get("packages", [])
268
+ if not isinstance(packages, list):
269
+ packages = []
270
+
271
+ filtered = []
272
+ for item in packages:
273
+ if item == source:
274
+ continue
275
+ if isinstance(item, dict) and item.get("source") == source:
276
+ continue
277
+ filtered.append(item)
278
+
279
+ filtered.append({"source": source, "skills": []})
280
+ data["packages"] = filtered
281
+ settings_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
282
+ PY
283
+ }
284
+
285
+ write_pi_import_sentinel() {
286
+ local sentinel="$PROJECT_ROOT/.pi/.tv-indicator-auto-imported"
287
+ if [[ "$DRY_RUN" == "1" ]]; then
288
+ log "[dry-run] write Pi auto-import sentinel: .pi/.tv-indicator-auto-imported"
289
+ return
290
+ fi
291
+ mkdir -p "$PROJECT_ROOT/.pi"
292
+ {
293
+ echo "Managed by tools/import_agent_tools.sh"
294
+ echo "platform=pi"
295
+ echo "updated_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
296
+ } > "$sentinel"
297
+ }
298
+
299
+ write_pi_mcp_config() {
300
+ [[ "$WRITE_PI_MCP" == "1" ]] || return 0
301
+
302
+ local config="$PROJECT_ROOT/.pi/config.toml"
303
+ local data_cmd browser_cmd agent_cmd
304
+ data_cmd="$(mcp_command_path training-view-mcp-data)"
305
+ browser_cmd="$(mcp_command_path training-view-mcp-browser)"
306
+ agent_cmd="$(mcp_command_path training-view-mcp-agent)"
307
+
308
+ local block
309
+ block="# BEGIN tv-indicator agent tools (managed by tools/import_agent_tools.sh)
310
+ [mcp_servers.training-view-data]
311
+ command = \"$data_cmd\"
312
+
313
+ [mcp_servers.training-view-browser]
314
+ command = \"$browser_cmd\"
315
+ env = { TV_BROWSER_HEADLESS = \"true\" }
316
+
317
+ [mcp_servers.training-view-agent]
318
+ command = \"$agent_cmd\"
319
+ # END tv-indicator agent tools
320
+ "
321
+
322
+ if [[ "$DRY_RUN" == "1" ]]; then
323
+ log "[dry-run] write/update managed MCP block in .pi/config.toml"
324
+ printf '%s\n' "$block"
325
+ return
326
+ fi
327
+
328
+ mkdir -p "$PROJECT_ROOT/.pi"
329
+ python3 - "$config" "$block" <<'PY'
330
+ import sys
331
+ from pathlib import Path
332
+
333
+ path = Path(sys.argv[1])
334
+ block = sys.argv[2]
335
+ start = "# BEGIN tv-indicator agent tools"
336
+ end = "# END tv-indicator agent tools"
337
+ text = path.read_text(encoding="utf-8") if path.exists() else ""
338
+
339
+ if start not in text:
340
+ for section in (
341
+ "[mcp_servers.training-view-data]",
342
+ "[mcp_servers.training-view-browser]",
343
+ "[mcp_servers.training-view-agent]",
344
+ ):
345
+ if section in text:
346
+ print(f"Skipping .pi/config.toml MCP write because {section} already exists outside managed block")
347
+ sys.exit(0)
348
+
349
+ if start in text and end in text:
350
+ before = text.split(start, 1)[0].rstrip()
351
+ after = text.split(end, 1)[1].lstrip()
352
+ new_text = (before + "\n\n" if before else "") + block + ("\n" + after if after else "")
353
+ else:
354
+ new_text = (text.rstrip() + "\n\n" if text.strip() else "") + block
355
+
356
+ path.write_text(new_text, encoding="utf-8")
357
+ print(f"Wrote managed MCP config: {path}")
358
+ PY
359
+ }
360
+
361
+ install_project_skills() {
362
+ log "Refreshing project skills, context files, docs, and active startup prompt..."
363
+ if [[ "$DRY_RUN" == "1" ]]; then
364
+ log "[dry-run] ./tools/install_skills.sh"
365
+ else
366
+ ./tools/install_skills.sh
367
+ fi
368
+ }
369
+
370
+ install_global_claude_skills_if_requested() {
371
+ [[ "$GLOBAL_CLAUDE_SKILLS" == "1" ]] || return 0
372
+ log "Installing user-global Claude skills..."
373
+ if [[ "$DRY_RUN" == "1" ]]; then
374
+ log "[dry-run] TV_INSTALL_GLOBAL_CLAUDE=1 ./tools/install_skills.sh"
375
+ else
376
+ TV_INSTALL_GLOBAL_CLAUDE=1 ./tools/install_skills.sh
377
+ fi
378
+ }
379
+
380
+ detect_active_platform() {
381
+ if [[ -n "${PI_CODING_AGENT_SESSION_DIR:-}${PI_SESSION_ID:-}" ]]; then echo "pi"; return; fi
382
+ if [[ -n "${CLAUDE_SESSION_ID:-}${CLAUDECODE:-}" ]]; then echo "claude-code"; return; fi
383
+ if [[ -n "${CODEX_SANDBOX:-}${CODEX_SESSION_ID:-}" ]]; then echo "codex"; return; fi
384
+ if [[ -n "${GEMINI_CLI:-}${GEMINI_SESSION_ID:-}" ]]; then echo "gemini"; return; fi
385
+ echo "all"
386
+ }
387
+
388
+ add_unique_platform() {
389
+ local current="$1"
390
+ local item="$2"
391
+ case " $current " in
392
+ *" $item "*) printf '%s\n' "$current" ;;
393
+ *) printf '%s\n' "${current:+$current }$item" ;;
394
+ esac
395
+ }
396
+
397
+ normalize_platforms() {
398
+ local raw="$1"
399
+ [[ "$raw" == "auto" ]] && raw="$(detect_active_platform)"
400
+ raw="${raw//,/ }"
401
+
402
+ local selected=""
403
+ local token mapped
404
+ for token in $raw; do
405
+ case "$token" in
406
+ all)
407
+ for mapped in pi claude-desktop claude-code codex gemini cursor; do
408
+ selected="$(add_unique_platform "$selected" "$mapped")"
409
+ done
410
+ ;;
411
+ pi) mapped="pi"; selected="$(add_unique_platform "$selected" "$mapped")" ;;
412
+ claude|claude-code|claude_code) mapped="claude-code"; selected="$(add_unique_platform "$selected" "$mapped")" ;;
413
+ claude-desktop|claude_desktop|desktop) mapped="claude-desktop"; selected="$(add_unique_platform "$selected" "$mapped")" ;;
414
+ codex|opencode|aider) mapped="codex"; selected="$(add_unique_platform "$selected" "$mapped")" ;;
415
+ gemini) mapped="gemini"; selected="$(add_unique_platform "$selected" "$mapped")" ;;
416
+ cursor) mapped="cursor"; selected="$(add_unique_platform "$selected" "$mapped")" ;;
417
+ "") ;;
418
+ *) echo "Unsupported platform: $token" >&2; exit 2 ;;
419
+ esac
420
+ done
421
+ printf '%s\n' "$selected"
422
+ }
423
+
424
+ print_detection_summary() {
425
+ log "Agent platform import"
426
+ log " project: $PROJECT_ROOT"
427
+ log " request: $PLATFORM"
428
+ log " selected: $(normalize_platforms "$PLATFORM")"
429
+ log " detected commands: pi=$(have_cmd pi && echo yes || echo no), claude=$(have_cmd claude && echo yes || echo no), codex=$(have_cmd codex && echo yes || echo no), gemini=$(have_cmd gemini && echo yes || echo no)"
430
+ log ""
431
+ }
432
+
433
+ configure_pi() {
434
+ log "[pi] Importing Pi package extension and MCP tools..."
435
+ json_update_pi_settings
436
+ write_pi_mcp_config
437
+ write_pi_import_sentinel
438
+ log "[pi] Uses .pi/APPEND_SYSTEM.md, .pi/skills, .pi/settings.json package extension, and .pi/config.toml MCP."
439
+ }
440
+
441
+ configure_claude_desktop() {
442
+ log "[claude-desktop] Importing Claude Desktop MCP servers..."
443
+ if [[ "$WRITE_CLAUDE_DESKTOP_MCP" == "1" ]]; then
444
+ write_mcp_json_config "$(claude_desktop_config_path)" "Claude Desktop"
445
+ fi
446
+ log "[claude-desktop] Uses Claude Desktop mcpServers: training-view-data, training-view-browser, training-view-agent."
447
+ }
448
+
449
+ configure_claude_code() {
450
+ log "[claude-code] Importing Claude Code project context, skills, and MCP servers..."
451
+ install_global_claude_skills_if_requested
452
+ if [[ "$WRITE_CLAUDE_CODE_MCP" == "1" ]]; then
453
+ write_mcp_json_config "$PROJECT_ROOT/.mcp.json" "Claude Code project"
454
+ fi
455
+ log "[claude-code] Uses CLAUDE.md, .claude/skills/, and .mcp.json."
456
+ }
457
+
458
+ configure_codex() {
459
+ log "[codex] Importing Codex/OpenCode/Aider project instructions and MCP servers..."
460
+ if [[ "$WRITE_CODEX_MCP" == "1" ]]; then
461
+ write_toml_mcp_block_config "$HOME/.codex/config.toml" "Codex" "mcpServers"
462
+ fi
463
+ log "[codex] Uses AGENTS.md and ~/.codex/config.toml MCP servers."
464
+ }
465
+
466
+ configure_gemini() {
467
+ log "[gemini] Importing Gemini project instructions and MCP servers..."
468
+ if [[ "$WRITE_GEMINI_MCP" == "1" ]]; then
469
+ write_mcp_json_config "$HOME/.gemini/settings.json" "Gemini CLI"
470
+ fi
471
+ log "[gemini] Uses GEMINI.md and ~/.gemini/settings.json MCP servers."
472
+ }
473
+
474
+ configure_cursor() {
475
+ log "[cursor] Importing Cursor rules and MCP servers..."
476
+ if [[ "$WRITE_CURSOR_MCP" == "1" ]]; then
477
+ write_mcp_json_config "$PROJECT_ROOT/.cursor/mcp.json" "Cursor project"
478
+ fi
479
+ log "[cursor] Uses .cursor/rules/*.mdc and .cursor/mcp.json."
480
+ }
481
+
482
+ main() {
483
+ print_detection_summary
484
+ install_project_skills
485
+
486
+ local selected
487
+ selected="$(normalize_platforms "$PLATFORM")"
488
+ for p in $selected; do
489
+ case "$p" in
490
+ pi) configure_pi ;;
491
+ claude-desktop) configure_claude_desktop ;;
492
+ claude-code) configure_claude_code ;;
493
+ codex) configure_codex ;;
494
+ gemini) configure_gemini ;;
495
+ cursor) configure_cursor ;;
496
+ esac
497
+ done
498
+
499
+ log ""
500
+ log "Done. Restart or reload the selected agent(s) to pick up changes."
501
+ }
502
+
503
+ main