@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,114 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# progress.sh - run a command with a compact installer spinner.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# tools/progress.sh [--success MSG] [--failure MSG] LABEL -- COMMAND [ARGS...]
|
|
6
|
+
#
|
|
7
|
+
# Set TV_INSTALL_SPINNER=0 to disable animated output.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
SUCCESS_MSG=""
|
|
12
|
+
FAILURE_MSG=""
|
|
13
|
+
SHOW_OUTPUT_ON_FAIL=1
|
|
14
|
+
|
|
15
|
+
usage() {
|
|
16
|
+
sed -n '1,7p' "$0" | sed 's/^# \{0,1\}//'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
while [[ $# -gt 0 ]]; do
|
|
20
|
+
case "$1" in
|
|
21
|
+
--success)
|
|
22
|
+
SUCCESS_MSG="${2:-}"
|
|
23
|
+
[[ -n "$SUCCESS_MSG" ]] || { echo "--success requires a value" >&2; exit 2; }
|
|
24
|
+
shift 2
|
|
25
|
+
;;
|
|
26
|
+
--failure)
|
|
27
|
+
FAILURE_MSG="${2:-}"
|
|
28
|
+
[[ -n "$FAILURE_MSG" ]] || { echo "--failure requires a value" >&2; exit 2; }
|
|
29
|
+
shift 2
|
|
30
|
+
;;
|
|
31
|
+
--no-output-on-fail)
|
|
32
|
+
SHOW_OUTPUT_ON_FAIL=0
|
|
33
|
+
shift
|
|
34
|
+
;;
|
|
35
|
+
-h|--help)
|
|
36
|
+
usage
|
|
37
|
+
exit 0
|
|
38
|
+
;;
|
|
39
|
+
--)
|
|
40
|
+
shift
|
|
41
|
+
break
|
|
42
|
+
;;
|
|
43
|
+
-*)
|
|
44
|
+
echo "Unknown option: $1" >&2
|
|
45
|
+
usage >&2
|
|
46
|
+
exit 2
|
|
47
|
+
;;
|
|
48
|
+
*)
|
|
49
|
+
break
|
|
50
|
+
;;
|
|
51
|
+
esac
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
LABEL="${1:-}"
|
|
55
|
+
[[ -n "$LABEL" ]] || { echo "Missing progress label" >&2; usage >&2; exit 2; }
|
|
56
|
+
shift
|
|
57
|
+
|
|
58
|
+
if [[ "${1:-}" == "--" ]]; then
|
|
59
|
+
shift
|
|
60
|
+
fi
|
|
61
|
+
[[ $# -gt 0 ]] || { echo "Missing command for progress label: $LABEL" >&2; exit 2; }
|
|
62
|
+
|
|
63
|
+
SUCCESS_MSG="${SUCCESS_MSG:-$LABEL complete}"
|
|
64
|
+
FAILURE_MSG="${FAILURE_MSG:-$LABEL failed}"
|
|
65
|
+
|
|
66
|
+
tmp_log="$(mktemp "${TMPDIR:-/tmp}/tv-indicator-progress.XXXXXX")"
|
|
67
|
+
cleanup() {
|
|
68
|
+
rm -f "$tmp_log"
|
|
69
|
+
}
|
|
70
|
+
trap cleanup EXIT
|
|
71
|
+
trap '[[ -n "${pid:-}" ]] && kill "$pid" 2>/dev/null || true; exit 130' INT TERM
|
|
72
|
+
|
|
73
|
+
use_spinner=0
|
|
74
|
+
if [[ "${TV_INSTALL_SPINNER:-1}" != "0" && -t 2 ]]; then
|
|
75
|
+
use_spinner=1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
status=0
|
|
79
|
+
if (( use_spinner )); then
|
|
80
|
+
"$@" >"$tmp_log" 2>&1 &
|
|
81
|
+
pid=$!
|
|
82
|
+
frames='|/-\'
|
|
83
|
+
i=0
|
|
84
|
+
while kill -0 "$pid" 2>/dev/null; do
|
|
85
|
+
frame="${frames:$((i % ${#frames})):1}"
|
|
86
|
+
printf '\r %s %s' "$frame" "$LABEL" >&2
|
|
87
|
+
i=$((i + 1))
|
|
88
|
+
sleep 0.12
|
|
89
|
+
done
|
|
90
|
+
if wait "$pid"; then
|
|
91
|
+
status=0
|
|
92
|
+
else
|
|
93
|
+
status=$?
|
|
94
|
+
fi
|
|
95
|
+
printf '\r\033[K' >&2
|
|
96
|
+
else
|
|
97
|
+
printf ' ... %s\n' "$LABEL" >&2
|
|
98
|
+
if "$@" >"$tmp_log" 2>&1; then
|
|
99
|
+
status=0
|
|
100
|
+
else
|
|
101
|
+
status=$?
|
|
102
|
+
fi
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if (( status == 0 )); then
|
|
106
|
+
printf ' %s\n' "$SUCCESS_MSG"
|
|
107
|
+
else
|
|
108
|
+
printf ' %s\n' "$FAILURE_MSG" >&2
|
|
109
|
+
if (( SHOW_OUTPUT_ON_FAIL )) && [[ -s "$tmp_log" ]]; then
|
|
110
|
+
sed 's/^/ /' "$tmp_log" >&2
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
exit "$status"
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remove tv-indicator MCP registrations and Pi package entries from per-agent
|
|
3
|
+
# config files. Symmetric inverse of tools/import_agent_tools.sh.
|
|
4
|
+
#
|
|
5
|
+
# This script ONLY touches the three managed servers — both the new names
|
|
6
|
+
# (training-view-data, training-view-browser, training-view-agent) and the
|
|
7
|
+
# legacy names (tv-data, tv-browser, tv-agent) from before the rename — plus
|
|
8
|
+
# the tradingview-stock-data-skill Pi package entry. Unrelated entries (e.g.
|
|
9
|
+
# the user's own MCP servers) are left intact.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# tools/uninstall_agent_tools.sh --platform auto
|
|
13
|
+
# tools/uninstall_agent_tools.sh --platform all
|
|
14
|
+
# tools/uninstall_agent_tools.sh --platform pi,claude-code,codex
|
|
15
|
+
#
|
|
16
|
+
# For removing skill symlinks and generated context files, use:
|
|
17
|
+
# tools/install_skills.sh --uninstall
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
22
|
+
cd "$PROJECT_ROOT"
|
|
23
|
+
|
|
24
|
+
PLATFORM="auto"
|
|
25
|
+
DRY_RUN=0
|
|
26
|
+
|
|
27
|
+
usage() {
|
|
28
|
+
cat <<EOF
|
|
29
|
+
Usage: $(basename "$0") [options]
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--platform <names> auto | all | pi | claude-desktop | claude-code | codex | gemini | cursor
|
|
33
|
+
(comma-separated for multiple, e.g. pi,claude-code)
|
|
34
|
+
--dry-run Show what would change without modifying files
|
|
35
|
+
-h, --help Show this help
|
|
36
|
+
|
|
37
|
+
What gets removed per platform (matches both new training-view-* and legacy tv-* names):
|
|
38
|
+
pi managed training-view-* / tv-* block in .pi/config.toml,
|
|
39
|
+
tradingview-stock-data-skill from .pi/settings.json packages,
|
|
40
|
+
.pi/.tv-indicator-auto-imported sentinel
|
|
41
|
+
claude-desktop training-view-{data,browser,agent} and legacy tv-{data,browser,agent}
|
|
42
|
+
from claude_desktop_config.json mcpServers
|
|
43
|
+
claude-code same servers from project .mcp.json
|
|
44
|
+
codex managed training-view-* / tv-* block from ~/.codex/config.toml
|
|
45
|
+
gemini same servers from ~/.gemini/settings.json
|
|
46
|
+
cursor same servers from project .cursor/mcp.json
|
|
47
|
+
|
|
48
|
+
Note: skill symlinks and generated context files (CLAUDE.md, AGENTS.md, etc.)
|
|
49
|
+
are managed separately. Run: tools/install_skills.sh --uninstall
|
|
50
|
+
EOF
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
while [[ $# -gt 0 ]]; do
|
|
54
|
+
case "$1" in
|
|
55
|
+
--platform) PLATFORM="${2:-auto}"; shift 2 ;;
|
|
56
|
+
--dry-run) DRY_RUN=1; shift ;;
|
|
57
|
+
-h|--help) usage; exit 0 ;;
|
|
58
|
+
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
|
|
59
|
+
esac
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
log() { printf '%s\n' "$*"; }
|
|
63
|
+
have_cmd() { command -v "$1" >/dev/null 2>&1; }
|
|
64
|
+
|
|
65
|
+
claude_desktop_config_path() {
|
|
66
|
+
case "$(uname -s 2>/dev/null || echo unknown)" in
|
|
67
|
+
Darwin) printf '%s\n' "$HOME/Library/Application Support/Claude/claude_desktop_config.json" ;;
|
|
68
|
+
Linux) printf '%s\n' "$HOME/.config/Claude/claude_desktop_config.json" ;;
|
|
69
|
+
MINGW*|MSYS*|CYGWIN*) printf '%s\n' "${APPDATA:-$HOME/AppData/Roaming}/Claude/claude_desktop_config.json" ;;
|
|
70
|
+
*) printf '%s\n' "$HOME/.config/Claude/claude_desktop_config.json" ;;
|
|
71
|
+
esac
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Remove training-view-{data,browser,agent} and legacy tv-{data,browser,agent}
|
|
75
|
+
# from mcpServers in a JSON config file.
|
|
76
|
+
# Leaves other entries intact. If the config becomes effectively empty, removes
|
|
77
|
+
# the file (for project-local configs only; user-global configs are kept).
|
|
78
|
+
remove_mcp_json_servers() {
|
|
79
|
+
local path="$1"
|
|
80
|
+
local label="$2"
|
|
81
|
+
local delete_if_empty="${3:-0}"
|
|
82
|
+
|
|
83
|
+
if [[ ! -f "$path" ]]; then
|
|
84
|
+
log "[$label] no config at: $path (nothing to remove)"
|
|
85
|
+
return
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
89
|
+
log "[dry-run] [$label] remove training-view-* and legacy tv-* MCP servers from $path"
|
|
90
|
+
return
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
python3 - "$path" "$label" "$delete_if_empty" <<'PY'
|
|
94
|
+
import json
|
|
95
|
+
import sys
|
|
96
|
+
from pathlib import Path
|
|
97
|
+
|
|
98
|
+
path = Path(sys.argv[1]).expanduser()
|
|
99
|
+
label = sys.argv[2]
|
|
100
|
+
delete_if_empty = sys.argv[3] == "1"
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
104
|
+
except json.JSONDecodeError:
|
|
105
|
+
print(f"[{label}] config is not valid JSON, skipping: {path}")
|
|
106
|
+
sys.exit(0)
|
|
107
|
+
|
|
108
|
+
mcp = data.get("mcpServers")
|
|
109
|
+
if not isinstance(mcp, dict):
|
|
110
|
+
print(f"[{label}] no mcpServers block in {path}, nothing to remove")
|
|
111
|
+
sys.exit(0)
|
|
112
|
+
|
|
113
|
+
removed = []
|
|
114
|
+
# Both legacy (tv-*) and new (training-view-*) names are cleaned so that
|
|
115
|
+
# existing installs migrate cleanly when the user re-imports.
|
|
116
|
+
for name in (
|
|
117
|
+
"training-view-data", "training-view-browser", "training-view-agent",
|
|
118
|
+
"tv-data", "tv-browser", "tv-agent",
|
|
119
|
+
):
|
|
120
|
+
if name in mcp:
|
|
121
|
+
del mcp[name]
|
|
122
|
+
removed.append(name)
|
|
123
|
+
|
|
124
|
+
if not removed:
|
|
125
|
+
print(f"[{label}] no tv-* entries to remove in {path}")
|
|
126
|
+
sys.exit(0)
|
|
127
|
+
|
|
128
|
+
if mcp:
|
|
129
|
+
data["mcpServers"] = mcp
|
|
130
|
+
else:
|
|
131
|
+
data.pop("mcpServers", None)
|
|
132
|
+
|
|
133
|
+
if delete_if_empty and not data:
|
|
134
|
+
path.unlink()
|
|
135
|
+
print(f"[{label}] removed {', '.join(removed)} and deleted empty config: {path}")
|
|
136
|
+
else:
|
|
137
|
+
path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
138
|
+
print(f"[{label}] removed {', '.join(removed)} from {path}")
|
|
139
|
+
PY
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Strip the managed "# BEGIN tv-indicator agent tools" ... "# END ..." block
|
|
143
|
+
# from a TOML file. Leaves everything outside that block intact.
|
|
144
|
+
remove_toml_managed_block() {
|
|
145
|
+
local path="$1"
|
|
146
|
+
local label="$2"
|
|
147
|
+
local delete_if_empty="${3:-0}"
|
|
148
|
+
|
|
149
|
+
if [[ ! -f "$path" ]]; then
|
|
150
|
+
log "[$label] no config at: $path (nothing to remove)"
|
|
151
|
+
return
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
155
|
+
log "[dry-run] [$label] strip managed tv-indicator block from $path"
|
|
156
|
+
return
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
python3 - "$path" "$label" "$delete_if_empty" <<'PY'
|
|
160
|
+
import sys
|
|
161
|
+
from pathlib import Path
|
|
162
|
+
|
|
163
|
+
path = Path(sys.argv[1]).expanduser()
|
|
164
|
+
label = sys.argv[2]
|
|
165
|
+
delete_if_empty = sys.argv[3] == "1"
|
|
166
|
+
|
|
167
|
+
start = "# BEGIN tv-indicator agent tools"
|
|
168
|
+
end = "# END tv-indicator agent tools"
|
|
169
|
+
text = path.read_text(encoding="utf-8")
|
|
170
|
+
|
|
171
|
+
if start not in text or end not in text:
|
|
172
|
+
print(f"[{label}] no managed tv-indicator block in {path}")
|
|
173
|
+
sys.exit(0)
|
|
174
|
+
|
|
175
|
+
before = text.split(start, 1)[0].rstrip()
|
|
176
|
+
after = text.split(end, 1)[1].lstrip()
|
|
177
|
+
new_text = before
|
|
178
|
+
if before and after:
|
|
179
|
+
new_text = before + "\n\n" + after
|
|
180
|
+
elif after:
|
|
181
|
+
new_text = after
|
|
182
|
+
if new_text and not new_text.endswith("\n"):
|
|
183
|
+
new_text += "\n"
|
|
184
|
+
|
|
185
|
+
if delete_if_empty and not new_text.strip():
|
|
186
|
+
path.unlink()
|
|
187
|
+
print(f"[{label}] stripped managed block and deleted empty config: {path}")
|
|
188
|
+
else:
|
|
189
|
+
path.write_text(new_text, encoding="utf-8")
|
|
190
|
+
print(f"[{label}] stripped managed block from {path}")
|
|
191
|
+
PY
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# Remove the tradingview-stock-data-skill package entry from .pi/settings.json.
|
|
195
|
+
remove_pi_package_entry() {
|
|
196
|
+
local settings="$PROJECT_ROOT/.pi/settings.json"
|
|
197
|
+
local package_source="../packages/tradingview-stock-data-skill"
|
|
198
|
+
|
|
199
|
+
if [[ ! -f "$settings" ]]; then
|
|
200
|
+
log "[pi] no .pi/settings.json (nothing to remove)"
|
|
201
|
+
return
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
205
|
+
log "[dry-run] [pi] remove $package_source from .pi/settings.json packages"
|
|
206
|
+
return
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
python3 - "$settings" "$package_source" <<'PY'
|
|
210
|
+
import json
|
|
211
|
+
import sys
|
|
212
|
+
from pathlib import Path
|
|
213
|
+
|
|
214
|
+
settings_path = Path(sys.argv[1])
|
|
215
|
+
source = sys.argv[2]
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
data = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
219
|
+
except json.JSONDecodeError:
|
|
220
|
+
print("[pi] .pi/settings.json is not valid JSON, skipping")
|
|
221
|
+
sys.exit(0)
|
|
222
|
+
|
|
223
|
+
packages = data.get("packages", [])
|
|
224
|
+
if not isinstance(packages, list):
|
|
225
|
+
print("[pi] no packages array in .pi/settings.json, nothing to remove")
|
|
226
|
+
sys.exit(0)
|
|
227
|
+
|
|
228
|
+
original_len = len(packages)
|
|
229
|
+
filtered = []
|
|
230
|
+
for item in packages:
|
|
231
|
+
if item == source:
|
|
232
|
+
continue
|
|
233
|
+
if isinstance(item, dict) and item.get("source") == source:
|
|
234
|
+
continue
|
|
235
|
+
filtered.append(item)
|
|
236
|
+
|
|
237
|
+
if len(filtered) == original_len:
|
|
238
|
+
print("[pi] tradingview-stock-data-skill not present in .pi/settings.json")
|
|
239
|
+
sys.exit(0)
|
|
240
|
+
|
|
241
|
+
if filtered:
|
|
242
|
+
data["packages"] = filtered
|
|
243
|
+
else:
|
|
244
|
+
data.pop("packages", None)
|
|
245
|
+
|
|
246
|
+
if not data:
|
|
247
|
+
settings_path.unlink()
|
|
248
|
+
print("[pi] removed package entry and deleted empty .pi/settings.json")
|
|
249
|
+
else:
|
|
250
|
+
settings_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
251
|
+
print("[pi] removed tradingview-stock-data-skill from .pi/settings.json")
|
|
252
|
+
PY
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
remove_pi_sentinel() {
|
|
256
|
+
local sentinel="$PROJECT_ROOT/.pi/.tv-indicator-auto-imported"
|
|
257
|
+
if [[ ! -f "$sentinel" ]]; then
|
|
258
|
+
log "[pi] no auto-import sentinel (nothing to remove)"
|
|
259
|
+
return
|
|
260
|
+
fi
|
|
261
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
262
|
+
log "[dry-run] [pi] remove .pi/.tv-indicator-auto-imported"
|
|
263
|
+
return
|
|
264
|
+
fi
|
|
265
|
+
rm -f "$sentinel"
|
|
266
|
+
log "[pi] removed .pi/.tv-indicator-auto-imported"
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
detect_active_platform() {
|
|
270
|
+
if [[ -n "${PI_CODING_AGENT_SESSION_DIR:-}${PI_SESSION_ID:-}" ]]; then echo "pi"; return; fi
|
|
271
|
+
if [[ -n "${CLAUDE_SESSION_ID:-}${CLAUDECODE:-}" ]]; then echo "claude-code"; return; fi
|
|
272
|
+
if [[ -n "${CODEX_SANDBOX:-}${CODEX_SESSION_ID:-}" ]]; then echo "codex"; return; fi
|
|
273
|
+
if [[ -n "${GEMINI_CLI:-}${GEMINI_SESSION_ID:-}" ]]; then echo "gemini"; return; fi
|
|
274
|
+
echo "all"
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
add_unique_platform() {
|
|
278
|
+
local current="$1" item="$2"
|
|
279
|
+
case " $current " in
|
|
280
|
+
*" $item "*) printf '%s\n' "$current" ;;
|
|
281
|
+
*) printf '%s\n' "${current:+$current }$item" ;;
|
|
282
|
+
esac
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
normalize_platforms() {
|
|
286
|
+
local raw="$1"
|
|
287
|
+
local selected=""
|
|
288
|
+
IFS=',' read -ra parts <<<"$raw"
|
|
289
|
+
for entry in "${parts[@]}"; do
|
|
290
|
+
case "$entry" in
|
|
291
|
+
auto) selected="$(add_unique_platform "$selected" "$(detect_active_platform)")" ;;
|
|
292
|
+
all)
|
|
293
|
+
for mapped in pi claude-desktop claude-code codex gemini cursor; do
|
|
294
|
+
selected="$(add_unique_platform "$selected" "$mapped")"
|
|
295
|
+
done
|
|
296
|
+
;;
|
|
297
|
+
pi|claude-desktop|claude-code|codex|gemini|cursor)
|
|
298
|
+
selected="$(add_unique_platform "$selected" "$entry")"
|
|
299
|
+
;;
|
|
300
|
+
codex|opencode|aider) selected="$(add_unique_platform "$selected" "codex")" ;;
|
|
301
|
+
claude) selected="$(add_unique_platform "$selected" "claude-code")" ;;
|
|
302
|
+
"") ;;
|
|
303
|
+
*) echo "Unknown platform: $entry" >&2; exit 1 ;;
|
|
304
|
+
esac
|
|
305
|
+
done
|
|
306
|
+
echo "$selected"
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
unconfigure_pi() {
|
|
310
|
+
log "[pi] Removing Pi MCP registrations and package entry..."
|
|
311
|
+
remove_toml_managed_block "$PROJECT_ROOT/.pi/config.toml" "pi" 1
|
|
312
|
+
remove_pi_package_entry
|
|
313
|
+
remove_pi_sentinel
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
unconfigure_claude_desktop() {
|
|
317
|
+
log "[claude-desktop] Removing Claude Desktop MCP servers..."
|
|
318
|
+
remove_mcp_json_servers "$(claude_desktop_config_path)" "claude-desktop" 0
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
unconfigure_claude_code() {
|
|
322
|
+
log "[claude-code] Removing Claude Code project MCP servers..."
|
|
323
|
+
remove_mcp_json_servers "$PROJECT_ROOT/.mcp.json" "claude-code" 1
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
unconfigure_codex() {
|
|
327
|
+
log "[codex] Removing Codex MCP servers..."
|
|
328
|
+
remove_toml_managed_block "$HOME/.codex/config.toml" "codex" 0
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
unconfigure_gemini() {
|
|
332
|
+
log "[gemini] Removing Gemini MCP servers..."
|
|
333
|
+
remove_mcp_json_servers "$HOME/.gemini/settings.json" "gemini" 0
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
unconfigure_cursor() {
|
|
337
|
+
log "[cursor] Removing Cursor MCP servers..."
|
|
338
|
+
remove_mcp_json_servers "$PROJECT_ROOT/.cursor/mcp.json" "cursor" 1
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
main() {
|
|
342
|
+
local selected
|
|
343
|
+
selected="$(normalize_platforms "$PLATFORM")"
|
|
344
|
+
|
|
345
|
+
if [[ -z "$selected" ]]; then
|
|
346
|
+
log "No platforms selected."
|
|
347
|
+
exit 0
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
log "Uninstalling tv-indicator agent integrations for: $selected"
|
|
351
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
352
|
+
log "(dry-run — no files will be modified)"
|
|
353
|
+
fi
|
|
354
|
+
log ""
|
|
355
|
+
|
|
356
|
+
for p in $selected; do
|
|
357
|
+
case "$p" in
|
|
358
|
+
pi) unconfigure_pi ;;
|
|
359
|
+
claude-desktop) unconfigure_claude_desktop ;;
|
|
360
|
+
claude-code) unconfigure_claude_code ;;
|
|
361
|
+
codex) unconfigure_codex ;;
|
|
362
|
+
gemini) unconfigure_gemini ;;
|
|
363
|
+
cursor) unconfigure_cursor ;;
|
|
364
|
+
esac
|
|
365
|
+
done
|
|
366
|
+
|
|
367
|
+
log ""
|
|
368
|
+
log "Done. Restart the affected agent(s) so they reload their MCP config."
|
|
369
|
+
log "Skill symlinks and generated context files are not removed by this script."
|
|
370
|
+
log "To remove those, run: tools/install_skills.sh --uninstall"
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
main "$@"
|
package/src/cli.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { cpSync, existsSync, mkdirSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
8
|
const TOOL_NAME = "indicator";
|
|
9
9
|
const DEFAULT_PLATFORM = "auto";
|
|
10
|
-
const DEFAULT_REPO_URL = "https://github.com/cr2s367067/indicator.git";
|
|
11
10
|
|
|
12
11
|
function printHelp() {
|
|
13
12
|
console.log(`Indicator CLI
|
|
@@ -24,7 +23,7 @@ Commands:
|
|
|
24
23
|
check-skills Verify generated agent skills/prompts
|
|
25
24
|
session-check Verify TradingView browser session
|
|
26
25
|
search QUERY Search TradingView symbols
|
|
27
|
-
model-status Show
|
|
26
|
+
model-status Show server-side stock/ETF/index model status
|
|
28
27
|
model-forecast SYMBOL Read/refresh a model forecast row
|
|
29
28
|
ohlcv SYMBOL Fetch TradingView OHLCV data
|
|
30
29
|
rating SYMBOL Fetch raw TradingView technical rating
|
|
@@ -56,8 +55,7 @@ Examples:
|
|
|
56
55
|
${TOOL_NAME} cli data ohlcv TPEX:6584 --interval 1W --bars 40
|
|
57
56
|
|
|
58
57
|
Environment:
|
|
59
|
-
TV_INDICATOR_INSTALL_DIR Install
|
|
60
|
-
TV_INDICATOR_REPO_URL Git repo URL used for first-time bootstrap
|
|
58
|
+
TV_INDICATOR_INSTALL_DIR Install directory for the npm-bundled project copy
|
|
61
59
|
TV_INDICATOR_PROJECT_ROOT Existing repo path to use instead of auto-detecting
|
|
62
60
|
`);
|
|
63
61
|
}
|
|
@@ -100,14 +98,11 @@ async function findProjectRoot({ bootstrap = false } = {}) {
|
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
const installDir = defaultInstallDir();
|
|
103
|
-
const starts = [process.cwd(),
|
|
101
|
+
const starts = [process.cwd(), installDir];
|
|
104
102
|
for (const start of starts) {
|
|
105
103
|
let current = resolve(start);
|
|
106
104
|
while (current !== dirname(current)) {
|
|
107
105
|
if (isProjectRoot(current)) {
|
|
108
|
-
if (bootstrap && current === installDir) {
|
|
109
|
-
await updateProjectRoot(current);
|
|
110
|
-
}
|
|
111
106
|
return current;
|
|
112
107
|
}
|
|
113
108
|
current = dirname(current);
|
|
@@ -118,6 +113,11 @@ async function findProjectRoot({ bootstrap = false } = {}) {
|
|
|
118
113
|
return await bootstrapProjectRoot(installDir);
|
|
119
114
|
}
|
|
120
115
|
|
|
116
|
+
const packaged = packagedProjectRoot();
|
|
117
|
+
if (isProjectRoot(packaged)) {
|
|
118
|
+
return packaged;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
121
|
throw new Error(
|
|
122
122
|
`Could not find Indicator project root. Run '${TOOL_NAME} install' first or set TV_INDICATOR_PROJECT_ROOT.`,
|
|
123
123
|
);
|
|
@@ -127,6 +127,10 @@ function defaultInstallDir() {
|
|
|
127
127
|
return resolve(process.env.TV_INDICATOR_INSTALL_DIR || resolve(homedir(), ".indicator"));
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
function packagedProjectRoot() {
|
|
131
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "project");
|
|
132
|
+
}
|
|
133
|
+
|
|
130
134
|
function isProjectRoot(path) {
|
|
131
135
|
return (
|
|
132
136
|
existsSync(resolve(path, "Makefile")) &&
|
|
@@ -142,27 +146,20 @@ async function bootstrapProjectRoot(targetDir) {
|
|
|
142
146
|
`Install directory exists but is not an Indicator project root: ${targetDir}`,
|
|
143
147
|
);
|
|
144
148
|
}
|
|
145
|
-
await updateProjectRoot(targetDir);
|
|
146
149
|
return targetDir;
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
throw new Error(`Failed to clone Indicator repo into ${targetDir}`);
|
|
152
|
+
const sourceDir = packagedProjectRoot();
|
|
153
|
+
if (!isProjectRoot(sourceDir)) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
"Packaged Indicator project files are missing. Reinstall @kuandotdev/indicator or set TV_INDICATOR_PROJECT_ROOT.",
|
|
156
|
+
);
|
|
155
157
|
}
|
|
156
|
-
return targetDir;
|
|
157
|
-
}
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (code !== 0) {
|
|
164
|
-
console.warn("Could not fast-forward update the Indicator repo; continuing with the existing checkout.");
|
|
165
|
-
}
|
|
159
|
+
mkdirSync(dirname(targetDir), { recursive: true });
|
|
160
|
+
console.log(`Indicator project not found; copying npm-bundled project into ${targetDir}`);
|
|
161
|
+
cpSync(sourceDir, targetDir, { recursive: true });
|
|
162
|
+
return targetDir;
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
async function run(spec, cwd) {
|