@alphafox/cli 0.3.3 → 0.3.4

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 (67) hide show
  1. package/README.md +4 -3
  2. package/dist/catalog/allowlist.d.ts +1 -1
  3. package/dist/catalog/allowlist.js +1 -1
  4. package/dist/catalog/generated/registry.json +338 -49
  5. package/dist/catalog/generated/schemas.json +1880 -779
  6. package/dist/catalog/omit.d.ts +6 -0
  7. package/dist/catalog/omit.js +11 -0
  8. package/dist/catalog/operations.d.ts +1 -1
  9. package/dist/catalog/operations.js +5 -2
  10. package/dist/commands/run.js +8 -1
  11. package/dist/engine-backtest/load-config.d.ts +4 -0
  12. package/dist/engine-backtest/load-config.js +44 -0
  13. package/dist/engine-backtest/parse-args.d.ts +3 -1
  14. package/dist/engine-backtest/parse-args.js +87 -3
  15. package/dist/engine-backtest/parse-axes.d.ts +3 -0
  16. package/dist/engine-backtest/parse-axes.js +167 -0
  17. package/dist/engine-backtest/persist.d.ts +60 -1
  18. package/dist/engine-backtest/persist.js +196 -1
  19. package/dist/engine-backtest/return-curve.d.ts +27 -0
  20. package/dist/engine-backtest/return-curve.js +80 -0
  21. package/dist/engine-backtest/run-command.d.ts +1 -4
  22. package/dist/engine-backtest/run-command.js +61 -45
  23. package/dist/engine-backtest/sweep-command.d.ts +13 -0
  24. package/dist/engine-backtest/sweep-command.js +749 -0
  25. package/dist/engine-backtest/sweep-kernel/caps.d.ts +26 -0
  26. package/dist/engine-backtest/sweep-kernel/caps.js +48 -0
  27. package/dist/engine-backtest/sweep-kernel/index.d.ts +13 -0
  28. package/dist/engine-backtest/sweep-kernel/index.js +34 -0
  29. package/dist/engine-backtest/sweep-kernel/plan.d.ts +22 -0
  30. package/dist/engine-backtest/sweep-kernel/plan.js +320 -0
  31. package/dist/engine-backtest/sweep-kernel/results.d.ts +72 -0
  32. package/dist/engine-backtest/sweep-kernel/results.js +150 -0
  33. package/dist/engine-backtest/sweep-kernel/session.d.ts +55 -0
  34. package/dist/engine-backtest/sweep-kernel/session.js +56 -0
  35. package/dist/engine-backtest/sweep-kernel/types.d.ts +36 -0
  36. package/dist/engine-backtest/sweep-kernel/types.js +2 -0
  37. package/dist/engine-backtest/types.d.ts +130 -0
  38. package/dist/install/wizard.js +1 -2
  39. package/dist/resolve-symbols/catalog.d.ts +3 -0
  40. package/dist/resolve-symbols/catalog.js +50 -0
  41. package/dist/resolve-symbols/errors.d.ts +18 -0
  42. package/dist/resolve-symbols/errors.js +26 -0
  43. package/dist/resolve-symbols/exchanges.d.ts +8 -0
  44. package/dist/resolve-symbols/exchanges.js +53 -0
  45. package/dist/resolve-symbols/match.d.ts +6 -0
  46. package/dist/resolve-symbols/match.js +250 -0
  47. package/dist/resolve-symbols/parse-args.d.ts +5 -0
  48. package/dist/resolve-symbols/parse-args.js +106 -0
  49. package/dist/resolve-symbols/run-command.d.ts +21 -0
  50. package/dist/resolve-symbols/run-command.js +149 -0
  51. package/dist/resolve-symbols/types.d.ts +37 -0
  52. package/dist/resolve-symbols/types.js +2 -0
  53. package/dist/version.d.ts +1 -1
  54. package/dist/version.js +1 -1
  55. package/docs/alphafox-cli-installation-guide.md +2 -2
  56. package/package.json +1 -1
  57. package/skills/account/SKILL.md +1 -1
  58. package/skills/admin/SKILL.md +1 -1
  59. package/skills/alphafox/SKILL.md +38 -0
  60. package/skills/alphafox-shared/SKILL.md +5 -1
  61. package/skills/auth/SKILL.md +1 -1
  62. package/skills/engine-backtest/SKILL.md +48 -6
  63. package/skills/exchange/SKILL.md +1 -1
  64. package/skills/market/SKILL.md +27 -4
  65. package/skills/notification/SKILL.md +1 -1
  66. package/skills/strategy/SKILL.md +7 -20
  67. package/skills/trading/SKILL.md +3 -1
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeSymbolSearchKey = normalizeSymbolSearchKey;
4
+ exports.parseCatalogSymbol = parseCatalogSymbol;
5
+ exports.indexCatalogSymbols = indexCatalogSymbols;
6
+ exports.resolveQueryAgainstCatalog = resolveQueryAgainstCatalog;
7
+ exports.levenshtein = levenshtein;
8
+ const LINEAR_PERP_PATTERN = /^([\p{L}\p{N}]+(?:-[\p{L}\p{N}]+)*)\/(USDT|USDC|USD):\2$/u;
9
+ const QUOTE_ASSETS = ["USDT", "USDC", "BUSD", "USD"];
10
+ const EXACT_REASONS = new Set([
11
+ "exact_canonical",
12
+ "exact_compact",
13
+ "exact_pair",
14
+ "exact_base",
15
+ ]);
16
+ function normalizeSymbolSearchKey(value) {
17
+ return value
18
+ .trim()
19
+ .toUpperCase()
20
+ .replace(/[^\p{L}\p{N}]/gu, "");
21
+ }
22
+ function parseCatalogSymbol(symbol) {
23
+ const trimmed = symbol.trim();
24
+ if (!trimmed)
25
+ return null;
26
+ const upper = trimmed.toUpperCase();
27
+ const linear = LINEAR_PERP_PATTERN.exec(upper);
28
+ if (linear) {
29
+ const base = linear[1];
30
+ const quote = linear[2];
31
+ const canonical = `${base}/${quote}:${quote}`;
32
+ const compact = `${base}${quote}`;
33
+ return {
34
+ symbol: canonical,
35
+ base,
36
+ quote,
37
+ pair: `${base}/${quote}`,
38
+ compact,
39
+ searchKey: normalizeSymbolSearchKey(canonical),
40
+ baseKey: normalizeSymbolSearchKey(base),
41
+ compactKey: normalizeSymbolSearchKey(compact),
42
+ };
43
+ }
44
+ const slash = upper.indexOf("/");
45
+ const colon = upper.lastIndexOf(":");
46
+ if (slash > 0) {
47
+ const base = upper.slice(0, slash);
48
+ const quote = colon > slash ? upper.slice(slash + 1, colon) : upper.slice(slash + 1);
49
+ const compact = `${base}${quote}`;
50
+ return {
51
+ symbol: upper,
52
+ base,
53
+ quote,
54
+ pair: `${base}/${quote}`,
55
+ compact,
56
+ searchKey: normalizeSymbolSearchKey(upper),
57
+ baseKey: normalizeSymbolSearchKey(base),
58
+ compactKey: normalizeSymbolSearchKey(compact),
59
+ };
60
+ }
61
+ return {
62
+ symbol: upper,
63
+ base: upper,
64
+ quote: "",
65
+ pair: upper,
66
+ compact: upper,
67
+ searchKey: normalizeSymbolSearchKey(upper),
68
+ baseKey: normalizeSymbolSearchKey(upper),
69
+ compactKey: normalizeSymbolSearchKey(upper),
70
+ };
71
+ }
72
+ function indexCatalogSymbols(symbols) {
73
+ const seen = new Set();
74
+ const indexed = [];
75
+ for (const raw of symbols) {
76
+ const parsed = parseCatalogSymbol(raw);
77
+ if (!parsed)
78
+ continue;
79
+ if (seen.has(parsed.symbol))
80
+ continue;
81
+ seen.add(parsed.symbol);
82
+ indexed.push(parsed);
83
+ }
84
+ return indexed;
85
+ }
86
+ function resolveQueryAgainstCatalog(query, catalog, limit = 8) {
87
+ const trimmed = query.trim();
88
+ const matches = rankMatches(trimmed, catalog);
89
+ const exact = matches.filter((item) => EXACT_REASONS.has(item.reason));
90
+ const close = matches.filter((item) => !EXACT_REASONS.has(item.reason));
91
+ const capped = (items) => items.slice(0, Math.max(1, limit));
92
+ if (exact.length === 1) {
93
+ return {
94
+ query: trimmed,
95
+ status: "exact",
96
+ resolved: exact[0].symbol,
97
+ needsConfirmation: false,
98
+ matches: exact,
99
+ matchCount: exact.length,
100
+ };
101
+ }
102
+ if (exact.length > 1) {
103
+ return {
104
+ query: trimmed,
105
+ status: "ambiguous",
106
+ resolved: null,
107
+ needsConfirmation: true,
108
+ matches: capped(exact),
109
+ matchCount: exact.length,
110
+ };
111
+ }
112
+ if (close.length === 1) {
113
+ return {
114
+ query: trimmed,
115
+ status: "close",
116
+ resolved: close[0].symbol,
117
+ needsConfirmation: true,
118
+ matches: close,
119
+ matchCount: 1,
120
+ };
121
+ }
122
+ if (close.length > 1) {
123
+ return {
124
+ query: trimmed,
125
+ status: "ambiguous",
126
+ resolved: null,
127
+ needsConfirmation: true,
128
+ matches: capped(close),
129
+ matchCount: close.length,
130
+ };
131
+ }
132
+ return {
133
+ query: trimmed,
134
+ status: "none",
135
+ resolved: null,
136
+ needsConfirmation: false,
137
+ matches: [],
138
+ matchCount: 0,
139
+ };
140
+ }
141
+ function rankMatches(query, catalog) {
142
+ const canonicalQuery = query.trim().toUpperCase();
143
+ const queryKey = normalizeSymbolSearchKey(query);
144
+ if (!canonicalQuery || !queryKey)
145
+ return [];
146
+ if (isQuoteAssetKey(queryKey))
147
+ return [];
148
+ const queryBaseKey = queryBaseSearchKey(canonicalQuery, queryKey);
149
+ const scored = [];
150
+ for (const item of catalog) {
151
+ const ranked = rankCatalogSymbol(item, canonicalQuery, queryKey, queryBaseKey);
152
+ if (ranked)
153
+ scored.push(ranked);
154
+ }
155
+ scored.sort((left, right) => {
156
+ if (right.score !== left.score)
157
+ return right.score - left.score;
158
+ return left.symbol.localeCompare(right.symbol);
159
+ });
160
+ return scored;
161
+ }
162
+ function rankCatalogSymbol(item, canonicalQuery, queryKey, queryBaseKey) {
163
+ if (item.symbol === canonicalQuery) {
164
+ return match(item.symbol, "exact_canonical", 100);
165
+ }
166
+ if (item.pair === canonicalQuery) {
167
+ return match(item.symbol, "exact_pair", 96);
168
+ }
169
+ if (item.compactKey === queryKey || item.searchKey === queryKey) {
170
+ return match(item.symbol, "exact_compact", 98);
171
+ }
172
+ if (item.baseKey === queryKey || item.baseKey === queryBaseKey) {
173
+ return match(item.symbol, "exact_base", 95);
174
+ }
175
+ let best = null;
176
+ const consider = (reason, score) => {
177
+ if (!best || score > best.score) {
178
+ best = match(item.symbol, reason, score);
179
+ }
180
+ };
181
+ if (queryBaseKey.length >= 2 && item.baseKey.startsWith(queryBaseKey)) {
182
+ consider("prefix", clampScore(80 - (item.baseKey.length - queryBaseKey.length)));
183
+ }
184
+ if (queryBaseKey.length >= 3 && queryBaseKey.startsWith(item.baseKey)) {
185
+ consider("prefix", clampScore(74 - (queryBaseKey.length - item.baseKey.length)));
186
+ }
187
+ if (queryBaseKey.length >= 3 && item.baseKey.includes(queryBaseKey)) {
188
+ consider("contains", 55);
189
+ }
190
+ const distance = levenshtein(queryBaseKey, item.baseKey);
191
+ if (isCloseDistance(queryBaseKey, item.baseKey, distance)) {
192
+ consider("close", clampScore(48 - distance * 8));
193
+ }
194
+ return best;
195
+ }
196
+ function queryBaseSearchKey(canonicalQuery, queryKey) {
197
+ const linear = LINEAR_PERP_PATTERN.exec(canonicalQuery);
198
+ if (linear)
199
+ return normalizeSymbolSearchKey(linear[1]);
200
+ const slash = canonicalQuery.indexOf("/");
201
+ if (slash > 0) {
202
+ return normalizeSymbolSearchKey(canonicalQuery.slice(0, slash));
203
+ }
204
+ for (const quote of QUOTE_ASSETS) {
205
+ if (queryKey.length > quote.length && queryKey.endsWith(quote)) {
206
+ return queryKey.slice(0, -quote.length);
207
+ }
208
+ }
209
+ return queryKey;
210
+ }
211
+ function isQuoteAssetKey(queryKey) {
212
+ return QUOTE_ASSETS.includes(queryKey);
213
+ }
214
+ function isCloseDistance(left, right, distance) {
215
+ const shortest = Math.min(left.length, right.length);
216
+ if (shortest < 2 || distance <= 0)
217
+ return false;
218
+ if (shortest <= 4)
219
+ return distance === 1;
220
+ return distance <= 2;
221
+ }
222
+ function match(symbol, reason, score) {
223
+ return { symbol, reason, score };
224
+ }
225
+ function clampScore(score) {
226
+ return Math.max(1, Math.min(94, score));
227
+ }
228
+ function levenshtein(left, right) {
229
+ if (left === right)
230
+ return 0;
231
+ if (left.length === 0)
232
+ return right.length;
233
+ if (right.length === 0)
234
+ return left.length;
235
+ const prev = new Array(right.length + 1);
236
+ const next = new Array(right.length + 1);
237
+ for (let j = 0; j <= right.length; j += 1)
238
+ prev[j] = j;
239
+ for (let i = 1; i <= left.length; i += 1) {
240
+ next[0] = i;
241
+ const leftCh = left.charCodeAt(i - 1);
242
+ for (let j = 1; j <= right.length; j += 1) {
243
+ const cost = leftCh === right.charCodeAt(j - 1) ? 0 : 1;
244
+ next[j] = Math.min((prev[j] ?? 0) + 1, (next[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost);
245
+ }
246
+ for (let j = 0; j <= right.length; j += 1)
247
+ prev[j] = next[j] ?? 0;
248
+ }
249
+ return prev[right.length] ?? Math.max(left.length, right.length);
250
+ }
@@ -0,0 +1,5 @@
1
+ import type { ResolveSymbolsRunArgs } from "./types";
2
+ export declare const RESOLVE_SYMBOLS_DEFAULT_LIMIT = 8;
3
+ export declare const RESOLVE_SYMBOLS_MAX_LIMIT = 25;
4
+ export declare const RESOLVE_SYMBOLS_USAGE: string[];
5
+ export declare function parseResolveSymbolsArgs(args: readonly string[]): ResolveSymbolsRunArgs;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESOLVE_SYMBOLS_USAGE = exports.RESOLVE_SYMBOLS_MAX_LIMIT = exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT = void 0;
4
+ exports.parseResolveSymbolsArgs = parseResolveSymbolsArgs;
5
+ const errors_1 = require("./errors");
6
+ const exchanges_1 = require("./exchanges");
7
+ exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT = 8;
8
+ exports.RESOLVE_SYMBOLS_MAX_LIMIT = 25;
9
+ exports.RESOLVE_SYMBOLS_USAGE = [
10
+ "alphafox resolve-symbols <query...> [--exchange binance] [--limit 8]",
11
+ "alphafox resolve-symbols --query BTC --query ETH --exchange okx",
12
+ ];
13
+ function usage(message, subtype = "invalid_args") {
14
+ throw new errors_1.ResolveSymbolsError({
15
+ type: "usage",
16
+ subtype,
17
+ message,
18
+ hint: exports.RESOLVE_SYMBOLS_USAGE[0],
19
+ status: 400,
20
+ });
21
+ }
22
+ function takeValue(args, i, flag) {
23
+ const next = args[i + 1];
24
+ if (next === undefined || next.startsWith("--")) {
25
+ usage(`Missing value for ${flag}`, "missing_flag_value");
26
+ }
27
+ return { value: next, next: i + 1 };
28
+ }
29
+ function parseResolveSymbolsArgs(args) {
30
+ const queries = [];
31
+ let exchange = exchanges_1.RESOLVE_SYMBOLS_DEFAULT_EXCHANGE;
32
+ let limit = exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT;
33
+ let help = false;
34
+ for (let i = 0; i < args.length; i += 1) {
35
+ const a = args[i];
36
+ if (a === "--help" || a === "-h") {
37
+ help = true;
38
+ continue;
39
+ }
40
+ if (!a.startsWith("--")) {
41
+ queries.push(a);
42
+ continue;
43
+ }
44
+ const eq = a.indexOf("=");
45
+ const flag = eq >= 0 ? a.slice(0, eq) : a;
46
+ const inline = eq >= 0 ? a.slice(eq + 1) : undefined;
47
+ const read = () => {
48
+ if (inline !== undefined)
49
+ return inline;
50
+ const taken = takeValue(args, i, flag);
51
+ i = taken.next;
52
+ return taken.value;
53
+ };
54
+ switch (flag) {
55
+ case "--query":
56
+ case "--symbol":
57
+ queries.push(read());
58
+ break;
59
+ case "--exchange":
60
+ exchange = read();
61
+ break;
62
+ case "--limit": {
63
+ const n = Number(read());
64
+ if (!Number.isInteger(n) || n <= 0 || n > exports.RESOLVE_SYMBOLS_MAX_LIMIT) {
65
+ usage(`--limit must be an integer 1..${exports.RESOLVE_SYMBOLS_MAX_LIMIT}`, "invalid_limit");
66
+ }
67
+ limit = n;
68
+ break;
69
+ }
70
+ default:
71
+ usage(`Unknown flag: ${flag}`, "unknown_flag");
72
+ }
73
+ }
74
+ if (help) {
75
+ return { help: true, queries: [], exchange, limit };
76
+ }
77
+ const normalizedQueries = queries.map((item) => item.trim()).filter(Boolean);
78
+ if (normalizedQueries.length === 0) {
79
+ usage("Provide at least one symbol query", "missing_query");
80
+ }
81
+ let resolvedExchange;
82
+ try {
83
+ resolvedExchange = (0, exchanges_1.resolveSymbolsExchangeId)(exchange).id;
84
+ }
85
+ catch (err) {
86
+ usage(err instanceof Error ? err.message : String(err), "invalid_exchange");
87
+ }
88
+ return {
89
+ help: false,
90
+ queries: uniqueQueries(normalizedQueries),
91
+ exchange: resolvedExchange,
92
+ limit,
93
+ };
94
+ }
95
+ function uniqueQueries(queries) {
96
+ const seen = new Set();
97
+ const out = [];
98
+ for (const query of queries) {
99
+ const key = query.toUpperCase();
100
+ if (seen.has(key))
101
+ continue;
102
+ seen.add(key);
103
+ out.push(query);
104
+ }
105
+ return out;
106
+ }
@@ -0,0 +1,21 @@
1
+ import type { ApiRequestOptions, ApiResponse } from "../http/client";
2
+ import type { ResolveSymbolsRunArgs, ResolveSymbolsSuccess } from "./types";
3
+ export interface ResolveSymbolsCliFlags {
4
+ readonly profile?: string;
5
+ readonly format: "json" | "jsonl" | "text";
6
+ readonly yes: boolean;
7
+ readonly dryRun: boolean;
8
+ readonly noInput: boolean;
9
+ readonly unsafeCustomEndpoint?: string;
10
+ readonly jq?: string;
11
+ }
12
+ export interface ResolveSymbolsRunDeps {
13
+ readonly apiRequest?: (options: ApiRequestOptions, env?: NodeJS.ProcessEnv) => Promise<ApiResponse>;
14
+ }
15
+ export declare function resolveSymbolsHelpData(): {
16
+ readonly name: string;
17
+ readonly usage: string[];
18
+ readonly notes: string[];
19
+ };
20
+ export declare function executeResolveSymbols(args: ResolveSymbolsRunArgs, flags: ResolveSymbolsCliFlags, env?: NodeJS.ProcessEnv, deps?: ResolveSymbolsRunDeps): Promise<ResolveSymbolsSuccess>;
21
+ export declare function cmdResolveSymbols(args: string[], flags: ResolveSymbolsCliFlags, env?: NodeJS.ProcessEnv, deps?: ResolveSymbolsRunDeps): Promise<number>;
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveSymbolsHelpData = resolveSymbolsHelpData;
4
+ exports.executeResolveSymbols = executeResolveSymbols;
5
+ exports.cmdResolveSymbols = cmdResolveSymbols;
6
+ const profiles_1 = require("../config/profiles");
7
+ const envelope_1 = require("../envelope");
8
+ const client_1 = require("../http/client");
9
+ const catalog_1 = require("./catalog");
10
+ const errors_1 = require("./errors");
11
+ const exchanges_1 = require("./exchanges");
12
+ const match_1 = require("./match");
13
+ const parse_args_1 = require("./parse-args");
14
+ function extractErrorMessage(json, fallback) {
15
+ if (json && typeof json === "object") {
16
+ const o = json;
17
+ if (typeof o.message === "string")
18
+ return o.message;
19
+ if (typeof o.detail === "string")
20
+ return o.detail;
21
+ if (typeof o.error === "string")
22
+ return o.error;
23
+ if (o.error && typeof o.error === "object") {
24
+ const e = o.error;
25
+ if (typeof e.message === "string")
26
+ return e.message;
27
+ }
28
+ }
29
+ return fallback || "Request failed";
30
+ }
31
+ function extractErrorCode(json) {
32
+ if (json && typeof json === "object") {
33
+ const o = json;
34
+ if (typeof o.code === "string" || typeof o.code === "number")
35
+ return o.code;
36
+ }
37
+ return undefined;
38
+ }
39
+ function resolveSymbolsHelpData() {
40
+ return {
41
+ name: "resolve-symbols",
42
+ usage: parse_args_1.RESOLVE_SYMBOLS_USAGE,
43
+ notes: [
44
+ "Resolves user-mentioned tickers against the public linear-perp catalog from market.symbols.list",
45
+ "Default --exchange is binance (binance_perp_usdt). Aliases: binance|okx|bybit|bitget|hyperliquid|aster",
46
+ "Do not guess CCXT symbols. Exact match may be used directly; a single close match needs user confirmation; multiple close matches must be chosen by the user",
47
+ "Catalog CRUD remains alphafox market symbols list --exchange <id>",
48
+ ],
49
+ };
50
+ }
51
+ async function executeResolveSymbols(args, flags, env = process.env, deps = {}) {
52
+ if (args.help) {
53
+ throw new errors_1.ResolveSymbolsError({
54
+ type: "usage",
55
+ message: "internal: help should be handled by the command wrapper",
56
+ });
57
+ }
58
+ const exchange = (0, exchanges_1.resolveSymbolsExchangeId)(args.exchange);
59
+ const profile = (0, profiles_1.resolveProfile)(flags.profile, env, {
60
+ unsafeCustomEndpoint: flags.unsafeCustomEndpoint,
61
+ });
62
+ const api = deps.apiRequest ?? client_1.apiRequest;
63
+ const res = await api({
64
+ method: "GET",
65
+ path: (0, catalog_1.marketSymbolsPath)(exchange.id),
66
+ profile,
67
+ }, env);
68
+ if (res.status >= 400) {
69
+ throw new errors_1.ResolveSymbolsError({
70
+ type: "http",
71
+ status: res.status,
72
+ message: extractErrorMessage(res.json, res.bodyText),
73
+ code: extractErrorCode(res.json),
74
+ hint: res.status === 401 || res.status === 403
75
+ ? "Run alphafox auth login. Tokens live in the OS keychain."
76
+ : undefined,
77
+ details: res.json,
78
+ });
79
+ }
80
+ const catalog = (0, match_1.indexCatalogSymbols)((0, catalog_1.extractCatalogSymbols)(res.json));
81
+ return {
82
+ exchange: exchange.id,
83
+ exchangeLabel: exchange.label,
84
+ catalogSize: catalog.length,
85
+ queries: args.queries.map((query) => (0, match_1.resolveQueryAgainstCatalog)(query, catalog, args.limit)),
86
+ };
87
+ }
88
+ async function cmdResolveSymbols(args, flags, env = process.env, deps = {}) {
89
+ let parsed;
90
+ try {
91
+ parsed = (0, parse_args_1.parseResolveSymbolsArgs)(args);
92
+ }
93
+ catch (err) {
94
+ if ((0, errors_1.isResolveSymbolsError)(err)) {
95
+ (0, envelope_1.writeError)({
96
+ type: err.type,
97
+ subtype: err.subtype,
98
+ message: err.message,
99
+ hint: err.hint,
100
+ status: err.status,
101
+ details: err.details,
102
+ }, { exitCode: err.status === 401 || err.status === 403 ? 77 : undefined });
103
+ }
104
+ throw err;
105
+ }
106
+ if (parsed.help) {
107
+ (0, envelope_1.writeSuccess)(resolveSymbolsHelpData(), {
108
+ format: flags.format,
109
+ jq: flags.jq,
110
+ });
111
+ return 0;
112
+ }
113
+ const exchange = (0, exchanges_1.resolveSymbolsExchangeId)(parsed.exchange);
114
+ if (flags.dryRun) {
115
+ (0, envelope_1.writeSuccess)({
116
+ dryRun: true,
117
+ operationId: "market.symbols.list",
118
+ method: "GET",
119
+ path: (0, catalog_1.marketSymbolsPath)(exchange.id),
120
+ exchange: exchange.id,
121
+ exchangeLabel: exchange.label,
122
+ queries: parsed.queries,
123
+ }, { format: flags.format, jq: flags.jq });
124
+ return 0;
125
+ }
126
+ try {
127
+ const result = await executeResolveSymbols(parsed, flags, env, deps);
128
+ (0, envelope_1.writeSuccess)(result, {
129
+ format: flags.format,
130
+ jq: flags.jq,
131
+ meta: { operationId: "market.symbols.list" },
132
+ });
133
+ return 0;
134
+ }
135
+ catch (err) {
136
+ if ((0, errors_1.isResolveSymbolsError)(err)) {
137
+ (0, envelope_1.writeError)({
138
+ type: err.type,
139
+ subtype: err.subtype,
140
+ message: err.message,
141
+ hint: err.hint,
142
+ status: err.status,
143
+ code: err.code,
144
+ details: err.details,
145
+ }, { exitCode: err.status === 401 || err.status === 403 ? 77 : undefined });
146
+ }
147
+ throw err;
148
+ }
149
+ }
@@ -0,0 +1,37 @@
1
+ export type ResolveSymbolsStatus = "exact" | "close" | "ambiguous" | "none";
2
+ export type ResolveSymbolsMatchReason = "exact_canonical" | "exact_compact" | "exact_pair" | "exact_base" | "prefix" | "contains" | "close";
3
+ export interface ResolveSymbolsMatch {
4
+ readonly symbol: string;
5
+ readonly reason: ResolveSymbolsMatchReason;
6
+ readonly score: number;
7
+ }
8
+ export interface ResolveSymbolsQueryResult {
9
+ readonly query: string;
10
+ readonly status: ResolveSymbolsStatus;
11
+ readonly resolved: string | null;
12
+ readonly needsConfirmation: boolean;
13
+ readonly matches: readonly ResolveSymbolsMatch[];
14
+ readonly matchCount: number;
15
+ }
16
+ export interface ResolveSymbolsSuccess {
17
+ readonly exchange: string;
18
+ readonly exchangeLabel: string;
19
+ readonly catalogSize: number;
20
+ readonly queries: readonly ResolveSymbolsQueryResult[];
21
+ }
22
+ export interface ResolveSymbolsRunArgs {
23
+ readonly help: boolean;
24
+ readonly queries: readonly string[];
25
+ readonly exchange: string;
26
+ readonly limit: number;
27
+ }
28
+ export interface CatalogSymbol {
29
+ readonly symbol: string;
30
+ readonly base: string;
31
+ readonly quote: string;
32
+ readonly pair: string;
33
+ readonly compact: string;
34
+ readonly searchKey: string;
35
+ readonly baseKey: string;
36
+ readonly compactKey: string;
37
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export declare const CLI_NAME = "alphafox";
2
2
  export declare const CLI_PACKAGE = "@alphafox/cli";
3
- export declare const CLI_VERSION = "0.3.3";
3
+ export declare const CLI_VERSION = "0.3.4";
4
4
  export { CATALOG_VERSION as CLI_CONTRACT_VERSION } from "./catalog/operations";
package/dist/version.js CHANGED
@@ -3,6 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CLI_CONTRACT_VERSION = exports.CLI_VERSION = exports.CLI_PACKAGE = exports.CLI_NAME = void 0;
4
4
  exports.CLI_NAME = "alphafox";
5
5
  exports.CLI_PACKAGE = "@alphafox/cli";
6
- exports.CLI_VERSION = "0.3.3";
6
+ exports.CLI_VERSION = "0.3.4";
7
7
  var operations_1 = require("./catalog/operations");
8
8
  Object.defineProperty(exports, "CLI_CONTRACT_VERSION", { enumerable: true, get: function () { return operations_1.CATALOG_VERSION; } });
@@ -88,8 +88,8 @@ Parse the JSON envelope: `ok === true` means success. Errors land on
88
88
  ## Step 4: Tell the user to restart
89
89
 
90
90
  Ask the user to **restart the AI tool** so the new Skills are loaded.
91
- Then they can ask the Agent to use Alphafox (auth, catalog, trading,
92
- engine-backtest, …).
91
+ Then they can ask the Agent to use Alphafox. The entry skill `alphafox` routes
92
+ to auth, market, engine-backtest, strategy, trading, and the rest.
93
93
 
94
94
  ## Human wizard (do not run this from an Agent)
95
95
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alphafox/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Alphafox CLI — Agent/Human entry for the public Application API.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-account
3
3
  description: Account, wallet, and subscription read paths.
4
- version: 0.3.3
4
+ version: 0.3.4
5
5
  ---
6
6
 
7
7
  # Account / wallet
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: alphafox-admin
3
3
  description: Admin-only operations reusing Web role authorization.
4
- version: 0.3.3
4
+ version: 0.3.4
5
5
  ---
6
6
 
7
7
  # Admin
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: alphafox
3
+ description: Alphafox CLI entry router. Use for any Alphafox request — install, login, whoami, 回测, engine backtest, strategy chat, traders, ticker/标的 resolve, market data, exchange connectors, wallet, subscriptions, notifications, or admin. Start here, then open the routed domain skill. Do not guess alphafox-engine-backtest vs alphafox-strategy from memory.
4
+ version: 0.3.4
5
+ ---
6
+
7
+ # Alphafox
8
+
9
+ This skill only routes. After choosing a row, **read that skill's `SKILL.md` and follow it**. Do not improvise domain procedures from this file.
10
+
11
+ Also read `alphafox-shared` before any CLI invocation (envelope, auth, risk, schema-first writes). Always `--format json --no-input`. Never `--token`.
12
+
13
+ Human-mentioned tickers go through `alphafox-market` (`alphafox resolve-symbols`) **before** they enter config, backtest, or writes.
14
+
15
+ ## Route
16
+
17
+ | User intent | Skill |
18
+ |---|---|
19
+ | Install, doctor, version, catalog, how to call the CLI | `alphafox-shared` |
20
+ | Login, logout, whoami, profile, staging vs production | `alphafox-auth` |
21
+ | Coin / ticker / 标的 / `BTC/USDT:USDT` / resolve a misspelled symbol | `alphafox-market` |
22
+ | Engine WASM backtest, experiment, `engine-backtest run`, persist a local run | `alphafox-engine-backtest` |
23
+ | Strategy chat, compiled strategy | `alphafox-strategy` |
24
+ | List / start / stop traders | `alphafox-trading` |
25
+ | Exchange connectors | `alphafox-exchange` |
26
+ | Account, wallet, subscription | `alphafox-account` |
27
+ | Notification channels | `alphafox-notification` |
28
+ | Admin-only operations | `alphafox-admin` |
29
+
30
+ If several rows apply, load **all** of them (typical: `alphafox-shared` + `alphafox-market` + one domain skill).
31
+
32
+ ## Do not mix these backtest paths
33
+
34
+ - Local Engine tape + wasm + optional persist → `alphafox-engine-backtest` (`alphafox engine-backtest run`, hyphen).
35
+ - Experiment catalog CRUD → same skill, underscore catalog `engine_backtest.*`.
36
+ - Chat-attached `/api/v1/backtests` (`backtests.*`) is **not** a CLI surface. Do not call it via typed commands, `schema`, or `alphafox api`.
37
+
38
+ Ambiguous “帮我回测” → `alphafox-engine-backtest`, after resolving symbols.