socialdatax-skills 0.2.12 → 0.2.13

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 CHANGED
@@ -46,12 +46,12 @@ Common search phrases for this skill package:
46
46
 
47
47
  ## Platform MCPs
48
48
 
49
- - XHS hosted MCP endpoint: `https://mcp.52choujiang.com/xhs/mcp`
50
- - Douyin hosted MCP endpoint: `https://mcp.52choujiang.com/douyin/mcp`
51
- - Kuaishou hosted MCP endpoint: `https://mcp.52choujiang.com/kuaishou/mcp`
52
- - Weibo hosted MCP endpoint: `https://mcp.52choujiang.com/weibo/mcp`
53
- - WeChat Channels hosted MCP endpoint: `https://mcp.52choujiang.com/wechat/mcp`
54
- - Sensitive Check hosted MCP endpoint: `https://mcp.52choujiang.com/sensitive-check/mcp`
49
+ - XHS hosted MCP endpoint: `https://mcp.socialdatax.com/xhs/mcp`
50
+ - Douyin hosted MCP endpoint: `https://mcp.socialdatax.com/douyin/mcp`
51
+ - Kuaishou hosted MCP endpoint: `https://mcp.socialdatax.com/kuaishou/mcp`
52
+ - Weibo hosted MCP endpoint: `https://mcp.socialdatax.com/weibo/mcp`
53
+ - WeChat Channels hosted MCP endpoint: `https://mcp.socialdatax.com/wechat/mcp`
54
+ - Sensitive Check hosted MCP endpoint: `https://mcp.socialdatax.com/sensitive-check/mcp`
55
55
  - Hosted transport: `streamable-http`
56
56
  - Authentication: `Authorization: Bearer <SOCIALDATAX_API_KEY>`
57
57
  - Website: <https://socialdatax.com>
@@ -68,7 +68,7 @@ Common search phrases for this skill package:
68
68
  - Sensitive Check MCP server name: `sensitive-check`
69
69
  - Sensitive Check future MCP registry name: `com.socialdatax/sensitive-check`
70
70
  - Unified MCP registry name: none; this package installs skills and calls explicit platform services.
71
- - Current public capability version: `0.2.12`
71
+ - Current public capability version: `0.2.13`
72
72
 
73
73
  ## Direct CLI
74
74
 
@@ -78,6 +78,7 @@ Examples:
78
78
 
79
79
  ```bash
80
80
  npx -y socialdatax-skills@latest xhs search --keyword "露营" --pretty
81
+ npx -y socialdatax-skills@latest xhs search --keyword "露营" --since-days 7 --pages 2 --pretty
81
82
  npx -y socialdatax-skills@latest xhs hot-search --pretty
82
83
  npx -y socialdatax-skills@latest xhs detail --note-id "<note_id>" --pretty
83
84
  npx -y socialdatax-skills@latest xhs comments --note-id "<note_id>" --pretty
@@ -85,6 +86,7 @@ npx -y socialdatax-skills@latest xhs comments --note-id "<note_id>" --all --incl
85
86
  npx -y socialdatax-skills@latest xhs sub-comments --note-id "<note_id>" --comment-id "<comment_id>" --pretty
86
87
  npx -y socialdatax-skills@latest xhs user-info --user-id "<user_id>" --pretty
87
88
  npx -y socialdatax-skills@latest xhs user-posts --user-id "<user_id>" --pretty
89
+ npx -y socialdatax-skills@latest xhs user-posts --user-id "<user_id>" --since-days 30 --pretty
88
90
  npx -y socialdatax-skills@latest douyin hot-search --pretty
89
91
  npx -y socialdatax-skills@latest douyin search --keyword "露营" --pretty
90
92
  npx -y socialdatax-skills@latest douyin detail --aweme-id "<aweme_id>" --pretty
@@ -143,6 +145,8 @@ For XHS outputs with a returned `note_url` field, when `note_url` is non-null, p
143
145
 
144
146
  For list-style commands, use `--pages <n>` to fetch N pages from the current starting point and `--max-items <n>` to cap the merged `data.items` output. Search commands support `--pages` but not `--all`, because search has no stable complete-result boundary.
145
147
 
148
+ For search and creator content-list commands, use `--since-days <1-365>` to keep only items whose public `publish_time` is within the last N days. Search remains bounded by `--pages` and does not promise complete platform coverage; creator content lists continue until the publish-time boundary when `--pages` is omitted.
149
+
146
150
  For comments, replies, creator content lists, and Douyin creator series, use `--all` to continue until the returned `next_page_token` is empty. `--all` has no default item or page cap; add `--max-items <n>` or use `--pages <n>` when you want a bounded run.
147
151
 
148
152
  For first-level comments, add `--include-replies` to fetch the second-level replies under each returned first-level comment. Multi-page output keeps `data.items` as the merged first-level list; each item gets `replies`, `replies_page_count`, and `replies_next_page_token`.
package/cli.mjs CHANGED
@@ -7,7 +7,7 @@ import { dirname, join, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
 
9
9
  const PACKAGE_NAME = "socialdatax-skills";
10
- const PACKAGE_VERSION = "0.2.12";
10
+ const PACKAGE_VERSION = "0.2.13";
11
11
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
12
12
  const LOG_PREFIX = `[${PACKAGE_NAME}]`;
13
13
  const MIN_NODE_VERSION = "20.18.1";
@@ -85,6 +85,7 @@ const XHS_DIRECT_ACTION_OPTIONS = {
85
85
  "pages",
86
86
  "all",
87
87
  "maxItems",
88
+ "sinceDays",
88
89
  "sortType",
89
90
  "noteType",
90
91
  "publishTimeRange",
@@ -112,7 +113,16 @@ const XHS_DIRECT_ACTION_OPTIONS = {
112
113
  "pretty",
113
114
  ],
114
115
  "user-info": ["userId", "profileUrl", "pretty"],
115
- "user-posts": ["userId", "profileUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
116
+ "user-posts": [
117
+ "userId",
118
+ "profileUrl",
119
+ "pageToken",
120
+ "pages",
121
+ "all",
122
+ "maxItems",
123
+ "sinceDays",
124
+ "pretty",
125
+ ],
116
126
  };
117
127
  const XHS_DIRECT_ACTION_NAMES = Object.keys(XHS_DIRECT_ACTION_OPTIONS).join(", ");
118
128
  const XHS_OPTION_DISPLAY_NAMES = {
@@ -126,6 +136,7 @@ const XHS_OPTION_DISPLAY_NAMES = {
126
136
  pageToken: "--page-token",
127
137
  pages: "--pages",
128
138
  maxItems: "--max-items",
139
+ sinceDays: "--since-days",
129
140
  all: "--all",
130
141
  includeReplies: "--include-replies",
131
142
  profileUrl: "--profile-url",
@@ -151,6 +162,7 @@ const DOUYIN_DIRECT_ACTION_OPTIONS = {
151
162
  "pages",
152
163
  "all",
153
164
  "maxItems",
165
+ "sinceDays",
154
166
  "sortType",
155
167
  "publishTimeRange",
156
168
  "durationRange",
@@ -179,7 +191,16 @@ const DOUYIN_DIRECT_ACTION_OPTIONS = {
179
191
  "pretty",
180
192
  ],
181
193
  "user-info": ["secUserId", "profileUrl", "pretty"],
182
- "user-posts": ["secUserId", "profileUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
194
+ "user-posts": [
195
+ "secUserId",
196
+ "profileUrl",
197
+ "pageToken",
198
+ "pages",
199
+ "all",
200
+ "maxItems",
201
+ "sinceDays",
202
+ "pretty",
203
+ ],
183
204
  "user-series": ["secUserId", "profileUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
184
205
  };
185
206
  const DOUYIN_DIRECT_ACTION_NAMES = Object.keys(DOUYIN_DIRECT_ACTION_OPTIONS).join(", ");
@@ -197,6 +218,7 @@ const DOUYIN_OPTION_DISPLAY_NAMES = {
197
218
  pageToken: "--page-token",
198
219
  pages: "--pages",
199
220
  maxItems: "--max-items",
221
+ sinceDays: "--since-days",
200
222
  all: "--all",
201
223
  includeReplies: "--include-replies",
202
224
  sortType: "--sort-type",
@@ -211,7 +233,7 @@ const DOUYIN_OPTION_DISPLAY_NAMES = {
211
233
  };
212
234
  const KUAISHOU_DIRECT_ACTION_OPTIONS = {
213
235
  "hot-search": ["pretty"],
214
- search: ["keyword", "pageToken", "pages", "all", "maxItems", "pretty"],
236
+ search: ["keyword", "pageToken", "pages", "all", "maxItems", "sinceDays", "pretty"],
215
237
  detail: ["photoId", "url", "pretty"],
216
238
  comments: [
217
239
  "photoId",
@@ -234,7 +256,16 @@ const KUAISHOU_DIRECT_ACTION_OPTIONS = {
234
256
  "pretty",
235
257
  ],
236
258
  "user-info": ["userId", "profileUrl", "pretty"],
237
- "user-posts": ["userId", "profileUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
259
+ "user-posts": [
260
+ "userId",
261
+ "profileUrl",
262
+ "pageToken",
263
+ "pages",
264
+ "all",
265
+ "maxItems",
266
+ "sinceDays",
267
+ "pretty",
268
+ ],
238
269
  };
239
270
  const KUAISHOU_DIRECT_ACTION_NAMES = Object.keys(KUAISHOU_DIRECT_ACTION_OPTIONS).join(", ");
240
271
  const KUAISHOU_OPTION_DISPLAY_NAMES = {
@@ -242,6 +273,7 @@ const KUAISHOU_OPTION_DISPLAY_NAMES = {
242
273
  pageToken: "--page-token",
243
274
  pages: "--pages",
244
275
  maxItems: "--max-items",
276
+ sinceDays: "--since-days",
245
277
  all: "--all",
246
278
  includeReplies: "--include-replies",
247
279
  url: "--url",
@@ -252,7 +284,7 @@ const KUAISHOU_OPTION_DISPLAY_NAMES = {
252
284
  };
253
285
  const WEIBO_DIRECT_ACTION_OPTIONS = {
254
286
  "hot-search": ["pretty"],
255
- search: ["keyword", "pageToken", "pages", "all", "maxItems", "pretty"],
287
+ search: ["keyword", "pageToken", "pages", "all", "maxItems", "sinceDays", "pretty"],
256
288
  detail: ["postId", "postUrl", "pretty"],
257
289
  comments: [
258
290
  "postId",
@@ -277,7 +309,16 @@ const WEIBO_DIRECT_ACTION_OPTIONS = {
277
309
  likers: ["postId", "pageToken", "pages", "all", "maxItems", "pretty"],
278
310
  reposts: ["postId", "pageToken", "pages", "all", "maxItems", "pretty"],
279
311
  "user-info": ["userId", "profileUrl", "pretty"],
280
- "user-posts": ["userId", "profileUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
312
+ "user-posts": [
313
+ "userId",
314
+ "profileUrl",
315
+ "pageToken",
316
+ "pages",
317
+ "all",
318
+ "maxItems",
319
+ "sinceDays",
320
+ "pretty",
321
+ ],
281
322
  };
282
323
  const WEIBO_DIRECT_ACTION_NAMES = Object.keys(WEIBO_DIRECT_ACTION_OPTIONS).join(", ");
283
324
  const WEIBO_OPTION_DISPLAY_NAMES = {
@@ -285,6 +326,7 @@ const WEIBO_OPTION_DISPLAY_NAMES = {
285
326
  pageToken: "--page-token",
286
327
  pages: "--pages",
287
328
  maxItems: "--max-items",
329
+ sinceDays: "--since-days",
288
330
  all: "--all",
289
331
  includeReplies: "--include-replies",
290
332
  postId: "--post-id",
@@ -301,6 +343,7 @@ const WECHAT_DIRECT_ACTION_OPTIONS = {
301
343
  "pages",
302
344
  "all",
303
345
  "maxItems",
346
+ "sinceDays",
304
347
  "sortType",
305
348
  "durationRange",
306
349
  "pretty",
@@ -329,7 +372,16 @@ const WECHAT_DIRECT_ACTION_OPTIONS = {
329
372
  "pretty",
330
373
  ],
331
374
  "user-info": ["userId", "pretty"],
332
- "user-posts": ["userId", "url", "pageToken", "pages", "all", "maxItems", "pretty"],
375
+ "user-posts": [
376
+ "userId",
377
+ "url",
378
+ "pageToken",
379
+ "pages",
380
+ "all",
381
+ "maxItems",
382
+ "sinceDays",
383
+ "pretty",
384
+ ],
333
385
  };
334
386
  const WECHAT_DIRECT_ACTION_NAMES = Object.keys(WECHAT_DIRECT_ACTION_OPTIONS).join(", ");
335
387
  const WECHAT_SEARCH_SORT_TYPES = ["all", "latest", "popular"];
@@ -344,6 +396,7 @@ const WECHAT_OPTION_DISPLAY_NAMES = {
344
396
  pageToken: "--page-token",
345
397
  pages: "--pages",
346
398
  maxItems: "--max-items",
399
+ sinceDays: "--since-days",
347
400
  all: "--all",
348
401
  includeReplies: "--include-replies",
349
402
  sortType: "--sort-type",
@@ -373,7 +426,7 @@ const PLATFORMS = {
373
426
  status: "public",
374
427
  registryName: "com.52choujiang/xhs-insights",
375
428
  futureRegistryName: "com.socialdatax/xhs-insights",
376
- endpoint: "https://mcp.52choujiang.com/xhs/mcp",
429
+ endpoint: "https://mcp.socialdatax.com/xhs/mcp",
377
430
  apiKeyEnv: API_KEY_ENV_NAMES,
378
431
  upstreamEnv: [
379
432
  "SOCIAL_MEDIA_XHS_MCP_UPSTREAM_URL",
@@ -455,7 +508,7 @@ const PLATFORMS = {
455
508
  status: "public",
456
509
  registryName: "com.52choujiang/douyin-insights",
457
510
  futureRegistryName: "com.socialdatax/douyin-insights",
458
- endpoint: "https://mcp.52choujiang.com/douyin/mcp",
511
+ endpoint: "https://mcp.socialdatax.com/douyin/mcp",
459
512
  apiKeyEnv: API_KEY_ENV_NAMES,
460
513
  upstreamEnv: [
461
514
  "SOCIAL_MEDIA_DOUYIN_MCP_UPSTREAM_URL",
@@ -539,7 +592,7 @@ const PLATFORMS = {
539
592
  status: "public",
540
593
  registryName: "com.52choujiang/kuaishou-insights",
541
594
  futureRegistryName: "com.socialdatax/kuaishou-insights",
542
- endpoint: "https://mcp.52choujiang.com/kuaishou/mcp",
595
+ endpoint: "https://mcp.socialdatax.com/kuaishou/mcp",
543
596
  apiKeyEnv: API_KEY_ENV_NAMES,
544
597
  upstreamEnv: [
545
598
  "SOCIAL_MEDIA_KUAISHOU_MCP_UPSTREAM_URL",
@@ -615,7 +668,7 @@ const PLATFORMS = {
615
668
  status: "public",
616
669
  registryName: "com.52choujiang/weibo-insights",
617
670
  futureRegistryName: "com.socialdatax/weibo-insights",
618
- endpoint: "https://mcp.52choujiang.com/weibo/mcp",
671
+ endpoint: "https://mcp.socialdatax.com/weibo/mcp",
619
672
  apiKeyEnv: API_KEY_ENV_NAMES,
620
673
  upstreamEnv: [
621
674
  "SOCIAL_MEDIA_WEIBO_MCP_UPSTREAM_URL",
@@ -699,7 +752,7 @@ const PLATFORMS = {
699
752
  status: "public",
700
753
  registryName: "com.52choujiang/wechat-channels-insights",
701
754
  futureRegistryName: "com.socialdatax/wechat-channels-insights",
702
- endpoint: "https://mcp.52choujiang.com/wechat/mcp",
755
+ endpoint: "https://mcp.socialdatax.com/wechat/mcp",
703
756
  apiKeyEnv: API_KEY_ENV_NAMES,
704
757
  upstreamEnv: [
705
758
  "SOCIAL_MEDIA_WECHAT_MCP_UPSTREAM_URL",
@@ -771,7 +824,7 @@ const PLATFORMS = {
771
824
  status: "public",
772
825
  registryName: "sensitive-check",
773
826
  futureRegistryName: "com.socialdatax/sensitive-check",
774
- endpoint: "https://mcp.52choujiang.com/sensitive-check/mcp",
827
+ endpoint: "https://mcp.socialdatax.com/sensitive-check/mcp",
775
828
  apiKeyEnv: API_KEY_ENV_NAMES,
776
829
  upstreamEnv: [
777
830
  "SOCIALDATAX_SENSITIVE_CHECK_MCP_UPSTREAM_URL",
@@ -1015,6 +1068,9 @@ function validateDirectPaginationOptions(platformId, action, options) {
1015
1068
  if (options.maxItems !== undefined) {
1016
1069
  parsePositiveIntegerOption(options.maxItems, "--max-items");
1017
1070
  }
1071
+ if (options.sinceDays !== undefined) {
1072
+ parseSinceDaysOption(options.sinceDays);
1073
+ }
1018
1074
  if (options.all && action === "search") {
1019
1075
  throw new Error(`--all is not supported for ${platformId} search. Use --pages instead.`);
1020
1076
  }
@@ -1035,6 +1091,37 @@ function parsePositiveIntegerOption(value, displayName) {
1035
1091
  return parsed;
1036
1092
  }
1037
1093
 
1094
+ function parseSinceDaysOption(value) {
1095
+ const text = String(value);
1096
+ if (!/^\d+$/.test(text)) {
1097
+ throw new Error("--since-days must be an integer between 1 and 365.");
1098
+ }
1099
+ const parsed = Number(text);
1100
+ if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 365) {
1101
+ throw new Error("--since-days must be an integer between 1 and 365.");
1102
+ }
1103
+ return parsed;
1104
+ }
1105
+
1106
+ function parseOptionalSinceDays(options) {
1107
+ return options.sinceDays === undefined
1108
+ ? undefined
1109
+ : parseSinceDaysOption(options.sinceDays);
1110
+ }
1111
+
1112
+ function nativePublishTimeRangeForSinceDays(sinceDays) {
1113
+ if (sinceDays <= 1) {
1114
+ return "day";
1115
+ }
1116
+ if (sinceDays <= 7) {
1117
+ return "week";
1118
+ }
1119
+ if (sinceDays <= 180) {
1120
+ return "half_year";
1121
+ }
1122
+ return undefined;
1123
+ }
1124
+
1038
1125
  function parseAllowedStringOption(value, displayName, allowedValues, label) {
1039
1126
  const normalized = String(value).trim();
1040
1127
  if (!allowedValues.includes(normalized)) {
@@ -1500,6 +1587,9 @@ function printHelp() {
1500
1587
  console.log(` npx -y ${PACKAGE_SPEC} xhs search --keyword "露营" --pretty`);
1501
1588
  console.log(" Call the XHS search tool directly and print JSON.");
1502
1589
  console.log("");
1590
+ console.log(` npx -y ${PACKAGE_SPEC} xhs search --keyword "露营" --since-days 7 --pages 2 --pretty`);
1591
+ console.log(" Search recent XHS results and keep items published in the last 7 days.");
1592
+ console.log("");
1503
1593
  console.log(` npx -y ${PACKAGE_SPEC} xhs hot-search --pretty`);
1504
1594
  console.log(" Call the XHS search hot list tool directly and print JSON.");
1505
1595
  console.log("");
@@ -1523,6 +1613,9 @@ function printHelp() {
1523
1613
  console.log(` npx -y ${PACKAGE_SPEC} xhs user-posts --user-id "<user_id>" --pretty`);
1524
1614
  console.log(" Call the XHS creator posts tool directly and print JSON.");
1525
1615
  console.log("");
1616
+ console.log(` npx -y ${PACKAGE_SPEC} xhs user-posts --user-id "<user_id>" --since-days 30 --pretty`);
1617
+ console.log(" Fetch recent creator posts until the publish-time boundary is reached.");
1618
+ console.log("");
1526
1619
  console.log(` npx -y ${PACKAGE_SPEC} douyin hot-search --pretty`);
1527
1620
  console.log(" Call the Douyin main hot search list tool directly and print JSON.");
1528
1621
  console.log("");
@@ -1762,6 +1855,8 @@ function printHelp() {
1762
1855
  console.log(" Continue comments, replies, creator content lists, and creator series until next_page_token is empty; not supported for search.");
1763
1856
  console.log(" --max-items <number>");
1764
1857
  console.log(" Stop after collecting this many primary items.");
1858
+ console.log(" --since-days <1-365>");
1859
+ console.log(" Keep search or creator content-list items with publish_time in the last N days. Search stays bounded by --pages; creator lists continue until the time boundary when --pages is omitted.");
1765
1860
  console.log(" --include-replies");
1766
1861
  console.log(" For comments commands, also fetch nested second-level replies for each returned first-level comment.");
1767
1862
  console.log(" --sort-type <general|time_descending|like_count_descending|comment_count_descending|collect_count_descending>");
@@ -1811,18 +1906,18 @@ function printRemovedMcpConfigHelp(command) {
1811
1906
  console.error(" com.socialdatax/wechat-channels-insights");
1812
1907
  console.error("");
1813
1908
  console.error("Use hosted streamable HTTP when your client supports remote MCP:");
1814
- console.error(" https://mcp.52choujiang.com/xhs/mcp");
1815
- console.error(" https://mcp.52choujiang.com/douyin/mcp");
1816
- console.error(" https://mcp.52choujiang.com/kuaishou/mcp");
1817
- console.error(" https://mcp.52choujiang.com/weibo/mcp");
1818
- console.error(" https://mcp.52choujiang.com/wechat/mcp");
1909
+ console.error(" https://mcp.socialdatax.com/xhs/mcp");
1910
+ console.error(" https://mcp.socialdatax.com/douyin/mcp");
1911
+ console.error(" https://mcp.socialdatax.com/kuaishou/mcp");
1912
+ console.error(" https://mcp.socialdatax.com/weibo/mcp");
1913
+ console.error(" https://mcp.socialdatax.com/wechat/mcp");
1819
1914
  console.error("");
1820
1915
  console.error("For command/stdio-only clients, use mcp-remote:");
1821
- console.error(` npx -y mcp-remote https://mcp.52choujiang.com/xhs/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1822
- console.error(` npx -y mcp-remote https://mcp.52choujiang.com/douyin/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1823
- console.error(` npx -y mcp-remote https://mcp.52choujiang.com/kuaishou/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1824
- console.error(` npx -y mcp-remote https://mcp.52choujiang.com/weibo/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1825
- console.error(` npx -y mcp-remote https://mcp.52choujiang.com/wechat/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1916
+ console.error(` npx -y mcp-remote https://mcp.socialdatax.com/xhs/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1917
+ console.error(` npx -y mcp-remote https://mcp.socialdatax.com/douyin/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1918
+ console.error(` npx -y mcp-remote https://mcp.socialdatax.com/kuaishou/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1919
+ console.error(` npx -y mcp-remote https://mcp.socialdatax.com/weibo/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1920
+ console.error(` npx -y mcp-remote https://mcp.socialdatax.com/wechat/mcp --header "Authorization: Bearer <${PRIMARY_API_KEY_ENV}>"`);
1826
1921
  }
1827
1922
 
1828
1923
  async function runXhsDirectCommand(args) {
@@ -2475,8 +2570,13 @@ function buildXhsSearchCall(options) {
2475
2570
  if (!options.keyword) {
2476
2571
  throw new Error("Missing --keyword for xhs search.");
2477
2572
  }
2573
+ const sinceDays = parseOptionalSinceDays(options);
2574
+ const requestedSortType =
2575
+ options.sortType === undefined && sinceDays !== undefined
2576
+ ? "time_descending"
2577
+ : options.sortType || "general";
2478
2578
  const sortType = parseSemanticOption(
2479
- options.sortType || "general",
2579
+ requestedSortType,
2480
2580
  "--sort-type",
2481
2581
  XHS_SEARCH_SORT_TYPES,
2482
2582
  XHS_LEGACY_SEARCH_SORT_TYPE_ALIASES,
@@ -2504,12 +2604,19 @@ function buildXhsSearchCall(options) {
2504
2604
  }
2505
2605
  if (options.sortType !== undefined) {
2506
2606
  toolArguments.sort_type = sortType;
2607
+ } else if (sinceDays !== undefined) {
2608
+ toolArguments.sort_type = "time_descending";
2507
2609
  }
2508
2610
  if (options.noteType !== undefined) {
2509
2611
  toolArguments.note_type = noteType;
2510
2612
  }
2511
2613
  if (options.publishTimeRange !== undefined) {
2512
2614
  toolArguments.publish_time_range = publishTimeRange;
2615
+ } else if (sinceDays !== undefined) {
2616
+ const nativeRange = nativePublishTimeRangeForSinceDays(sinceDays);
2617
+ if (nativeRange) {
2618
+ toolArguments.publish_time_range = nativeRange;
2619
+ }
2513
2620
  }
2514
2621
  return {
2515
2622
  tool: "xhs_search_notes",
@@ -2576,6 +2683,7 @@ function buildDouyinSearchCall(options) {
2576
2683
  if (!options.keyword) {
2577
2684
  throw new Error("Missing --keyword for douyin search.");
2578
2685
  }
2686
+ const sinceDays = parseOptionalSinceDays(options);
2579
2687
  const toolArguments = {
2580
2688
  keyword: options.keyword,
2581
2689
  };
@@ -2586,6 +2694,8 @@ function buildDouyinSearchCall(options) {
2586
2694
  DOUYIN_SEARCH_SORT_TYPES,
2587
2695
  DOUYIN_SEARCH_SORT_TYPES.join(", ")
2588
2696
  );
2697
+ } else if (sinceDays !== undefined) {
2698
+ toolArguments.sort_type = "time_descending";
2589
2699
  }
2590
2700
  if (options.publishTimeRange !== undefined) {
2591
2701
  toolArguments.publish_time_range = parseAllowedStringOption(
@@ -2594,6 +2704,11 @@ function buildDouyinSearchCall(options) {
2594
2704
  DOUYIN_SEARCH_PUBLISH_TIME_RANGES,
2595
2705
  DOUYIN_SEARCH_PUBLISH_TIME_RANGES.join(", ")
2596
2706
  );
2707
+ } else if (sinceDays !== undefined) {
2708
+ const nativeRange = nativePublishTimeRangeForSinceDays(sinceDays);
2709
+ if (nativeRange) {
2710
+ toolArguments.publish_time_range = nativeRange;
2711
+ }
2597
2712
  }
2598
2713
  if (options.durationRange !== undefined) {
2599
2714
  toolArguments.duration_range = parseAllowedStringOption(
@@ -2656,6 +2771,7 @@ function buildWechatSearchCall(options) {
2656
2771
  if (!options.keyword) {
2657
2772
  throw new Error("Missing --keyword for wechat search.");
2658
2773
  }
2774
+ const sinceDays = parseOptionalSinceDays(options);
2659
2775
  const toolArguments = {
2660
2776
  keyword: options.keyword,
2661
2777
  };
@@ -2666,6 +2782,8 @@ function buildWechatSearchCall(options) {
2666
2782
  WECHAT_SEARCH_SORT_TYPES,
2667
2783
  WECHAT_SEARCH_SORT_TYPES.join(", ")
2668
2784
  );
2785
+ } else if (sinceDays !== undefined) {
2786
+ toolArguments.sort_type = "latest";
2669
2787
  }
2670
2788
  if (options.durationRange !== undefined) {
2671
2789
  toolArguments.duration_range = parseAllowedStringOption(
@@ -2875,7 +2993,8 @@ function shouldUsePaginatedDirectOutput(options) {
2875
2993
  options.all ||
2876
2994
  options.pages !== undefined ||
2877
2995
  options.maxItems !== undefined ||
2878
- options.includeReplies
2996
+ options.includeReplies ||
2997
+ options.sinceDays !== undefined
2879
2998
  );
2880
2999
  }
2881
3000
 
@@ -2897,6 +3016,7 @@ function parseDirectPaginationOptions(options) {
2897
3016
  options.maxItems === undefined
2898
3017
  ? undefined
2899
3018
  : parsePositiveIntegerOption(options.maxItems, "--max-items"),
3019
+ sinceDays: parseOptionalSinceDays(options),
2900
3020
  };
2901
3021
  }
2902
3022
 
@@ -2905,7 +3025,14 @@ async function collectPaginatedDirectData(
2905
3025
  pagination,
2906
3026
  { includeReplies = false } = {}
2907
3027
  ) {
2908
- const pageLimit = pagination.all ? Number.POSITIVE_INFINITY : pagination.pages || 1;
3028
+ const pageLimit =
3029
+ pagination.all || shouldAutoPaginateSinceDays(operation, pagination)
3030
+ ? Number.POSITIVE_INFINITY
3031
+ : pagination.pages || 1;
3032
+ const sinceDaysCutoff =
3033
+ pagination.sinceDays === undefined
3034
+ ? undefined
3035
+ : cutoffPublishTimeForSinceDays(pagination.sinceDays);
2909
3036
  const collectedItems = [];
2910
3037
  const itemDedupeState = createPaginatedItemDedupeState();
2911
3038
  const seenNextMarkers = initialPaginationMarkers(operation);
@@ -2914,6 +3041,7 @@ async function collectPaginatedDirectData(
2914
3041
  let nextMarker;
2915
3042
  let parentContextData = {};
2916
3043
  let currentOperation = cloneDirectOperation(operation);
3044
+ let stoppedBySinceDays = false;
2917
3045
 
2918
3046
  while (pageCount < pageLimit) {
2919
3047
  const pageData = await callDirectOperation(currentOperation);
@@ -2926,8 +3054,12 @@ async function collectPaginatedDirectData(
2926
3054
  directPageItems(pageData),
2927
3055
  itemDedupeState
2928
3056
  );
3057
+ const filteredPage = filterItemsBySinceDays(pageItems, sinceDaysCutoff);
3058
+ if (shouldStopAtSinceDaysBoundary(operation, sinceDaysCutoff, pageItems)) {
3059
+ stoppedBySinceDays = true;
3060
+ }
2929
3061
  const candidateItems = itemsForRemainingLimit(
2930
- pageItems,
3062
+ filteredPage.items,
2931
3063
  collectedItems.length,
2932
3064
  pagination.maxItems
2933
3065
  );
@@ -2948,6 +3080,7 @@ async function collectPaginatedDirectData(
2948
3080
  if (
2949
3081
  !nextMarker ||
2950
3082
  reachedMaxItems(collectedItems, pagination.maxItems) ||
3083
+ stoppedBySinceDays ||
2951
3084
  pageCount >= pageLimit
2952
3085
  ) {
2953
3086
  if (markerRepeated) {
@@ -2971,6 +3104,7 @@ async function collectPaginatedDirectData(
2971
3104
  items: collectedItems,
2972
3105
  pageCount,
2973
3106
  nextMarker,
3107
+ stoppedBySinceDays,
2974
3108
  });
2975
3109
  }
2976
3110
 
@@ -2985,6 +3119,54 @@ function directPageItems(pageData) {
2985
3119
  return Array.isArray(pageData?.items) ? pageData.items : [];
2986
3120
  }
2987
3121
 
3122
+ function shouldAutoPaginateSinceDays(operation, pagination) {
3123
+ return (
3124
+ operation.operation === "user-posts" &&
3125
+ pagination.sinceDays !== undefined &&
3126
+ pagination.pages === undefined
3127
+ );
3128
+ }
3129
+
3130
+ function cutoffPublishTimeForSinceDays(sinceDays) {
3131
+ return Math.floor(Date.now() / 1000) - sinceDays * 86400;
3132
+ }
3133
+
3134
+ function itemPublishTimeSeconds(item) {
3135
+ const value = item?.publish_time;
3136
+ const timestamp =
3137
+ typeof value === "number"
3138
+ ? value
3139
+ : typeof value === "string"
3140
+ ? Number(value)
3141
+ : NaN;
3142
+ if (!Number.isFinite(timestamp) || timestamp <= 0) {
3143
+ return undefined;
3144
+ }
3145
+ return timestamp;
3146
+ }
3147
+
3148
+ function filterItemsBySinceDays(items, cutoffPublishTime) {
3149
+ if (cutoffPublishTime === undefined) {
3150
+ return { items };
3151
+ }
3152
+ return {
3153
+ items: items.filter((item) => {
3154
+ const publishTime = itemPublishTimeSeconds(item);
3155
+ return publishTime !== undefined && publishTime >= cutoffPublishTime;
3156
+ }),
3157
+ };
3158
+ }
3159
+
3160
+ function shouldStopAtSinceDaysBoundary(operation, cutoffPublishTime, items) {
3161
+ if (operation.operation !== "user-posts" || cutoffPublishTime === undefined) {
3162
+ return false;
3163
+ }
3164
+ return items.some((item) => {
3165
+ const publishTime = itemPublishTimeSeconds(item);
3166
+ return publishTime !== undefined && publishTime < cutoffPublishTime;
3167
+ });
3168
+ }
3169
+
2988
3170
  function createPaginatedItemDedupeState() {
2989
3171
  return { seenKeys: new Set() };
2990
3172
  }
@@ -3204,6 +3386,7 @@ function buildPaginatedData({
3204
3386
  items,
3205
3387
  pageCount,
3206
3388
  nextMarker,
3389
+ stoppedBySinceDays,
3207
3390
  }) {
3208
3391
  const data = {
3209
3392
  ...(lastPageData && typeof lastPageData === "object" ? lastPageData : {}),
@@ -3214,6 +3397,9 @@ function buildPaginatedData({
3214
3397
  };
3215
3398
  delete data.next_page;
3216
3399
  data.next_page_token = nextMarker || "";
3400
+ if (stoppedBySinceDays) {
3401
+ data.stopped_by_since_days = true;
3402
+ }
3217
3403
  return data;
3218
3404
  }
3219
3405
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socialdatax-skills",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "SocialDataX / 社媒数据助手 skill installer and direct CLI helper for hosted platform social media MCP services.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -76,6 +76,7 @@ Optional arguments:
76
76
  - Douyin `--pages <n>`: fetch and merge N search pages from the current starting point; continue with returned `next_page_token`.
77
77
  - Kuaishou `--pages <n>`: fetch and merge N search pages from the current starting point; continue with returned `next_page_token`.
78
78
  - `--max-items <n>`: stop after collecting N search results.
79
+ - `--since-days <1-365>`: keep only results whose public `publish_time` is within the last N days. Search remains bounded by `--pages` and does not promise complete platform coverage.
79
80
  - `--pretty`: output formatting only.
80
81
  - Kuaishou `--page-token <next_page_token>`: opaque pagination token; omit it on the first search request. Continue only with the complete returned `next_page_token` from the same search pagination chain. Do not modify, truncate, redact, mask, omit, normalize, rebuild, generate, or replace the middle with ellipses.
81
82
  - Weibo `--page-token <next_page_token>`: opaque pagination token; omit it on the first search request. Continue only with the complete returned `next_page_token` from the same search pagination chain. Do not modify, truncate, redact, mask, omit, normalize, rebuild, generate, or replace the middle with ellipses.
@@ -126,6 +127,7 @@ Douyin content type filter values:
126
127
  - `image`: image/text posts.
127
128
 
128
129
  The command prints JSON with `platform`, `tool`, `arguments`, and `data`. Search supports `--pages` and `--max-items`, but not `--all`, because search has no stable complete-result boundary. Multi-page output keeps merged results in `data.items` and adds `page_count`, `item_count`, and the next-page marker.
130
+ With CLI `--since-days <n>`, XHS and Douyin search automatically prefer newest-first sorting and the closest native publish-time range unless the user explicitly sets sort or publish-time filters; WeChat Channels search defaults to `latest`; Kuaishou and Weibo only apply CLI-side `publish_time` filtering.
129
131
  Kuaishou search pagination:
130
132
  - Continue only when `next_page_token` is not empty.
131
133
  - Pass the complete returned `next_page_token` back unchanged as `page_token` for the same search pagination chain. Do not modify, truncate, redact, mask, omit, normalize, rebuild, generate, or replace the middle with ellipses.
@@ -198,6 +200,7 @@ Do not pass `page` to `wechat_search_videos`; omit `page_token` on the first req
198
200
  - `sort_type`: optional, one of `all`, `latest`, `popular`; omit it for the default sort.
199
201
  - `duration_range`: optional duration filter, one of `all`, `under_5_min`, `between_5_and_20_min`, `over_20_min`; omit it for no duration filter.
200
202
  Continue WeChat Channels pagination only when `next_page_token` is not empty. Pass the complete returned `next_page_token` back unchanged as `page_token` for the same keyword and filter chain.
203
+ `--since-days` uses CLI-side filtering only and is not an MCP tool argument; for MCP-only calls, request newest-first/native publish-time filters where available and filter returned `publish_time` values in your analysis.
201
204
 
202
205
  ## Output Guidance
203
206
 
@@ -73,6 +73,7 @@ Optional arguments:
73
73
  - `--pages <n>`: fetch and merge N pages of creator content or creator series.
74
74
  - `--all`: continue until `next_page_token` is empty; there is no default item or page cap.
75
75
  - `--max-items <n>`: stop after collecting N creator content or series items.
76
+ - `--since-days <1-365>`: keep only creator content whose public `publish_time` is within the last N days. When `--pages` is omitted, the CLI continues creator content lists until the publish-time boundary is reached.
76
77
  - `--pretty`: output formatting only.
77
78
  - Kuaishou `--user-id <user_id>`: preferred when the creator user_id is already known.
78
79
  - Kuaishou `--profile-url <profile_url_or_share_text>`: use for a profile URL, short link, or profile share text.
@@ -84,6 +85,7 @@ Optional arguments:
84
85
  Use either the ID option or the profile URL option for a single command, not both.
85
86
 
86
87
  The command prints JSON with `platform`, `tool`, `arguments`, and `data`. Multi-page output keeps merged creator content or series items in `data.items` and adds `page_count`, `item_count`, and `next_page_token`.
88
+ For recent creator research, prefer CLI `--since-days 30` or another user-specified day window. `--since-days` applies to creator content lists only, not Douyin `user-series`.
87
89
 
88
90
  ## Safety Boundary
89
91
 
@@ -124,6 +126,7 @@ Weibo creator post pagination uses opaque `page_token` values; pass the complete
124
126
  - `wechat_get_user_posted_videos_by_user_id`: preferred when the WeChat Channels / 视频号 `@finder` user_id is already known.
125
127
  - `wechat_get_user_posted_videos_by_url`: use a WeChat Channels / 视频号 video link or share text to resolve the author and list that creator's videos.
126
128
  WeChat Channels / 视频号 creator video pagination uses opaque `page_token` values; pass the complete returned `next_page_token` back unchanged for the same user. Do not modify, truncate, redact, mask, omit, normalize, rebuild, generate, or replace the middle with ellipses.
129
+ `--since-days` uses CLI-side filtering only and is not an MCP tool argument; for MCP-only calls, continue pages as needed and filter returned `publish_time` values in your analysis.
127
130
 
128
131
  ## Output Guidance
129
132
 
@@ -168,6 +168,7 @@ Optional arguments:
168
168
  - `--pages <n>`: fetch and merge N pages for search, comments, replies, creator posts, or creator series.
169
169
  - `--all`: fetch comments, replies, creator posts, or creator series until `next_page_token` is empty; do not use it for search.
170
170
  - `--max-items <n>`: stop after collecting N primary results.
171
+ - `--since-days <1-365>`: keep search or creator content-list items whose public `publish_time` is within the last N days. Search stays bounded by `--pages`; creator content lists continue until the publish-time boundary when `--pages` is omitted.
171
172
  - `--include-replies`: for first-level comments, also fetch nested second-level replies under each returned comment.
172
173
  - `--sort-type`, `--note-type`, `--publish-time-range`, `--duration-range`, and `--content-type`: apply only to XHS, Douyin, or WeChat Channels keyword research commands when the user asks for filtering; Kuaishou and Weibo search currently use `--keyword` and optional `--page-token`.
173
174
  - `--comment-id`: required for reply/sub-comment commands together with the content ID.
@@ -185,6 +186,7 @@ Optional arguments:
185
186
  ## Choose The Narrowest Entry
186
187
 
187
188
  Use the most specific direct CLI command for the user's task instead of forcing every request through keyword research. The command prints JSON with `platform`, `tool`, `arguments`, and `data`.
189
+ When the user asks for recent content, pass CLI `--since-days <n>` with bounded `--pages`; keep in mind this filters returned `publish_time` values and does not promise complete platform coverage.
188
190
 
189
191
  ## Safety Boundary
190
192