@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.
- package/README.md +2 -7
- package/package.json +4 -1
- package/project/.cursor/rules/agent-docs.mdc +16 -0
- package/project/.cursor/rules/api-credential-storage.mdc +16 -0
- package/project/.cursor/rules/pinescript-v6.mdc +16 -0
- package/project/.cursor/rules/stock-model-forecast.mdc +16 -0
- package/project/.cursor/rules/system-prompt-injection.mdc +16 -0
- package/project/.cursor/rules/system-prompt-updater.mdc +16 -0
- package/project/.cursor/rules/tradingview-stock-data.mdc +16 -0
- package/project/.env.example +44 -0
- package/project/.npm-packaged-project +1 -0
- package/project/.pi/APPEND_SYSTEM.md +338 -0
- package/project/.pi/settings.json +8 -0
- package/project/AGENTS.md +538 -0
- package/project/CLAUDE.md +538 -0
- package/project/GEMINI.md +538 -0
- package/project/Makefile +488 -0
- package/project/README.md +419 -0
- package/project/conda-env-active.sh +98 -0
- package/project/conda-env-deactive.sh +42 -0
- package/project/docs/agent-install.md +446 -0
- package/project/docs/agent-skill-directory.md +222 -0
- package/project/docs/integration.html +271 -0
- package/project/packages/indicator/README.md +39 -0
- package/project/packages/indicator/package.json +40 -0
- package/project/packages/indicator/scripts/build-project-snapshot.js +57 -0
- package/project/packages/indicator/src/cli.js +368 -0
- package/project/packages/tradingview-stock-data-skill/README.md +112 -0
- package/project/packages/tradingview-stock-data-skill/extensions/stock-prompt-injector.ts +121 -0
- package/project/packages/tradingview-stock-data-skill/package.json +35 -0
- package/project/packages/tradingview-stock-data-skill/scripts/postinstall.sh +73 -0
- package/project/packages/tradingview-stock-data-skill/skills/tradingview-stock-data/SKILL.md +241 -0
- package/project/pyproject.toml +68 -0
- package/project/screenshots/.gitkeep +0 -0
- package/project/scripts/indicators/example_rsi_bands.pine +27 -0
- package/project/scripts/indicators/tsla_levels.pine +57 -0
- package/project/skills/agent-docs/SKILL.md +56 -0
- package/project/skills/api-credential-storage/SKILL.md +83 -0
- package/project/skills/api-credential-storage/scripts/upsert_env.py +151 -0
- package/project/skills/pinescript-v6/SKILL.md +129 -0
- package/project/skills/pinescript-v6/reference/built-ins.md +219 -0
- package/project/skills/pinescript-v6/reference/templates/alert-webhook.pine +76 -0
- package/project/skills/pinescript-v6/reference/templates/indicator.pine +48 -0
- package/project/skills/pinescript-v6/reference/templates/strategy.pine +50 -0
- package/project/skills/pinescript-v6/reference/v5-to-v6-migration.md +102 -0
- package/project/skills/pinescript-v6/reference/v6-language.md +202 -0
- package/project/skills/stock-model-forecast/SKILL.md +192 -0
- package/project/skills/system-prompt-injection/CUSTOM_SYSTEM_PROMPT.md +333 -0
- package/project/skills/system-prompt-injection/DEFAULT_SYSTEM_PROMPT.md +327 -0
- package/project/skills/system-prompt-injection/SKILL.md +90 -0
- package/project/skills/system-prompt-injection/SYSTEM_PROMPT.md +23 -0
- package/project/skills/system-prompt-updater/SKILL.md +82 -0
- package/project/skills/system-prompt-updater/scripts/system_prompt_update.sh +106 -0
- package/project/skills/tradingview-stock-data/SKILL.md +272 -0
- package/project/src/tv_indicator/__init__.py +0 -0
- package/project/src/tv_indicator/browser/__init__.py +0 -0
- package/project/src/tv_indicator/browser/automation.py +541 -0
- package/project/src/tv_indicator/browser/selectors.py +70 -0
- package/project/src/tv_indicator/cli/__init__.py +0 -0
- package/project/src/tv_indicator/cli/browser_cmds.py +92 -0
- package/project/src/tv_indicator/cli/data_cmds.py +178 -0
- package/project/src/tv_indicator/cli/main.py +56 -0
- package/project/src/tv_indicator/cli/model_cmds.py +255 -0
- package/project/src/tv_indicator/cli/pine_cmds.py +140 -0
- package/project/src/tv_indicator/config.py +98 -0
- package/project/src/tv_indicator/data/__init__.py +0 -0
- package/project/src/tv_indicator/data/client.py +187 -0
- package/project/src/tv_indicator/data/screener.py +268 -0
- package/project/src/tv_indicator/mcp/__init__.py +0 -0
- package/project/src/tv_indicator/mcp/agent_server.py +398 -0
- package/project/src/tv_indicator/mcp/browser_server.py +133 -0
- package/project/src/tv_indicator/mcp/data_server.py +239 -0
- package/project/src/tv_indicator/model/__init__.py +19 -0
- package/project/src/tv_indicator/model/forecast.py +693 -0
- package/project/tools/import_agent_tools.sh +503 -0
- package/project/tools/install_skills.sh +673 -0
- package/project/tools/interactive_install.sh +917 -0
- package/project/tools/progress.sh +114 -0
- package/project/tools/uninstall_agent_tools.sh +373 -0
- 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
|