@aisa-one/cli 0.2.1 → 0.2.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 (52) hide show
  1. package/CHANGELOG.md +144 -1
  2. package/README.md +249 -102
  3. package/dist/api.d.ts +46 -1
  4. package/dist/api.js +77 -8
  5. package/dist/cache.d.ts +18 -0
  6. package/dist/cache.js +65 -0
  7. package/dist/catalog.d.ts +90 -0
  8. package/dist/catalog.js +101 -0
  9. package/dist/commands/account.d.ts +4 -1
  10. package/dist/commands/account.js +36 -6
  11. package/dist/commands/api.d.ts +18 -4
  12. package/dist/commands/api.js +357 -24
  13. package/dist/commands/cacheCmd.d.ts +2 -0
  14. package/dist/commands/cacheCmd.js +10 -0
  15. package/dist/commands/completionCmd.d.ts +7 -0
  16. package/dist/commands/completionCmd.js +37 -0
  17. package/dist/commands/configCmd.js +19 -1
  18. package/dist/commands/finance.d.ts +4 -0
  19. package/dist/commands/finance.js +212 -29
  20. package/dist/commands/models.js +6 -0
  21. package/dist/commands/run.d.ts +21 -0
  22. package/dist/commands/run.js +113 -6
  23. package/dist/commands/search.js +8 -3
  24. package/dist/commands/skills.d.ts +6 -2
  25. package/dist/commands/skills.js +189 -134
  26. package/dist/commands/twitter.js +2 -3
  27. package/dist/commands/video.d.ts +40 -0
  28. package/dist/commands/video.js +272 -55
  29. package/dist/completion.d.ts +26 -0
  30. package/dist/completion.js +293 -0
  31. package/dist/constants.d.ts +55 -5
  32. package/dist/constants.js +136 -11
  33. package/dist/index.js +86 -23
  34. package/dist/skills-registry.d.ts +64 -0
  35. package/dist/skills-registry.js +219 -0
  36. package/dist/types.d.ts +32 -1
  37. package/dist/utils/file.d.ts +5 -1
  38. package/dist/utils/file.js +5 -1
  39. package/dist/utils/pool.d.ts +8 -0
  40. package/dist/utils/pool.js +27 -0
  41. package/package.json +9 -3
  42. package/dist/agent/env-setup.d.ts +0 -5
  43. package/dist/agent/env-setup.js +0 -49
  44. package/dist/agent/index-client.d.ts +0 -7
  45. package/dist/agent/index-client.js +0 -22
  46. package/dist/agent/installer.d.ts +0 -19
  47. package/dist/agent/installer.js +0 -70
  48. package/dist/agent/setup-python.d.ts +0 -15
  49. package/dist/agent/setup-python.js +0 -51
  50. package/dist/commands/agent.d.ts +0 -37
  51. package/dist/commands/agent.js +0 -129
  52. package/tests/streaming.test.ts +0 -73
@@ -1,15 +1,65 @@
1
- export declare const VERSION = "0.2.0";
1
+ export declare const VERSION = "0.2.3";
2
+ /** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
2
3
  export declare const BASE_URL = "https://api.aisa.one";
3
- export declare const CLI_BASE_URL = "https://api.aisa.one/v1";
4
- export declare const APIS_BASE_URL = "https://api.aisa.one/apis/v1";
5
4
  export declare const ENV_VAR_NAME = "AISA_API_KEY";
6
5
  export declare const MCP_URL = "https://docs.aisa.one/mcp";
6
+ /**
7
+ * First path segment of every route the LLM gateway serves under /v1 (plus
8
+ * /v1beta for Gemini). Everything else is an integration API under /apis/v1.
9
+ *
10
+ * This list is deliberately the whitelist rather than the provider slugs: the
11
+ * gateway routes are hardcoded server-side and change about once a year, while
12
+ * provider slugs are data that operations add and remove continuously. Keeping
13
+ * the whitelist on the slow-moving side means a newly added provider works
14
+ * without a CLI release.
15
+ */
16
+ export declare const LLM_ROUTE_PREFIXES: readonly string[];
7
17
  export declare const AGENT_DIRS: Record<string, string>;
8
18
  export declare const MCP_CONFIGS: Record<string, {
9
19
  path: string;
10
20
  key: string;
11
21
  }>;
12
- export declare const API_CATEGORIES: readonly ["llm", "search", "finance", "twitter", "video", "scholar"];
13
- export type ApiCategory = (typeof API_CATEGORIES)[number];
22
+ /**
23
+ * Ticker symbol → CoinGecko coin id.
24
+ *
25
+ * CoinGecko keys everything by id ("bitcoin"), not symbol ("BTC"), and dozens
26
+ * of unrelated tokens share popular symbols — resolving by symbol alone would
27
+ * happily return the price of the wrong coin. This table covers the symbols
28
+ * people actually type; anything else falls back to an explicit lookup.
29
+ */
30
+ export declare const COINGECKO_IDS: Record<string, string>;
31
+ /**
32
+ * Models routed to /v1/video/generations, and the request body each one wants.
33
+ *
34
+ * The gateway only rewrites the top-level `model` and passes the rest through,
35
+ * so the body shape belongs to whichever vendor owns the model — a Bailian-style
36
+ * `{input, parameters}` sent to Seedance is rejected outright with a 400.
37
+ *
38
+ * `requiresMedia` records the `input.media[].type` an image/clip-driven model
39
+ * needs. Without it the upstream accepts the submission and only fails a minute
40
+ * later, after billing, so the CLI refuses up front instead.
41
+ */
42
+ export type VideoFamily = "bailian" | "seedance";
43
+ export interface VideoModelSpec {
44
+ family: VideoFamily;
45
+ requiresMedia?: MediaType;
46
+ }
47
+ /** Accepted values for `input.media[].type` (enumerated by the upstream error). */
48
+ export declare const MEDIA_TYPES: readonly ["first_frame", "last_frame", "driving_audio", "first_clip"];
49
+ export type MediaType = (typeof MEDIA_TYPES)[number];
50
+ export declare const VIDEO_MODELS: Record<string, VideoModelSpec>;
51
+ export declare const DEFAULT_VIDEO_MODEL = "wan2.7-t2v";
52
+ /** Model families are matched by prefix so new revisions work without a release. */
53
+ export declare function videoModelSpec(model: string): VideoModelSpec;
54
+ /**
55
+ * Client-side grouping of integration providers.
56
+ *
57
+ * The catalog returns no description or tags — `buildCatalogItem` never
58
+ * populates them — so there is nothing server-side to filter on. Entries are
59
+ * matched by prefix, which keeps the polymarket-* family together.
60
+ */
61
+ export declare const PROVIDER_CATEGORIES: Record<string, string[]>;
62
+ export declare function categoryOf(providerId: string): string;
63
+ export declare const API_CATEGORIES: readonly [...string[], "other"];
14
64
  export declare const MODEL_PROVIDERS: readonly ["openai", "anthropic", "google", "deepseek", "xai", "moonshot", "alibaba", "bytedance"];
15
65
  export type ModelProvider = (typeof MODEL_PROVIDERS)[number];
package/dist/constants.js CHANGED
@@ -1,9 +1,31 @@
1
- export const VERSION = "0.2.0";
1
+ export const VERSION = "0.2.3";
2
+ /** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
2
3
  export const BASE_URL = "https://api.aisa.one";
3
- export const CLI_BASE_URL = `${BASE_URL}/v1`;
4
- export const APIS_BASE_URL = `${BASE_URL}/apis/v1`;
5
4
  export const ENV_VAR_NAME = "AISA_API_KEY";
6
5
  export const MCP_URL = "https://docs.aisa.one/mcp";
6
+ /**
7
+ * First path segment of every route the LLM gateway serves under /v1 (plus
8
+ * /v1beta for Gemini). Everything else is an integration API under /apis/v1.
9
+ *
10
+ * This list is deliberately the whitelist rather than the provider slugs: the
11
+ * gateway routes are hardcoded server-side and change about once a year, while
12
+ * provider slugs are data that operations add and remove continuously. Keeping
13
+ * the whitelist on the slow-moving side means a newly added provider works
14
+ * without a CLI release.
15
+ */
16
+ export const LLM_ROUTE_PREFIXES = [
17
+ "chat",
18
+ "responses",
19
+ "embeddings",
20
+ "messages",
21
+ "models",
22
+ "rerank",
23
+ "classify",
24
+ "images",
25
+ "credits",
26
+ "video",
27
+ "v1beta",
28
+ ];
7
29
  export const AGENT_DIRS = {
8
30
  claude: "~/.claude/skills/",
9
31
  cursor: "~/.cursor/skills/",
@@ -24,14 +46,117 @@ export const MCP_CONFIGS = {
24
46
  key: "mcpServers",
25
47
  },
26
48
  };
27
- export const API_CATEGORIES = [
28
- "llm",
29
- "search",
30
- "finance",
31
- "twitter",
32
- "video",
33
- "scholar",
34
- ];
49
+ /**
50
+ * Ticker symbol → CoinGecko coin id.
51
+ *
52
+ * CoinGecko keys everything by id ("bitcoin"), not symbol ("BTC"), and dozens
53
+ * of unrelated tokens share popular symbols — resolving by symbol alone would
54
+ * happily return the price of the wrong coin. This table covers the symbols
55
+ * people actually type; anything else falls back to an explicit lookup.
56
+ */
57
+ export const COINGECKO_IDS = {
58
+ btc: "bitcoin",
59
+ eth: "ethereum",
60
+ usdt: "tether",
61
+ bnb: "binancecoin",
62
+ sol: "solana",
63
+ usdc: "usd-coin",
64
+ xrp: "ripple",
65
+ doge: "dogecoin",
66
+ ada: "cardano",
67
+ trx: "tron",
68
+ avax: "avalanche-2",
69
+ shib: "shiba-inu",
70
+ dot: "polkadot",
71
+ link: "chainlink",
72
+ bch: "bitcoin-cash",
73
+ near: "near",
74
+ matic: "matic-network",
75
+ ltc: "litecoin",
76
+ icp: "internet-computer",
77
+ uni: "uniswap",
78
+ leo: "leo-token",
79
+ dai: "dai",
80
+ etc: "ethereum-classic",
81
+ apt: "aptos",
82
+ render: "render-token",
83
+ hbar: "hedera-hashgraph",
84
+ fil: "filecoin",
85
+ arb: "arbitrum",
86
+ atom: "cosmos",
87
+ imx: "immutable-x",
88
+ op: "optimism",
89
+ vet: "vechain",
90
+ inj: "injective-protocol",
91
+ sui: "sui",
92
+ ton: "the-open-network",
93
+ pepe: "pepe",
94
+ tao: "bittensor",
95
+ sei: "sei-network",
96
+ aave: "aave",
97
+ algo: "algorand",
98
+ xlm: "stellar",
99
+ cro: "crypto-com-chain",
100
+ mnt: "mantle",
101
+ wld: "worldcoin-wld",
102
+ };
103
+ /** Accepted values for `input.media[].type` (enumerated by the upstream error). */
104
+ export const MEDIA_TYPES = ["first_frame", "last_frame", "driving_audio", "first_clip"];
105
+ export const VIDEO_MODELS = {
106
+ "wan2.7-t2v": { family: "bailian" },
107
+ "wan2.7-i2v": { family: "bailian", requiresMedia: "first_frame" },
108
+ "wan2.7-r2v": { family: "bailian", requiresMedia: "first_clip" },
109
+ "happyhorse-1.1-t2v": { family: "bailian" },
110
+ "happyhorse-1.1-i2v": { family: "bailian", requiresMedia: "first_frame" },
111
+ "happyhorse-1.1-r2v": { family: "bailian", requiresMedia: "first_clip" },
112
+ "dreamina-seedance-2-0-260128": { family: "seedance" },
113
+ "dreamina-seedance-2-0-fast-260128": { family: "seedance" },
114
+ };
115
+ export const DEFAULT_VIDEO_MODEL = "wan2.7-t2v";
116
+ /** Model families are matched by prefix so new revisions work without a release. */
117
+ export function videoModelSpec(model) {
118
+ if (VIDEO_MODELS[model])
119
+ return VIDEO_MODELS[model];
120
+ const family = model.startsWith("dreamina-seedance") ? "seedance" : "bailian";
121
+ if (/-i2v\b|-i2v$/.test(model))
122
+ return { family, requiresMedia: "first_frame" };
123
+ if (/-r2v\b|-r2v$/.test(model))
124
+ return { family, requiresMedia: "first_clip" };
125
+ return { family };
126
+ }
127
+ /**
128
+ * Client-side grouping of integration providers.
129
+ *
130
+ * The catalog returns no description or tags — `buildCatalogItem` never
131
+ * populates them — so there is nothing server-side to filter on. Entries are
132
+ * matched by prefix, which keeps the polymarket-* family together.
133
+ */
134
+ export const PROVIDER_CATEGORIES = {
135
+ finance: ["financial", "fred", "edinet", "coingecko", "polymarket", "kalshi"],
136
+ search: [
137
+ "search",
138
+ "tavily",
139
+ "brave",
140
+ "exa",
141
+ "perplexity",
142
+ "querit",
143
+ "parallel",
144
+ "dataforseo",
145
+ "jina",
146
+ "scholar",
147
+ ],
148
+ social: ["twitter", "aisa-twitter", "youtube", "scrape-creators", "waveinflu"],
149
+ productivity: ["agentmail", "apollo", "composio"],
150
+ };
151
+ export function categoryOf(providerId) {
152
+ for (const [category, prefixes] of Object.entries(PROVIDER_CATEGORIES)) {
153
+ if (prefixes.some((p) => providerId === p || providerId.startsWith(`${p}-`) || providerId.startsWith(`${p}.`))) {
154
+ return category;
155
+ }
156
+ }
157
+ return "other";
158
+ }
159
+ export const API_CATEGORIES = [...Object.keys(PROVIDER_CATEGORIES), "other"];
35
160
  export const MODEL_PROVIDERS = [
36
161
  "openai",
37
162
  "anthropic",
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- import { VERSION } from "./constants.js";
3
+ import { VERSION, DEFAULT_VIDEO_MODEL } from "./constants.js";
4
4
  // Auth
5
5
  import { loginAction, logoutAction, whoamiAction } from "./commands/auth.js";
6
6
  // Account
@@ -27,6 +27,10 @@ import { skillsListAction, skillsSearchAction, skillsShowAction, skillsInstallAc
27
27
  import { mcpSetupAction, mcpStatusAction } from "./commands/mcp.js";
28
28
  // Config
29
29
  import { configSetAction, configGetAction, configListAction, configResetAction } from "./commands/configCmd.js";
30
+ // Cache
31
+ import { cacheClearAction, cachePathAction } from "./commands/cacheCmd.js";
32
+ // Completion
33
+ import { completionAction, completeAction } from "./commands/completionCmd.js";
30
34
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
35
  function wrap(fn) {
32
36
  return (...args) => {
@@ -58,34 +62,48 @@ program
58
62
  // ── Account ──
59
63
  program
60
64
  .command("balance")
61
- .description("Show credit balance (WIP)")
65
+ .description("Show credit balance")
66
+ .option("--json", "Output raw JSON")
62
67
  .action(wrap(balanceAction));
63
68
  program
64
69
  .command("usage")
65
- .description("Show usage history (WIP)")
70
+ .description("Show usage history (awaiting gateway support)")
66
71
  .option("--limit <n>", "Max records")
67
72
  .option("--days <n>", "Lookback days")
68
73
  .action(wrap(usageAction));
69
74
  // ── API ──
70
- const api = program.command("api").description("Discover and inspect APIs (WIP)");
75
+ const api = program.command("api").description("Discover and inspect APIs");
71
76
  api
72
77
  .command("list")
73
- .description("List available API endpoints (WIP)")
74
- .option("--category <cat>", "Filter by category (llm, search, finance, twitter, video, scholar)")
78
+ .description("List available APIs")
79
+ .option("--category <cat>", "Filter by category (client-side grouping): finance, search, social, productivity, other")
80
+ .option("--health", "Include provider health status")
81
+ .option("--json", "Output raw JSON")
82
+ .option("--refresh", "Bypass the cached catalog")
75
83
  .action(wrap(apiListAction));
76
84
  api
77
85
  .command("search <query>")
78
- .description("Search APIs by keyword (WIP)")
79
- .option("--limit <n>", "Max results")
86
+ .description("Search APIs and endpoints by keyword")
87
+ .option("--provider <id>", "Restrict to one API")
88
+ .option("--limit <n>", "Max results", "20")
89
+ .option("--json", "Output raw JSON")
90
+ .option("--refresh", "Bypass the cached catalog")
80
91
  .action(wrap(apiSearchAction));
81
92
  api
82
- .command("show <slug> [path]")
83
- .description("Show API endpoint details (WIP)")
93
+ .command("show <api> [path]")
94
+ .description("Show an API's endpoints, or one endpoint's details")
95
+ .option("--all", "Show every endpoint instead of the first 40")
96
+ .option("--group", "Group by the provider's raw endpoint groups")
97
+ .option("--health", "Include provider health status")
98
+ .option("--json", "Output raw JSON")
99
+ .option("--refresh", "Bypass the cached catalog")
84
100
  .action(wrap(apiShowAction));
85
101
  api
86
102
  .command("code <slug> <path>")
87
- .description("Generate code snippet (WIP)")
88
- .option("--lang <language>", "Language: typescript, python, curl", "typescript")
103
+ .description("Generate a request snippet for an endpoint")
104
+ .option("--lang <language>", "Language: curl, python, node, typescript", "curl")
105
+ .option("--method <method>", "HTTP method (the catalog's method is advisory)", "GET")
106
+ .option("--refresh", "Bypass the cached catalog")
89
107
  .action(wrap(apiCodeAction));
90
108
  // ── Run ──
91
109
  program
@@ -96,12 +114,18 @@ program
96
114
  .option("--method <method>", "HTTP method")
97
115
  .option("--raw", "Raw JSON output")
98
116
  .option("--stream", "Stream response")
117
+ .option("--domain", "Force the integration API base (/apis/v1) — the default")
118
+ .option("--llm", "Force the LLM gateway base (/v1)")
119
+ .option("--show-cost", "Print the billing headers the gateway reported (stderr)")
99
120
  .action((slug, path, opts) => wrap(runAction)(slug, path, {
100
121
  q: opts.query,
101
122
  d: opts.data,
102
123
  method: opts.method,
103
124
  raw: opts.raw,
104
125
  stream: opts.stream,
126
+ llm: opts.llm,
127
+ domain: opts.domain,
128
+ showCost: opts.showCost,
105
129
  }));
106
130
  // ── Chat (LLM Gateway) ──
107
131
  program
@@ -129,7 +153,7 @@ models
129
153
  program
130
154
  .command("web-search <query>")
131
155
  .description("Search the web")
132
- .option("--type <type>", "Search type: smart, full, youtube, scholar, tavily", "smart")
156
+ .option("--type <type>", "Search type: tavily, youtube, scholar, smart (degraded), full (degraded)", "tavily")
133
157
  .option("--limit <n>", "Max results")
134
158
  .option("--raw", "Raw JSON output")
135
159
  .action(wrap(webSearchAction));
@@ -149,13 +173,17 @@ program
149
173
  program
150
174
  .command("crypto <symbol>")
151
175
  .description("Look up crypto price")
152
- .option("--period <period>", "Time period: current, 1d, 7d, 30d, 1y")
176
+ .option("--period <period>", "Time period: current, 1d, 7d, 30d, 90d, 1y")
177
+ .option("--id <coingecko-id>", "Use an exact CoinGecko id instead of resolving the symbol")
178
+ .option("--source <source>", "Data source: coingecko (default) or financial")
153
179
  .option("--raw", "Raw JSON output")
154
180
  .action(wrap(cryptoAction));
155
181
  program
156
182
  .command("screener")
157
183
  .description("Screen stocks by criteria")
158
- .option("--sector <sector>", "Filter by sector")
184
+ .option("--sector <sector>", "Filter by GICS sector (e.g. 'Information Technology')")
185
+ .option("--min-market-cap <n>", "Minimum market cap in USD")
186
+ .option("--filter <f...>", "Extra filter as field:operator:value (e.g. market_cap:gt:1e12)")
159
187
  .option("--limit <n>", "Max results")
160
188
  .option("--raw", "Raw JSON output")
161
189
  .action(wrap(screenerAction));
@@ -387,14 +415,20 @@ const video = program.command("video").description("AI video generation");
387
415
  video
388
416
  .command("create <prompt>")
389
417
  .description("Create a video generation task")
390
- .option("--model <model>", "Generation model")
418
+ .option("--model <model>", `Generation model (default: ${DEFAULT_VIDEO_MODEL})`)
419
+ .option("--image <url...>", "Source image for i2v models (shorthand for --media first_frame=<url>)")
420
+ .option("--media <type=url...>", "Source media: first_frame, last_frame, driving_audio, first_clip")
421
+ .option("--resolution <res>", "Resolution, e.g. 720P or 1080P", "720P")
422
+ .option("--duration <seconds>", "Clip duration in seconds", "5")
423
+ .option("--body <json>", "Send this request body verbatim (escape hatch for unmodelled vendors)")
391
424
  .option("--wait", "Wait for completion")
392
- .option("--output <path>", "Download output path")
425
+ .option("--output <path>", "Download the finished video to this path (implies --wait)")
393
426
  .option("--raw", "Raw JSON output")
394
- .action(wrap(videoCreateAction));
427
+ .action((prompt, opts) => wrap(videoCreateAction)(prompt, { ...opts, wait: Boolean(opts.wait || opts.output) }));
395
428
  video
396
429
  .command("status <task-id>")
397
430
  .description("Check video task status")
431
+ .option("--output <path>", "Download the finished video to this path")
398
432
  .option("--raw", "Raw JSON output")
399
433
  .action(wrap(videoStatusAction));
400
434
  // ── Skills ──
@@ -402,13 +436,15 @@ const skills = program.command("skills").description("Browse and manage agent sk
402
436
  skills
403
437
  .command("list")
404
438
  .description("List available skills")
405
- .option("--category <cat>", "Filter by category")
439
+ .option("--category <cat>", "Filter by category (ai-models, financial, marketing, ...)")
406
440
  .option("--limit <n>", "Max results")
441
+ .option("--refresh", "Bypass the cached skill index")
407
442
  .action(wrap(skillsListAction));
408
443
  skills
409
444
  .command("search <query>")
410
445
  .description("Search skills by keyword")
411
446
  .option("--limit <n>", "Max results")
447
+ .option("--refresh", "Bypass the cached skill index")
412
448
  .action(wrap(skillsSearchAction));
413
449
  skills
414
450
  .command("show <slug>")
@@ -418,11 +454,13 @@ skills
418
454
  .command("install <slug>")
419
455
  .description("Install a skill to agent directories")
420
456
  .option("--agent <agent>", "Target agent: claude, cursor, copilot, windsurf, codex, gemini, openclaw, all")
457
+ .option("--force", "Overwrite a directory holding a different skill")
421
458
  .action(wrap(skillsInstallAction));
422
459
  skills
423
460
  .command("remove <slug>")
424
461
  .description("Remove an installed skill")
425
462
  .option("--agent <agent>", "Target agent")
463
+ .option("--force", "Remove even if the directory holds a different skill")
426
464
  .action(skillsRemoveAction);
427
465
  skills
428
466
  .command("init <name>")
@@ -462,13 +500,38 @@ configCmd
462
500
  // ── Top-level aliases ──
463
501
  program
464
502
  .command("search <query>")
465
- .description("Search APIs (alias for 'api search') (WIP)")
466
- .option("--limit <n>", "Max results")
503
+ .description("Search APIs (alias for 'api search')")
504
+ .option("--provider <id>", "Restrict to one API")
505
+ .option("--limit <n>", "Max results", "20")
506
+ .option("--json", "Output raw JSON")
507
+ .option("--refresh", "Bypass the cached catalog")
467
508
  .action(wrap(apiSearchAction));
468
509
  program
469
510
  .command("code <slug> <path>")
470
- .description("Generate code (alias for 'api code') (WIP)")
471
- .option("--lang <language>", "Language", "typescript")
511
+ .description("Generate a request snippet (alias for 'api code')")
512
+ .option("--lang <language>", "Language: curl, python, node, typescript", "curl")
513
+ .option("--method <method>", "HTTP method")
472
514
  .action(wrap(apiCodeAction));
515
+ const cache = program.command("cache").description("Manage the local catalog and skills cache");
516
+ cache
517
+ .command("clear")
518
+ .description("Delete all cached catalog and skills data")
519
+ .action(cacheClearAction);
520
+ cache
521
+ .command("path")
522
+ .description("Print the cache directory")
523
+ .action(cachePathAction);
524
+ // ── Shell completion ──
525
+ program
526
+ .command("completion [shell]")
527
+ .description("Print a shell completion script (bash, zsh, fish)")
528
+ .action(completionAction);
529
+ // Called by the generated scripts on every Tab press. Hidden, and tolerant of
530
+ // whatever half-typed input the shell hands it.
531
+ program
532
+ .command("__complete [words...]", { hidden: true })
533
+ .allowUnknownOption()
534
+ .allowExcessArguments()
535
+ .action((words = []) => completeAction(program, words));
473
536
  // ── Parse ──
474
537
  program.parse();
@@ -0,0 +1,64 @@
1
+ /**
2
+ * GitHub-backed skill registry.
3
+ *
4
+ * The repo nests skills two levels deep (`<category>/<name>/SKILL.md`), so the
5
+ * canonical slug is the repo-relative directory. Everything here is driven off
6
+ * a single recursive tree fetch: api.github.com allows only 60 unauthenticated
7
+ * requests per hour per IP, and the previous per-command fetching burned
8
+ * several of those on every invocation.
9
+ */
10
+ export declare const SKILLS_REPO = "AIsa-team/agent-skills";
11
+ export declare const GH_RAW = "https://raw.githubusercontent.com/AIsa-team/agent-skills/main";
12
+ export interface SkillBlob {
13
+ path: string;
14
+ sha: string;
15
+ }
16
+ export interface SkillIndex {
17
+ /** Canonical slugs (`<category>/<name>`), sorted. */
18
+ slugs: string[];
19
+ /** All blobs under each slug, including SKILL.md. */
20
+ blobs: Record<string, SkillBlob[]>;
21
+ }
22
+ export interface SkillInfo {
23
+ slug: string;
24
+ name: string;
25
+ description: string;
26
+ emoji: string;
27
+ }
28
+ /**
29
+ * Fetch (or reuse) the skill index. Memoized per process on top of a 1h disk
30
+ * cache with ETag revalidation, so repeated commands cost at most one
31
+ * conditional request.
32
+ */
33
+ export declare function getSkillIndex(options?: {
34
+ refresh?: boolean;
35
+ }): Promise<SkillIndex>;
36
+ /** Last path segment — what the skill is called once installed. */
37
+ export declare function leafName(slug: string): string;
38
+ /**
39
+ * Accept either a canonical `<category>/<name>` slug or a bare name. A bare
40
+ * name that matches several skills is an error rather than a guess: installing
41
+ * the wrong skill silently is worse than asking.
42
+ */
43
+ export declare function resolveSlug(input: string, index: SkillIndex): string;
44
+ /** Parse YAML frontmatter from SKILL.md content. */
45
+ export declare function parseSkillFrontmatter(slug: string, content: string): SkillInfo;
46
+ export declare function fetchSkillMarkdown(slug: string): Promise<string>;
47
+ /**
48
+ * Metadata for every skill. Cached by blob sha, so a warm cache needs zero
49
+ * content requests and only re-fetches the skills that actually changed.
50
+ */
51
+ export declare function listSkills(index: SkillIndex): Promise<SkillInfo[]>;
52
+ export interface SkillFile {
53
+ path: string;
54
+ content: Buffer;
55
+ }
56
+ /**
57
+ * Download every file in a skill directory. Content is kept as bytes: skills
58
+ * may ship images or fonts, and decoding those as UTF-8 corrupts them.
59
+ *
60
+ * A partial download is an error, not a smaller skill. Installing replaces the
61
+ * target directory, so silently dropping failed files would delete a working
62
+ * skill and leave a broken one — possibly without its SKILL.md.
63
+ */
64
+ export declare function fetchSkillFiles(slug: string, index: SkillIndex): Promise<SkillFile[]>;