siluzan-tso-cli 1.1.48-beta.7 → 1.1.48-beta.8

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
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
- > **注意**:当前为测试版(1.1.48-beta.7),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.48-beta.8),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -133378,6 +133378,28 @@ function genderLabel(gender) {
133378
133378
  if (g === "female") return "female";
133379
133379
  return str(gender) || "unknown";
133380
133380
  }
133381
+ function displayResultType(raw) {
133382
+ const v = str(raw);
133383
+ if (!v) return "";
133384
+ const key = v.toLowerCase().replace(/[\s_]+/g, "");
133385
+ if (key === "lead" || key === "leads" || key.includes("leadgrouped") || key.includes("instantform") || key === "onsiteconversionleadgrouped") {
133386
+ return "\u6F5C\u5728\u5BA2\u6237\u4FE1\u606F\uFF08\u8868\u5355\uFF09\u91CF";
133387
+ }
133388
+ return v;
133389
+ }
133390
+ function displayAttribution(raw) {
133391
+ const v = str(raw);
133392
+ if (!v) return "";
133393
+ const key = v.toLowerCase().replace(/[\s_\-/]/g, "");
133394
+ if (key === "multiple" || key === "7dclick" || key === "7dclick1dview" || key.includes("7dclick") || key.includes("7dayclick")) {
133395
+ return "\u70B9\u51FB\u540E 7 \u5929\u5185\u6216\u6D4F\u89C8\u540E 1 \u5929\u5185";
133396
+ }
133397
+ return v;
133398
+ }
133399
+ function hasResults(row) {
133400
+ const n = num3(row.results);
133401
+ return n != null && n > 0;
133402
+ }
133381
133403
 
133382
133404
  // src/commands/facebook-analysis/merge-snapshot.ts
133383
133405
  async function readManifest2(snapshotDir) {
@@ -133619,88 +133641,102 @@ function metricHeaders(currency, firstCol) {
133619
133641
  "\u5355\u6B21\u6210\u6548\u8D39\u7528"
133620
133642
  ];
133621
133643
  }
133644
+ function moneyText(value) {
133645
+ const n = num3(value);
133646
+ if (n == null) return "\u2014";
133647
+ return `$${n.toFixed(2)}`;
133648
+ }
133622
133649
  function metricCells(row) {
133650
+ const results = num3(row.results);
133651
+ const showResults = hasResults({ results });
133623
133652
  return [
133624
133653
  num3(row.reach) ?? "",
133625
133654
  num3(row.impressions) ?? "",
133626
133655
  num3(row.frequency) ?? "",
133627
- str(row.attributionSetting),
133628
- str(row.resultType),
133629
- num3(row.results) ?? "",
133656
+ displayAttribution(row.attributionSetting),
133657
+ showResults ? displayResultType(row.resultType) : "",
133658
+ showResults ? results ?? "" : "",
133630
133659
  num3(row.spend) ?? "",
133631
- num3(row.costPerResult) ?? ""
133660
+ showResults ? num3(row.costPerResult) ?? "" : ""
133632
133661
  ];
133633
133662
  }
133634
133663
  function bullets(value) {
133635
133664
  return asArray2(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
133636
133665
  }
133637
- function joinBlock(title, lines) {
133638
- return [title, ...lines.map((line) => `\u2022 ${line}`)].join("\n");
133666
+ function insightLines(section) {
133667
+ const rec = asRecord15(section);
133668
+ const insight = asRecord15(rec?.insight) ?? rec;
133669
+ return [...bullets(insight?.analysis), ...bullets(insight?.advice)];
133670
+ }
133671
+ function numberedSection(index, title, lines) {
133672
+ const body = lines.length > 0 ? lines.join("\n") : "\u672C\u671F\u8BE5\u7EF4\u5EA6\u65E0\u8865\u5145\u8BF4\u660E\u3002";
133673
+ return `${index}. ${title}
133674
+ ${body}`;
133639
133675
  }
133640
133676
  function buildSummaryNarrative(data) {
133641
- const blocks = [];
133642
133677
  const kpis = asRecord15(data.kpis) ?? {};
133643
133678
  const meta = asRecord15(data.meta) ?? {};
133644
- const period = str(meta.periodLabel) || `${str(meta.startDate)} ~ ${str(meta.endDate)}`;
133679
+ const sections = asRecord15(data.sections) ?? {};
133680
+ const period = str(meta.periodLabel) || `${str(meta.startDate)} ~ ${str(meta.endDate)}`.trim();
133681
+ const account = str(meta.accountName) || "Facebook \u5E7F\u544A\u8D26\u6237";
133645
133682
  const campaign = str(meta.campaignName);
133646
- blocks.push(
133647
- [
133648
- `${period} Facebook \u5E7F\u544A\u8D26\u6237`,
133649
- campaign ? `\u5E7F\u544A\u7CFB\u5217\uFF1A${campaign}` : "",
133650
- `\u82B1\u8D39 ${num3(kpis.spend) ?? "\u2014"}\uFF0C\u6210\u6548 ${num3(kpis.results) ?? "\u2014"}\uFF0C\u5355\u6B21\u6210\u6548\u8D39\u7528 ${num3(kpis.costPerResult) ?? "\u2014"}\u3002`,
133651
- `\u8986\u76D6 ${num3(kpis.reach) ?? "\u2014"}\uFF0C\u5C55\u793A ${num3(kpis.impressions) ?? "\u2014"}\uFF0C\u9891\u6B21 ${num3(kpis.frequency) ?? "\u2014"}\u3002`
133652
- ].filter(Boolean).join("\n")
133683
+ const resultLabel = displayResultType(kpis.resultType ?? meta.resultType) || "\u6210\u6548";
133684
+ const resultNoun = /潜在客户|lead/i.test(resultLabel) ? "\u4E2A\u6F5C\u5728\u5BA2\u6237\u8868\u5355" : `\u4E2A${resultLabel}`;
133685
+ const firstQuestion = asRecord15(asArray2(data.fourQuestions)[0]);
133686
+ const extra = str(firstQuestion?.verdict);
133687
+ const spendLine = [
133688
+ campaign ? `\uFF08\u5E7F\u544A\u7CFB\u5217\uFF1A${campaign}\uFF09` : "",
133689
+ `\u5171\u6D88\u8017 ${moneyText(kpis.spend)}\uFF0C\u83B7\u53D6 ${num3(kpis.results) ?? "\u2014"} ${resultNoun}\uFF0C\u5E73\u5747\u5355\u6B21\u83B7\u5BA2\u6210\u672C ${moneyText(kpis.costPerResult)}\u3002`,
133690
+ extra
133691
+ ].filter(Boolean).join("");
133692
+ const partOne = [`\u4E00\u3001${account}${period ? ` ${period}` : ""} Facebook \u5E7F\u544A\u8D26\u6237`, spendLine].join(
133693
+ "\n"
133653
133694
  );
133654
- for (const raw of asArray2(data.fourQuestions)) {
133655
- const q = asRecord15(raw);
133656
- if (!q) continue;
133657
- const lines = [
133658
- str(q.verdict) ? `\u7ED3\u8BBA\uFF1A${str(q.verdict)}` : "",
133659
- ...bullets(q.bullets),
133660
- str(q.action) ? `\u600E\u4E48\u529E\uFF1A${str(q.action)}` : ""
133661
- ].filter(Boolean);
133662
- blocks.push(joinBlock(str(q.title) || "\u603B\u89C8\u95EE\u9898", lines));
133663
- }
133664
- for (const raw of asArray2(data.recommendations)) {
133695
+ const partTwo = [
133696
+ "\u4E8C\u3001\u5206\u6790\u4E0E\u4F18\u5316\u5EFA\u8BAE",
133697
+ "",
133698
+ numberedSection(1, "\u56FD\u5BB6/\u5730\u533A\u8868\u73B0\u5206\u6790", insightLines(sections.country)),
133699
+ "",
133700
+ numberedSection(2, "\u53D7\u4F17\u7ED3\u6784\u4E0E\u7EBF\u7D22\u8D28\u91CF\u98CE\u9669", insightLines(sections.audience)),
133701
+ "",
133702
+ numberedSection(3, "\u5E7F\u544A\u9891\u6B21\u4E0E\u65E5\u8D8B\u52BF", insightLines(sections.daily))
133703
+ ].join("\n");
133704
+ const recBlocks = asArray2(data.recommendations).map((raw, i) => {
133665
133705
  const rec = asRecord15(raw);
133666
- if (!rec) continue;
133706
+ if (!rec) return "";
133667
133707
  const title = [str(rec.title), str(rec.tag)].filter(Boolean).join(" \xB7 ");
133668
- blocks.push(joinBlock(title || "\u4F18\u5316\u5EFA\u8BAE", bullets(rec.items)));
133669
- }
133670
- const sections = asRecord15(data.sections) ?? {};
133671
- for (const key of ["daily", "country", "campaigns", "audience"]) {
133672
- const insight = asRecord15(asRecord15(sections[key])?.insight);
133673
- if (!insight) continue;
133674
- const analysis = bullets(insight.analysis);
133675
- const advice = bullets(insight.advice);
133676
- if (analysis.length) blocks.push(joinBlock(`${key} \u5206\u6790`, analysis));
133677
- if (advice.length) blocks.push(joinBlock(`${key} \u5EFA\u8BAE`, advice));
133678
- }
133679
- return blocks;
133708
+ return numberedSection(i + 1, title || "\u4F18\u5316\u5EFA\u8BAE", bullets(rec.items));
133709
+ }).filter(Boolean);
133710
+ const partThree = ["\u4E09\u3001\u4E0B\u4E00\u6B65\u4F18\u5316\u5EFA\u8BAE", "", recBlocks.join("\n\n") || "\u672C\u671F\u6682\u65E0\u4F18\u5316\u5EFA\u8BAE\u3002"].join(
133711
+ "\n"
133712
+ );
133713
+ return [partOne, partTwo, partThree];
133680
133714
  }
133681
133715
  function buildMetaPeriodExcelSheets(data) {
133682
133716
  const meta = asRecord15(data.meta) ?? {};
133683
133717
  const kpis = asRecord15(data.kpis) ?? {};
133684
133718
  const tables = asRecord15(data.tables) ?? {};
133685
133719
  const currency = str(meta.currency) || "USD";
133720
+ const resultType = displayResultType(kpis.resultType ?? meta.resultType);
133721
+ const attribution = displayAttribution(kpis.attributionSetting ?? meta.attributionSetting);
133686
133722
  const summaryRows = [
133687
133723
  metricHeaders(currency, "\u76EE\u6807"),
133688
133724
  [
133689
- str(kpis.resultType) || str(meta.resultType),
133725
+ resultType,
133690
133726
  ...metricCells({
133691
133727
  ...kpis,
133692
- attributionSetting: kpis.attributionSetting ?? meta.attributionSetting,
133693
- resultType: kpis.resultType ?? meta.resultType
133728
+ attributionSetting: attribution,
133729
+ resultType
133694
133730
  })
133695
133731
  ],
133696
133732
  [],
133697
133733
  ...buildSummaryNarrative(data).map((text2) => [text2])
133698
133734
  ];
133699
- const dailyRows = [metricHeaders(currency, "\u65E5\u671F")];
133735
+ const dailyRows = [metricHeaders(currency, "\u5355\u65E5")];
133700
133736
  for (const raw of asArray2(tables.daily)) {
133701
133737
  const row = asRecord15(raw);
133702
133738
  if (!row) continue;
133703
- dailyRows.push([str(row.date), ...metricCells(row)]);
133739
+ dailyRows.push([normalizeInsightDate(row.date), ...metricCells(row)]);
133704
133740
  }
133705
133741
  const countryRows = [metricHeaders(currency, "\u56FD\u5BB6/\u5730\u533A")];
133706
133742
  for (const raw of asArray2(tables.countries)) {
@@ -133734,7 +133770,7 @@ function buildMetaPeriodExcelSheets(data) {
133734
133770
  audienceRows.push([str(row.age), str(row.gender), ...metricCells(row)]);
133735
133771
  }
133736
133772
  return [
133737
- { name: "\u6C47\u603B", rows: summaryRows },
133773
+ { name: "\u6C47\u603B\u6570\u636E", rows: summaryRows },
133738
133774
  { name: "\u65E5\u8D8B\u52BF", rows: dailyRows },
133739
133775
  { name: "\u56FD\u5BB6", rows: countryRows },
133740
133776
  { name: "\u5E7F\u544A\u7CFB\u5217", rows: campaignRows2 },
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.48-beta.7",
4
- "publishedAt": 1787733228580
3
+ "version": "1.1.48-beta.8",
4
+ "publishedAt": 1787738525935
5
5
  }
@@ -1,6 +1,6 @@
1
1
  # Meta 周期报告撰写规则(Agent 用)
2
2
 
3
- > 对照运营交付物:浅色 HTML(汇总 / 日趋势 / 国家 / 广告系列 / 受众 / 优化建议)+ 五 Sheet Excel
3
+ > 对照运营交付物:浅色 HTML(汇总 / 日趋势 / 国家 / 广告系列 / 受众 / 优化建议)+ 五 Sheet Excel(**汇总数据** KPI 下必须是 **一 / 二 / 三**)。
4
4
  > **禁止**只交 KPI 表或空话建议。
5
5
 
6
6
  ---
@@ -27,7 +27,7 @@
27
27
  | `google-account-diagnosis-report.md` | Google 账户深度诊断(健康度/转化/结构等) |
28
28
  | `google-ads-diagnosis.md` | Google **广告诊断**完整纲要(HTML 区块、**每日趋势 2 位小数**、**每模块必填分析/建议**) |
29
29
  | `meta-period-report.md` / `.html` | **Meta(Facebook)周期报告(默认)**:四步流程 → `facebook-analysis render` 出 HTML,见 P4-FB |
30
- | `meta-period-report-excel.md` | **仅用户要 Excel 时**:5 Sheet(汇总/日趋势/国家/广告系列/受众);`facebook-analysis render --format xlsx` |
30
+ | `meta-period-report-excel.md` | **仅用户要 Excel 时**:5 Sheet(**汇总数据**/日趋势/国家/广告系列/受众,汇总 KPI 下是一/二/三);`facebook-analysis render --format xlsx` |
31
31
  | `meta-account-diagnosis-report.md` / `.html` | **Meta(Facebook)诊断报告(默认)**:四步流程 → `facebook-analysis diagnosis-render` 出 HTML(在 7 Section 内对齐 Google 诊断结构,未覆盖小节标 `notAvailable`) |
32
32
  | `tiktok-period-report.md` / `.html` | **TikTok 广告主周期报告(默认)**:四步流程 → `tiktok-analysis render` 出 HTML |
33
33
  | `bing-period-report.md` / `.html` | **Bing(BingV2)分析报告(默认)**:四步流程 → `bing-analysis render` 出 HTML |
@@ -3,7 +3,7 @@
3
3
  > **适用场景**:用户**明确要求 Excel / xlsx / 表格形式**时读本文件。
4
4
  > 用户未指定格式时,**默认交付 HTML**,走 `meta-period-report.md` 标准四步。
5
5
 
6
- > 对照业务交付物:`光盈光电7月fb广告报告.xlsx`(5 个 Sheet:汇总 / 日趋势 / 国家 / 广告系列 / 受众)。
6
+ > 对照业务交付物:`光盈光电7月fb广告报告.xlsx`(5 个 Sheet:**汇总数据** / 日趋势 / 国家 / 广告系列 / 受众)。
7
7
  > **由 CLI 生成**:`facebook-analysis render --format xlsx`,不要手写 xlsx,也不要假设还有别的 excel 子命令。
8
8
  > **ID 列硬规则**:账户 / 系列等 id **一律写成字符串(文本)**;见 `references/core/agent-conventions.md`。
9
9
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  | Sheet | 中文名 | CLI `--sections` | 落盘文件 | 角色 |
15
15
  | ----- | ---------- | ---------------- | ---------------------- | ---------------------------- |
16
- | 1 | **汇总** | `overview` | `overview-<id>.json` | KPI 首行 + 四问/建议叙事 |
16
+ | 1 | **汇总数据** | `overview` | `overview-<id>.json` | KPI 首行 + **一 / 二 / 三** 叙事 |
17
17
  | 2 | **日趋势** | `daily` | `daily-<id>.json` | 按日表(`days[]`) |
18
18
  | 3 | **国家** | `country` | `country-<id>.json` | 国家/地区表 |
19
19
  | 4 | **广告系列** | `campaigns` | `campaigns-<id>.json` | 系列表 |
@@ -36,7 +36,7 @@
36
36
  | 已花费金额 (USD) | `spend` | 账户币种,非 micros |
37
37
  | 单次成效费用 | `costPerResult` | CPL |
38
38
 
39
- Sheet 2 首列:`日期`(`date`,YYYY-MM-DD)。
39
+ Sheet 2 首列:`单日`(`date`,YYYY-MM-DD)。
40
40
  Sheet 3 首列:`国家/地区`(`countryOrRegion`)。
41
41
  Sheet 4 首列:`广告系列名称`(`campaignName`)。
42
42
  Sheet 5 另加:`年龄`(`age`)、`性别`(`gender`)。
@@ -45,6 +45,20 @@ Sheet 5 另加:`年龄`(`age`)、`性别`(`gender`)。
45
45
 
46
46
  ---
47
47
 
48
+ ## 汇总数据叙事(KPI 下方,格式的一部分)
49
+
50
+ 空 1~2 行后,**只写三块**,标题字面必须是:
51
+
52
+ | 块 | 标题 | 内容从哪来 |
53
+ | -- | ---- | ---------- |
54
+ | **一** | `一、{账户名} {周期} Facebook 广告账户` | 系列名 + 花费/成效/单次成本(CLI 用 KPI 拼);可接 `fourQuestions[0].verdict` |
55
+ | **二** | `二、分析与优化建议` | 下面固定 3 小节:`1. 国家/地区表现分析`、`2. 受众结构与线索质量风险`、`3. 广告频次与日趋势`(来自 `sections.country/audience/daily.insight`) |
56
+ | **三** | `三、下一步优化建议` | `recommendations[]` 按 `1. 2. 3.` 编号 |
57
+
58
+ **禁止**在 Excel 汇总数据里写「钱花得值不值?」四问标题,或 `daily 分析` / `country 建议` 这种机读标题。四问仍写在 Agent JSON 里给 HTML 用。
59
+
60
+ ---
61
+
48
62
  ## 拉数与渲染
49
63
 
50
64
  ```bash
@@ -51,7 +51,7 @@ siluzan-tso facebook-analysis render \
51
51
  ```
52
52
 
53
53
  同时要 HTML + Excel:`--format html,xlsx`。
54
- 版式见 **`report-templates/meta-period-report-excel.md`**(5 Sheet:汇总 / 日趋势 / 国家 / 广告系列 / 受众)。
54
+ 版式见 **`report-templates/meta-period-report-excel.md`**(5 Sheet:**汇总数据** / 日趋势 / 国家 / 广告系列 / 受众;汇总 KPI 下是 **一 / 二 / 三**)。
55
55
 
56
56
  ---
57
57
 
@@ -27,7 +27,7 @@
27
27
  | `google-account-diagnosis-report.md` | Google 账户深度诊断(健康度/转化/结构等) |
28
28
  | `google-ads-diagnosis.md` | Google **广告诊断**完整纲要(HTML 区块、**每日趋势 2 位小数**、**每模块必填分析/建议**) |
29
29
  | `meta-period-report.md` / `.html` | **Meta(Facebook)周期报告(默认)**:四步流程 → `facebook-analysis render` 出 HTML,见 P4-FB |
30
- | `meta-period-report-excel.md` | **仅用户要 Excel 时**:5 Sheet(汇总/日趋势/国家/广告系列/受众);`facebook-analysis render --format xlsx` |
30
+ | `meta-period-report-excel.md` | **仅用户要 Excel 时**:5 Sheet(**汇总数据**/日趋势/国家/广告系列/受众,汇总 KPI 下是一/二/三);`facebook-analysis render --format xlsx` |
31
31
  | `meta-account-diagnosis-report.md` / `.html` | **Meta(Facebook)诊断报告(默认)**:四步流程 → `facebook-analysis diagnosis-render` 出 HTML(在 7 Section 内对齐 Google 诊断结构,未覆盖小节标 `notAvailable`) |
32
32
  | `tiktok-period-report.md` / `.html` | **TikTok 广告主周期报告(默认)**:四步流程 → `tiktok-analysis render` 出 HTML |
33
33
  | `bing-period-report.md` / `.html` | **Bing(BingV2)分析报告(默认)**:四步流程 → `bing-analysis render` 出 HTML |
@@ -3,7 +3,7 @@
3
3
  > **适用场景**:用户**明确要求 Excel / xlsx / 表格形式**时读本文件。
4
4
  > 用户未指定格式时,**默认交付 HTML**,走 `meta-period-report.md` 标准四步。
5
5
 
6
- > 对照业务交付物:`光盈光电7月fb广告报告.xlsx`(5 个 Sheet:汇总 / 日趋势 / 国家 / 广告系列 / 受众)。
6
+ > 对照业务交付物:`光盈光电7月fb广告报告.xlsx`(5 个 Sheet:**汇总数据** / 日趋势 / 国家 / 广告系列 / 受众)。
7
7
  > **由 CLI 生成**:`facebook-analysis render --format xlsx`,不要手写 xlsx,也不要假设还有别的 excel 子命令。
8
8
  > **ID 列硬规则**:账户 / 系列等 id **一律写成字符串(文本)**;见 `references/core/agent-conventions.md`。
9
9
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  | Sheet | 中文名 | CLI `--sections` | 落盘文件 | 角色 |
15
15
  | ----- | ---------- | ---------------- | ---------------------- | ---------------------------- |
16
- | 1 | **汇总** | `overview` | `overview-<id>.json` | KPI 首行 + 四问/建议叙事 |
16
+ | 1 | **汇总数据** | `overview` | `overview-<id>.json` | KPI 首行 + **一 / 二 / 三** 叙事 |
17
17
  | 2 | **日趋势** | `daily` | `daily-<id>.json` | 按日表(`days[]`) |
18
18
  | 3 | **国家** | `country` | `country-<id>.json` | 国家/地区表 |
19
19
  | 4 | **广告系列** | `campaigns` | `campaigns-<id>.json` | 系列表 |
@@ -36,7 +36,7 @@
36
36
  | 已花费金额 (USD) | `spend` | 账户币种,非 micros |
37
37
  | 单次成效费用 | `costPerResult` | CPL |
38
38
 
39
- Sheet 2 首列:`日期`(`date`,YYYY-MM-DD)。
39
+ Sheet 2 首列:`单日`(`date`,YYYY-MM-DD)。
40
40
  Sheet 3 首列:`国家/地区`(`countryOrRegion`)。
41
41
  Sheet 4 首列:`广告系列名称`(`campaignName`)。
42
42
  Sheet 5 另加:`年龄`(`age`)、`性别`(`gender`)。
@@ -45,6 +45,20 @@ Sheet 5 另加:`年龄`(`age`)、`性别`(`gender`)。
45
45
 
46
46
  ---
47
47
 
48
+ ## 汇总数据叙事(KPI 下方,格式的一部分)
49
+
50
+ 空 1~2 行后,**只写三块**,标题字面必须是:
51
+
52
+ | 块 | 标题 | 内容从哪来 |
53
+ | -- | ---- | ---------- |
54
+ | **一** | `一、{账户名} {周期} Facebook 广告账户` | 系列名 + 花费/成效/单次成本(CLI 用 KPI 拼);可接 `fourQuestions[0].verdict` |
55
+ | **二** | `二、分析与优化建议` | 下面固定 3 小节:`1. 国家/地区表现分析`、`2. 受众结构与线索质量风险`、`3. 广告频次与日趋势`(来自 `sections.country/audience/daily.insight`) |
56
+ | **三** | `三、下一步优化建议` | `recommendations[]` 按 `1. 2. 3.` 编号 |
57
+
58
+ **禁止**在 Excel 汇总数据里写「钱花得值不值?」四问标题,或 `daily 分析` / `country 建议` 这种机读标题。四问仍写在 Agent JSON 里给 HTML 用。
59
+
60
+ ---
61
+
48
62
  ## 拉数与渲染
49
63
 
50
64
  ```bash
@@ -51,7 +51,7 @@ siluzan-tso facebook-analysis render \
51
51
  ```
52
52
 
53
53
  同时要 HTML + Excel:`--format html,xlsx`。
54
- 版式见 **`report-templates/meta-period-report-excel.md`**(5 Sheet:汇总 / 日趋势 / 国家 / 广告系列 / 受众)。
54
+ 版式见 **`report-templates/meta-period-report-excel.md`**(5 Sheet:**汇总数据** / 日趋势 / 国家 / 广告系列 / 受众;汇总 KPI 下是 **一 / 二 / 三**)。
55
55
 
56
56
  ---
57
57
 
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  $PKG_NAME = 'siluzan-tso-cli'
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- $PKG_VERSION = '1.1.48-beta.7'
12
+ $PKG_VERSION = '1.1.48-beta.8'
13
13
  $CLI_BIN = 'siluzan-tso'
14
14
  $SKILL_LABEL = 'Siluzan TSO'
15
15
  $INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
@@ -9,7 +9,7 @@ set -euo pipefail
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  readonly PKG_NAME="siluzan-tso-cli"
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- readonly PKG_VERSION="1.1.48-beta.7"
12
+ readonly PKG_VERSION="1.1.48-beta.8"
13
13
  readonly CLI_BIN="siluzan-tso"
14
14
  readonly SKILL_LABEL="Siluzan TSO"
15
15
  readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.48-beta.7",
3
+ "version": "1.1.48-beta.8",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",