siluzan-tso-cli 1.0.0-beta.29 → 1.0.0-beta.30

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
@@ -20,7 +20,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
20
20
  siluzan-tso init --force # 强制覆盖已存在文件
21
21
  ```
22
22
 
23
- > **注意**:当前为测试版(1.0.0-beta.29),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
23
+ > **注意**:当前为测试版(1.0.0-beta.30),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
24
24
 
25
25
  | 助手 | 建议 `--ai` |
26
26
  |------|-------------|
package/dist/index.js CHANGED
@@ -1239,7 +1239,7 @@ var ASCII_TABLE_CHARS = {
1239
1239
  };
1240
1240
  function printCliTable(rows, columns, options) {
1241
1241
  if (rows.length === 0) return;
1242
- const plain = !!options?.plain;
1242
+ const plain = options?.plain !== false;
1243
1243
  const indent = options?.indent ?? " ";
1244
1244
  const printLine = options?.printLine ?? ((msg) => {
1245
1245
  console.log(msg);
@@ -1628,19 +1628,19 @@ async function runListAccounts(opts) {
1628
1628
  const isMetaAd = opts.media === "MetaAd";
1629
1629
  const isBingV2 = opts.media === "BingV2";
1630
1630
  const isKwai = opts.media === "Kwai";
1631
- const plain = !!opts.plain;
1631
+ const tableBorderOpts = opts.unicode ? { plain: false } : void 0;
1632
1632
  if (isGoogle) {
1633
- printGoogleTable(items, plain);
1633
+ printGoogleTable(items, tableBorderOpts);
1634
1634
  } else if (isYandex) {
1635
- printYandexTable(items, plain);
1635
+ printYandexTable(items, tableBorderOpts);
1636
1636
  } else if (isTikTok) {
1637
- printTikTokTable(items, plain);
1637
+ printTikTokTable(items, tableBorderOpts);
1638
1638
  } else if (isMetaAd) {
1639
- printMetaTable(items, plain);
1639
+ printMetaTable(items, tableBorderOpts);
1640
1640
  } else if (isBingV2 || isKwai) {
1641
- printSpendMetricsAccountTable(items, plain);
1641
+ printSpendMetricsAccountTable(items, tableBorderOpts);
1642
1642
  } else {
1643
- printDefaultTable(items, opts.media, plain);
1643
+ printDefaultTable(items, opts.media, tableBorderOpts);
1644
1644
  }
1645
1645
  const hasMore = total !== void 0 ? page * pageSize < total : items.length >= pageSize;
1646
1646
  if (hasMore) {
@@ -1806,7 +1806,7 @@ function formatMetaAuditStatus(ma) {
1806
1806
  };
1807
1807
  return strMap[u] ?? String(raw);
1808
1808
  }
1809
- function printMetaTable(items, plain) {
1809
+ function printMetaTable(items, tableOpts) {
1810
1810
  const rows = items.map((item) => {
1811
1811
  const ma = item.ma;
1812
1812
  return {
@@ -1819,9 +1819,9 @@ function printMetaTable(items, plain) {
1819
1819
  createdAt: fmtDate(ma.createdDateTime)
1820
1820
  };
1821
1821
  });
1822
- printCliTable(rows, COL_META, { plain });
1822
+ printCliTable(rows, COL_META, tableOpts);
1823
1823
  }
1824
- function printSpendMetricsAccountTable(items, plain) {
1824
+ function printSpendMetricsAccountTable(items, tableOpts) {
1825
1825
  const rows = items.map((item) => {
1826
1826
  const ma = item.ma;
1827
1827
  const authStatus = ma.invalidOAuthToken ? "\u26A0\uFE0F \u5931\u6548" : "\u2705 \u6B63\u5E38";
@@ -1842,9 +1842,9 @@ function printSpendMetricsAccountTable(items, plain) {
1842
1842
  shared: sharedCount
1843
1843
  };
1844
1844
  });
1845
- printCliTable(rows, COL_SPEND_METRICS, { plain });
1845
+ printCliTable(rows, COL_SPEND_METRICS, tableOpts);
1846
1846
  }
1847
- function printYandexTable(items, plain) {
1847
+ function printYandexTable(items, tableOpts) {
1848
1848
  const rows = items.map((item) => {
1849
1849
  const ma = item.ma;
1850
1850
  const authStatus = ma.invalidOAuthToken ? "\u26A0\uFE0F \u5931\u6548" : "\u2705 \u6B63\u5E38";
@@ -1866,9 +1866,9 @@ function printYandexTable(items, plain) {
1866
1866
  shared: sharedCount
1867
1867
  };
1868
1868
  });
1869
- printCliTable(rows, COL_YANDEX, { plain });
1869
+ printCliTable(rows, COL_YANDEX, tableOpts);
1870
1870
  }
1871
- function printTikTokTable(items, plain) {
1871
+ function printTikTokTable(items, tableOpts) {
1872
1872
  const rows = items.map((item) => {
1873
1873
  const ma = item.ma;
1874
1874
  const authStatus = ma.invalidOAuthToken ? "\u26A0\uFE0F \u5931\u6548" : "\u2705 \u6B63\u5E38";
@@ -1891,9 +1891,9 @@ function printTikTokTable(items, plain) {
1891
1891
  shared: sharedCount
1892
1892
  };
1893
1893
  });
1894
- printCliTable(rows, COL_TIKTOK, { plain });
1894
+ printCliTable(rows, COL_TIKTOK, tableOpts);
1895
1895
  }
1896
- function printGoogleTable(items, plain) {
1896
+ function printGoogleTable(items, tableOpts) {
1897
1897
  const rows = items.map((item) => {
1898
1898
  const ma = item.ma;
1899
1899
  const authStatus = ma.invalidOAuthToken ? "\u26A0\uFE0F \u5931\u6548" : "\u2705 \u6B63\u5E38";
@@ -1917,7 +1917,7 @@ function printGoogleTable(items, plain) {
1917
1917
  shared: sharedCount
1918
1918
  };
1919
1919
  });
1920
- printCliTable(rows, COL_GOOGLE, { plain });
1920
+ printCliTable(rows, COL_GOOGLE, tableOpts);
1921
1921
  }
1922
1922
  var COL_DEFAULT_WITH_MEDIA = [
1923
1923
  { key: "mediaType", header: "\u5A92\u4F53\u7C7B\u578B" },
@@ -1928,7 +1928,7 @@ var COL_DEFAULT_WITH_MEDIA = [
1928
1928
  { key: "status", header: "\u8D26\u6237\u72B6\u6001" }
1929
1929
  ];
1930
1930
  var COL_DEFAULT_SINGLE = COL_DEFAULT_WITH_MEDIA.filter((c) => c.key !== "mediaType");
1931
- function printDefaultTable(items, media, plain) {
1931
+ function printDefaultTable(items, media, tableOpts) {
1932
1932
  const rows = items.map((item) => {
1933
1933
  const ma = item.ma;
1934
1934
  let statusLabel;
@@ -1952,7 +1952,7 @@ function printDefaultTable(items, media, plain) {
1952
1952
  };
1953
1953
  });
1954
1954
  const columns = media ? COL_DEFAULT_SINGLE : COL_DEFAULT_WITH_MEDIA;
1955
- printCliTable(rows, columns, { plain });
1955
+ printCliTable(rows, columns, tableOpts);
1956
1956
  }
1957
1957
 
1958
1958
  // src/commands/balance.ts
@@ -9358,8 +9358,12 @@ program.command("list-accounts").description("\u67E5\u8BE2\u5E7F\u544A\u8D26\u62
9358
9358
  "\u8D26\u6237\u72B6\u6001\uFF1Anormal\uFF08\u6B63\u5E38\uFF09| invalid\uFF08\u5931\u6548\uFF09| all\uFF08\u5168\u90E8\uFF0C\u9ED8\u8BA4\uFF09",
9359
9359
  "all"
9360
9360
  ).option("-p, --page <n>", "\u9875\u7801\uFF08\u9ED8\u8BA4 1\uFF09", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF\uFF08\u9ED8\u8BA4 20\uFF09", parseInt).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA\u539F\u59CB\u54CD\u5E94", false).option(
9361
+ "--unicode",
9362
+ "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08cli-table3\uFF09\uFF1B\u9ED8\u8BA4 ASCII +-|",
9363
+ false
9364
+ ).option(
9361
9365
  "--plain",
9362
- "\u4F7F\u7528 ASCII \u7EBF\u6846\u8868\u683C\uFF08cli-table3\uFF09\uFF1B\u9ED8\u8BA4\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C",
9366
+ "\u5DF2\u9ED8\u8BA4 ASCII \u7EBF\u6846\uFF0C\u65E0\u9700\u518D\u4F20\uFF1B\u4FDD\u7559\u517C\u5BB9\u65E7\u811A\u672C",
9363
9367
  false
9364
9368
  ).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
9365
9369
  await runListAccounts({
@@ -9370,7 +9374,7 @@ program.command("list-accounts").description("\u67E5\u8BE2\u5E7F\u544A\u8D26\u62
9370
9374
  page: opts.page,
9371
9375
  pageSize: opts.pageSize,
9372
9376
  json: opts.json,
9373
- plain: opts.plain,
9377
+ unicode: opts.unicode,
9374
9378
  verbose: opts.verbose
9375
9379
  });
9376
9380
  });
@@ -10649,10 +10653,7 @@ openAccountCmd.command("kwai").description("\u63D0\u4EA4 Kwai \u5F00\u6237\u7533
10649
10653
  });
10650
10654
  program.command("diagnostic").description("Google \u5E7F\u544A\u8BCA\u65AD\uFF1A\u8F93\u51FA\u4E1D\u8DEF\u8D5E\u5E7F\u544A\u8BCA\u65AD\u62A5\u544A\u9875\u9762\u5730\u5740").action(() => {
10651
10655
  console.log("\nGoogle \u5E7F\u544A\u8BCA\u65AD\n");
10652
- console.log(" \u4E1D\u8DEF\u8D5E\u63D0\u4F9B\u4E13\u4E1A\u7684 Google \u5E7F\u544A\u8BCA\u65AD\u62A5\u544A\uFF0C\u5E2E\u52A9\u5206\u6790\u8D26\u6237\u5065\u5EB7\u5EA6\u5E76\u751F\u6210\u4F18\u5316\u5EFA\u8BAE\u3002\n");
10653
- console.log(" \u8BCA\u65AD\u5165\u53E3\uFF08\u751F\u4EA7\uFF09\uFF1Ahttps://www.siluzan.com/diagnostics/");
10654
- console.log(" \u8BCA\u65AD\u5165\u53E3\uFF08\u6D4B\u8BD5\uFF09\uFF1Ahttps://www-ci.siluzan.com/diagnostics/\n");
10655
- console.log(" \u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u4E0A\u8FF0\u5730\u5740\u5B8C\u6210\u8BCA\u65AD\u64CD\u4F5C\u3002\n");
10656
+ console.log(" siluzan.com \u63D0\u4F9B\u4E13\u4E1A\u7684 Google \u5E7F\u544A\u8BCA\u65AD\u62A5\u544A\uFF0C\u5E2E\u52A9\u5206\u6790\u8D26\u6237\u5065\u5EB7\u5EA6\u5E76\u751F\u6210\u4F18\u5316\u5EFA\u8BAE\u3002\n");
10656
10657
  });
10657
10658
  var activeCmd = process.argv[2];
10658
10659
  program.parseAsync().then(() => {
@@ -0,0 +1,62 @@
1
+ > 改进观察应写入 `user-adaptations.md`(见下方),由 Skill 维护者人工决定是否纳入正式 Skill 版本。
2
+
3
+ 1. 记录失败的测试编号与实际输出
4
+ 2. 定位到具体 Skill 文档段落(哪条规则未被遵守)
5
+ 3. **将改进建议追加到 `user-adaptations.md`**(格式见下方),不修改 Skill 文件本身
6
+ 4. 重新验证失败用例,通过后由维护者决定是否发布
7
+
8
+ ---
9
+
10
+ ## user-adaptations.md 格式规范
11
+
12
+ `user-adaptations.md` 是一份**用户本地适配文件**,存放在userpath/.siluzan/user-adaptations.md (如果没有则你主动创建)
13
+ 它**不属于 Skill 包**,执行 `siluzan-tso update` 时不会被覆盖。
14
+
15
+ ### 用途
16
+
17
+ - 记录用户在本项目中的操作习惯与偏好
18
+ - 记录回归测试中发现的改进点(留给维护者参考)
19
+ - 作为本 Skill 的**补充规则**,优先级低于官方 Skill 文档
20
+
21
+
22
+ ### 条目格式
23
+
24
+ ```markdown
25
+ ## 用户适配记录
26
+
27
+ ### [YYYY-MM-DD] <简短标题>
28
+
29
+ - **类别**:[报告习惯 / 命令偏好 / 媒体偏好 / 账户配置 / 其他]
30
+ - **观察**:<描述用户表现出的习惯或偏好>
31
+ - **适配规则**:<在本项目中应当如何调整行为>
32
+ - **来源**:[用户明确告知 / 会话观察推断 / 回归测试发现]
33
+
34
+ ---
35
+ ```
36
+
37
+ ### 示例条目
38
+
39
+ ```markdown
40
+ ## 用户适配记录
41
+
42
+ ### [2025-04-01] 用户偏好不展示门禁一声明
43
+
44
+ - **类别**:报告习惯
45
+ - **观察**:用户多次反馈"不需要看类型判断声明,直接给报告"
46
+ - **适配规则**:本项目中,满足快速路径条件时直接生成,省略门禁一输出
47
+ - **来源**:用户明确告知
48
+
49
+ ---
50
+
51
+ ### [2025-04-03] 默认账户 ID
52
+
53
+ - **类别**:账户配置
54
+ - **观察**:用户本项目主要操作 Google 账户 4545500137
55
+ - **适配规则**:当用户说"那个 Google 账户"未指定 ID 时,优先询问是否为 4545500137
56
+ - **来源**:会话观察推断
57
+
58
+ ---
59
+ ```
60
+
61
+ ---
62
+
@@ -32,11 +32,11 @@ description: >-
32
32
  | `references/google-ads-test.md` | Google 广告业务矩阵与 Agent 测试 |
33
33
  | `references/aigc.md` | AIGC 广告方案数据准备(`aigc prepare`)及落地流程 |
34
34
  | `references/reporting.md` | TSO steward 报告、Google 网关分析、`google-analysis`、**Meta/TikTok/Bing TSO 分析**(`report meta-overview`、`report tiktok-*`、`report bing-*`)、AI 智投 / 优化 / 预警 / 线索 |
35
- | `report-templates/` | Google / Meta / TikTok / Bing 周期与诊断类报告 **Markdown 纲要与Style样式参考html文件**(与 `google-analysis`、`report tiktok-*`、`report bing-*` 等配合) |
35
+ | `report-templates/` | Google / Meta / TikTok / Bing 报告资源目录。**`.md` 是内容必选纲要;`.html` 只是视觉样式参考**(与 `google-analysis`、`report tiktok-*`、`report bing-*` 等配合) |
36
36
  | `references/finance.md` | 转账 / 发票(充值需网页) |
37
37
  | `references/tso-home.md` | TSO 首页看板页面与 CLI 对照 |
38
38
  | `references/setup.md` | 安装 / 登录 / 配置 / 环境切换 |
39
-
39
+ | `EVAL-HARNESS.md`|自我升级|
40
40
  ---
41
41
 
42
42
  ## 业务流程速查
@@ -151,4 +151,48 @@ CLI 命令执行后可能在输出末尾出现以下两种标记,Agent 必须
151
151
  - **分页**:默认每页 20 条,数据量大时加 `--page-size 100` 或 `--page` 翻页。
152
152
  - **充值等网页操作**:先 `config show` 取 `webUrl`,再拼路径引导用户。
153
153
  - **Google 新开户**:终端用户优先建议 `open-account google-wizard`;说明网页「五步」里**充值激活**必须网页完成。详见 `references/open-account-google-ui.md`。
154
+ - **报告目录优先级**:生成报告时,先读 `report-templates/*.md` 定义内容,再读 `report-template*.html` 选择样式;**内容优先级永远高于样式**。
154
155
  - **不确定时读文档**:遇到不熟悉的命令,先读对应 references 文件,不要猜参数。
156
+
157
+ ---
158
+
159
+ ## 用户习惯记录(User Adaptations)
160
+
161
+ ### Skill 文件只读原则
162
+
163
+ **Agent 不得修改本目录中的任何 Skill 文件**(包括 `SKILL.md`、`references/*.md`、`report-templates/*.md`、`EVAL-HARNESS.md`)。
164
+ 执行 `siluzan-tso update` 时,这些文件会被 Skill 包覆盖,手动修改会丢失。
165
+
166
+ ### 用户习惯写入 user-adaptations.md
167
+
168
+ 当 Agent 在会话中观察到用户习惯、偏好或项目级配置时,将其**追加**到本 Skill 目录下的 `user-adaptations.md`:
169
+
170
+ ```
171
+ .cursor/skills/siluzan-tso/user-adaptations.md
172
+ ```
173
+
174
+ 此文件**不属于 Skill 包**,`siluzan-tso update` 时不会覆盖。
175
+
176
+ ### 触发条件(何时写入)
177
+
178
+ 满足以下任一条件时,主动写入 `user-adaptations.md`:
179
+ 1. 用户明确告知一个偏好("我不需要看声明"、"默认用这个账户"等)
180
+ 2. 同一会话中用户连续 2 次以上纠正相同行为
181
+ 3. 用户说"记住这个"、"以后都这样"等明确保存意图的表达
182
+
183
+ ### 何时读取
184
+
185
+ 每次新会话开始时,检查 `user-adaptations.md` 是否存在:
186
+ - **存在**:读取并作为本项目的补充规则(优先级低于官方 Skill 文档,但高于默认行为)
187
+ - **不存在**:正常执行,无需创建空文件
188
+
189
+ ### 条目格式
190
+
191
+ ```markdown
192
+ ### [YYYY-MM-DD] <一句话描述>
193
+
194
+ - **类别**:[报告习惯 / 命令偏好 / 账户配置 / 流程偏好 / 其他]
195
+ - **观察**:<具体的用户行为或明确表达>
196
+ - **适配规则**:<本项目中 Agent 应当如何调整>
197
+ - **来源**:[用户明确告知 / 会话观察]
198
+ ```
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.0.0-beta.29",
4
- "publishedAt": 1774495966996
3
+ "version": "1.0.0-beta.30",
4
+ "publishedAt": 1774507264267
5
5
  }
@@ -18,6 +18,8 @@ siluzan-tso list-accounts [选项]
18
18
  | `-p, --page <n>` | 页码(默认 1) |
19
19
  | `--page-size <n>` | 每页数量(默认 20) |
20
20
  | `--json` | 输出原始 JSON |
21
+ | `--unicode` | 表格使用 Unicode 线框;**默认**为 ASCII `+-|` 线框(兼容各类终端) |
22
+ | `--plain` | 已默认 ASCII,无需再传;保留兼容旧脚本 |
21
23
 
22
24
  **示例:**
23
25
 
@@ -1,32 +1,145 @@
1
- # 报告模板(账户分析纲要)
2
-
3
- 本目录为 **Markdown 纲要**
4
-
5
- ## 报告应有的内容
6
- 对应类型的报告应有哪些内容可以参考以下markdown文件
7
- | 内容描述文件 | 适用场景 |
8
- |----------|----------|
9
- | `google-period-report.md` | 按单段时间做系列/地域/设备/关键词等汇总 |
10
- | `google-account-diagnosis-report.md` | 健康度、结构、定向、创意、搜索词等分章诊断(纲要版) |
11
- | `google-ads-diagnosis.md` | **与网页版《Google Ads 账户诊断报告》章节、字段对齐**的完整 Markdown 骨架 |
12
- | `meta-period-report.md` | Meta(Facebook)账户周期报告纲要(当前仅总览;见 `report meta-overview`) |
13
- | `tiktok-period-report.md` | TikTok 广告主周期报告纲要(见 `report tiktok-*` 子命令) |
14
- | `bing-period-report.md` | Bing(BingV2)分析纲要(见 `report bing-*`;日期不可含今天/昨天) |
15
-
16
- - **Google**:数据用 `siluzan-tso google-analysis <子命令>` 拉取(需 `googleApiUrl`)。
17
- - **Meta / TikTok / Bing**:走 TSO `reporting/media-account/...`,见 `report meta-overview`、`report tiktok-overview`、`report bing-overview` 等。
18
-
19
- HTTP 路径的对应关系见 `references/reporting.md` 中「Google 账户分析」「Meta」「TikTok」「Bing」等小节。
20
-
21
- ## 报告样式参考
22
- 将 Markdown 纲要转为成品 HTML 时,可选用下列 **同一套区块结构**(`data-section-id` 一致)的版式参考;技术栈均为 Tailwind Play CDN + Font Awesome 6 + ECharts 5。
23
-
24
- | 报告样式文件 | 风格 | 说明 |
25
- |-------------|------|------|
26
- | `report-template.html` | **商务 / 数据看板** | 品牌色、圆角卡片、轻阴影、徽章式环比;适合对内经营分析、运营周报。 |
27
- | `report-template-academic.html` | **专业学术** | 衬线标题(Noto Serif SC / Source Serif 4)、纸本式窄栏、线框表、低饱和图表;适合偏研究/方法说明口吻。 |
28
- | `report-template-formal.html` | **正式文件 / 公文** | 红头线、居中标题、文头信息表、中文序号「一、二、…」、全框黑线表;适合对外的说明性、留痕类文档。 |
29
- | `report-template-dark.html` | **深色 / 演示投屏** | 锌灰底、青紫高对比图表与字号;适合会议室大屏(打印样式会尝试改浅色)。 |
30
- | `report-template-print.html` | **黑白打印** | 纯线框、无圆角阴影、灰阶 ECharts、环比用 ↑↓/加粗;适合归档与传真式阅读。 |
31
- | `report-template-onepager.html` | **对客单页精华** | 首屏叙事 + 主 KPI + 主图;第 2–6 节为 `<details>` 折叠(`data-section-id` 在折叠容器上)。 |
32
- | `report-template-mobile.html` | **移动端优先** | `max-w-lg` 窄栏、大触控留白、表格外滑动提示、饼图图例置底;`sm+` 渐进接近商务版。 |
1
+ # 报告模板(账户分析纲要)
2
+
3
+ 本目录包含两类文件,职责**完全不同**,使用前必须区分:
4
+
5
+ | 文件类型 | 职责 | 权威性 |
6
+ |---------|------|--------|
7
+ | `*.md` | **内容合同**:必写章节、数据来源、最低交付标准 | 最高 — 冲突时以此为准 |
8
+ | `report-template*.html` | **样式参考**:排版、卡片、图表、打印或移动端视觉方案 | 仅限视觉 — 不定义内容 |
9
+
10
+ > 完整的能力边界(哪些媒体能拉哪些数据、命令参数)**只维护**在 `references/reporting.md`,本目录不重复描述。
11
+
12
+ ## 使用顺序(必须遵守)
13
+
14
+ 1. **先读对应的 `*.md`** 确认"这份报告必须写什么"
15
+ 2. **再读 `report-template*.html`** — 确认"这些内容怎么展示"
16
+ 3. **冲突时以 `*.md` 为准** 不能因 HTML 缺某章节就省略 `.md` 要求的内容
17
+ 4. **HTML 中的假数据不是内容** 示例里的假标题、假数字只是 UI 演示
18
+
19
+ ## 内容合同文件(先读这些)
20
+
21
+ | 文件 | 适用场景 |
22
+ |------|---------|
23
+ | `google-period-report.md` | Google 账户周期分析(系列 / 地域 / 设备 / 关键词汇总) |
24
+ | `google-account-diagnosis-report.md` | Google 账户健康度诊断(结构、定向、创意、搜索词等分章) |
25
+ | `google-ads-diagnosis.md` | 与网页版《Google Ads 账户诊断报告》对齐的完整 Markdown 骨架 |
26
+ | `meta-period-report.md` | Meta(Facebook)账户周期报告(当前仅总览;见 `report meta-overview`) |
27
+ | `tiktok-period-report.md` | TikTok 广告主周期报告(见 `report tiktok-*` 子命令) |
28
+ | `bing-period-report.md` | Bing(BingV2)分析(见 `report bing-*`;日期不可含今天/昨天) |
29
+
30
+ **数据来源**:
31
+ - Google:`siluzan-tso google-analysis <子命令>`(需配置 `googleApiUrl`)
32
+ - Meta / TikTok / Bing:`siluzan-tso report <媒体>-overview`,见 `references/reporting.md`
33
+
34
+ ## 样式参考文件(后读这些)
35
+
36
+ 技术栈:Tailwind Play CDN + Font Awesome 6 + ECharts 5
37
+
38
+ | 样式文件 | 风格 | 适用场景 |
39
+ |---------|------|---------|
40
+ | `report-template.html` | 商务 / 数据看板 | 对内经营分析、运营周报 |
41
+ | `report-template-academic.html` | 专业学术 | 研究 / 方法说明口吻 |
42
+ | `report-template-formal.html` | 正式文件 / 公文 | 对外说明性、留痕类文档 |
43
+ | `report-template-dark.html` | 深色 / 演示投屏 | 会议室大屏 |
44
+ | `report-template-print.html` | 黑白打印 | 归档与传真式阅读 |
45
+ | `report-template-onepager.html` | 对客单页精华 | 首屏摘要 + 折叠详情 |
46
+ | `report-template-mobile.html` | 移动端优先 | 手机查看 |
47
+
48
+ ## 未知报告名的处理
49
+
50
+ 当用户要求不在上表中的报告名时,参见 `REPORT-WORKFLOW.md` 中的映射与降级流程。
51
+
52
+ ## 报告生成规则
53
+
54
+ > 设计哲学:**先建轨道,再跑模型**。未知报告名不是失败,编造数据才是失败。
55
+
56
+ ### 报告资源目录的角色划分(单一事实源)
57
+
58
+ | 文件类型 | 职责 | 权威性 |
59
+ |---------|------|--------|
60
+ | `report-templates/*.md` | **内容合同**:必写章节、数据来源、最少交付要求 | 最高,冲突时以此为准 |
61
+ | `report-template*.html` | **样式样板**:排版、色彩、图表、响应式 | 仅限视觉,不定义内容 |
62
+ | `references/reporting.md` | **能力边界**:哪些媒体能拉哪些数据、命令参数 | 能力唯一真源,不在其他地方重复 |
63
+
64
+ ### 门禁一:开始前必须声明(不可跳过)
65
+
66
+ 任何报告生成任务,在拉取数据或写任何内容之前,**先在对话中输出以下声明**:
67
+
68
+ ```
69
+ 报告类型判断:
70
+ - 用户意图:[周期分析 / 诊断 / 对比 / 汇报摘要]
71
+ - 媒体:[Google / Meta / TikTok / Bing / ...]
72
+ - 使用的内容合同(.md):[文件名]
73
+ - 使用的样式模板(.html):[文件名,若需要]
74
+ - 能力覆盖预判:[能覆盖的章节] / [因接口缺失暂无法覆盖的章节]
75
+ ```
76
+
77
+ 未知报告名映射规则(详见 `report-templates/REPORT-WORKFLOW.md`):
78
+ - 无法识别的报告名 → 先映射到 4 个原型之一(周期 / 诊断 / 对比 / 汇报),再选最接近的 `.md`
79
+ - 无对应媒体的 `.md` → 以 Google 对应原型纲要为骨架,注明适配
80
+
81
+ ### 门禁二:数据驱动,禁止编造
82
+
83
+ - **必须**:所有数字结论来自实际执行的 CLI 命令输出;命令 → 字段路径 → 结论,三者可追溯
84
+ - **禁止**:照抄 HTML 示例中的假数据(如 `¥12,580`、`18,420`)填入报告
85
+ - **禁止**:在缺乏接口支持时生成"看起来完整"的章节
86
+ - 若某章节数据无法获取,在报告中**明确标注**,不写内容、不做猜测
87
+
88
+ ### 门禁三:输出前覆盖声明(必须附在每份报告末尾)
89
+
90
+ ```
91
+ 覆盖说明:
92
+ ✅ 已覆盖:[已输出的章节列表]
93
+ ⚠️ 未覆盖:[章节名] — 原因:[缺接口/缺参数/缺权限/用户未提供账户ID]
94
+ ```
95
+
96
+ ### 快速路径(数据已齐全时)
97
+
98
+ 满足以下全部条件时,可跳过门禁一声明,直接生成:
99
+ 1. 报告类型已明确(有对应 `.md`)
100
+ 2. 账户 ID 已确认(来自 `list-accounts` 或用户明确提供)
101
+ 3. 数据已拉取(命令已执行,结果在上下文中)
102
+
103
+ ### HTML 报告默认样式选择
104
+
105
+ | 场景 | 默认模板 |
106
+ |------|---------|
107
+ | 对内分析 / 运营周报 | `report-template.html` |
108
+ | 正式汇报 / 对外文档 | `report-template-formal.html` |
109
+ | 投屏演示 | `report-template-dark.html` |
110
+ | 单页摘要 | `report-template-onepager.html` |
111
+ | 手机查看 | `report-template-mobile.html` |
112
+ | 打印归档 | `report-template-print.html` |
113
+
114
+ "带图表"= 在满足 `.md` 内容要求的前提下,把趋势/结构/对比优先用图表展示,**不是**只给图表不写结论。
115
+
116
+ ### 少样本决策示例(3 个必读模式)
117
+
118
+ **示例 A — 标准报告生成**
119
+
120
+ 用户说:"帮我做个 Google 月度投放总结"
121
+ → 意图:周期分析 → 媒体:Google
122
+ → 内容合同:读 `google-period-report.md`
123
+ → 执行门禁一声明:输出类型判断 + 覆盖预判
124
+ → 执行数据拉取:`google-analysis overview / campaigns / devices / keywords`
125
+ → 填充章节,末尾附覆盖声明
126
+ → 样式默认:`report-template.html`(未指定时)
127
+
128
+ **示例 B — 未知报告名映射**
129
+
130
+ 用户说:"给客户一份 Meta 广告账户健康检查"
131
+ → "健康检查"= 诊断型 → 媒体:Meta
132
+ → 当前 Meta 无诊断专用 `.md`
133
+ → 映射:以 `google-account-diagnosis-report.md` 为骨架,查 `references/reporting.md` 校验 Meta 能力
134
+ → 发现 Meta 当前只有 overview 接口(无系列/受众/搜索词细分)
135
+ → 声明:降级生成,注明未覆盖章节与原因
136
+ → 执行:`report meta-overview -a <id> --json`,仅填能覆盖的节
137
+ → 末尾覆盖声明:列出未覆盖原因
138
+
139
+ **示例 C — 账户操作 ID 门禁**
140
+
141
+ 用户说:"查一下 Google 账户 123456 的余额"
142
+ → 禁止直接用"123456"执行命令
143
+ → 先执行 `siluzan-tso list-accounts -m Google` 确认该 ID 在列表中
144
+ → 确认后取 `mediaCustomerId`,再执行 `siluzan-tso balance -m Google -a <mediaCustomerId>`
145
+ → 不在列表中:告知用户该 ID 未找到,请确认账户归属
@@ -0,0 +1,165 @@
1
+ # 报告决策示例(少样本参考)
2
+
3
+ > **重要**:这些示例记录的是**决策过程**,不是成品文本的模板。
4
+ > 目的:帮助 AI 理解如何从用户意图映射到正确的工具调用链,而不是照抄任何一份完整输出。
5
+
6
+ ---
7
+
8
+ ## 示例 1:标准 Google 周期报告
9
+
10
+ **用户输入**:帮我做个 Google 上个月的投放总结,账户 4545500137
11
+
12
+ **决策过程**:
13
+ ```
14
+ 意图识别:周期型 + Google
15
+ 内容合同:google-period-report.md
16
+ 账户确认:mediaCustomerId=4545500137(用户明确提供,仍需在 list-accounts 中验证)
17
+ 覆盖预判:能覆盖 总览/系列/设备/关键词 → 地域数据视接口状态
18
+ ```
19
+
20
+ **执行命令链**:
21
+ ```bash
22
+ siluzan-tso google-analysis overview -a 4545500137 --start 2025-03-01 --end 2025-03-31 --json
23
+ siluzan-tso google-analysis campaigns -a 4545500137 --start 2025-03-01 --end 2025-03-31 --json
24
+ siluzan-tso google-analysis devices -a 4545500137 --start 2025-03-01 --end 2025-03-31 --json
25
+ siluzan-tso google-analysis keywords -a 4545500137 --start 2025-03-01 --end 2025-03-31 --json
26
+ ```
27
+
28
+ **关键字段提取**(以 overview 为例):
29
+ ```
30
+ cost → 总消耗
31
+ clicks / impressions / ctr / conversions / cpc → 核心 KPI
32
+ costMicros 转换:除以 1,000,000 得到货币值
33
+ ```
34
+
35
+ **章节填充规则**:
36
+ - 有数据的章节:用真实数字,注明环比(需拉上期区间对比)
37
+ - 数据为 0 的章节:如实写 0,说明可能原因(无投放/停投/权限)
38
+ - 无接口的章节:占位标注,不推测
39
+
40
+ **末尾覆盖声明模板**:
41
+ ```
42
+ ✅ 已覆盖:总览 / 广告系列表现 / 设备分布 / 关键词消耗
43
+ ⚠️ 未覆盖:地域分布 — 原因:overview 接口未返回地域细分,需单独地域命令
44
+ 📌 数据来源:google-analysis overview / campaigns / devices / keywords(上述命令已执行)
45
+ ```
46
+
47
+ ---
48
+
49
+ ## 示例 2:未知报告名 → 降级处理
50
+
51
+ **用户输入**:给我出一份 Meta 广告账户健康体检报告,账户 ID 112233445
52
+
53
+ **决策过程**:
54
+ ```
55
+ 意图识别:"健康体检"= 诊断型 + Meta
56
+ 内容合同查找:meta 无专用诊断 .md
57
+ 映射降级:以 google-account-diagnosis-report.md 为骨架
58
+ 能力校验(查 references/reporting.md):
59
+ - Meta 当前支持:OverviewSectionData(总览)
60
+ - Meta 当前不支持:系列细分、受众细分、搜索词、创意诊断
61
+ 降级决策:仅填总览章节,其余标注"接口待开放"
62
+ ```
63
+
64
+ **执行命令链**:
65
+ ```bash
66
+ siluzan-tso report meta-overview -a 112233445 --json
67
+ ```
68
+
69
+ **声明输出(在开始前向用户说明)**:
70
+ ```
71
+ 报告类型判断:
72
+ - 用户意图:诊断型
73
+ - 媒体:MetaAd
74
+ - 内容合同:google-account-diagnosis-report.md(降级适配,Meta 暂无专用诊断模板)
75
+ - 能覆盖:账户总览(消耗/点击/展示/转化)
76
+ - 当前无法覆盖:广告系列诊断、受众诊断、创意健康度 — 原因:Meta TSO 当前仅开放 overview 接口
77
+ ```
78
+
79
+ **执行结果中的注意点**:
80
+ - `OverviewSectionData` 返回的是时间段汇总数据,不含历史趋势序列
81
+ - 无法给出"建议优化哪条广告系列"的结论(无系列级数据)
82
+ - 可以给出的结论:CTR/CPC 整体水平判断、消耗趋势方向(若有环比)
83
+
84
+ ---
85
+
86
+ ## 示例 3:跨媒体汇总请求
87
+
88
+ **用户输入**:给我汇总一下上周 Google 和 TikTok 的投放数据
89
+
90
+ **决策过程**:
91
+ ```
92
+ 意图识别:周期型 + 多媒体(Google + TikTok)
93
+ 内容合同:google-period-report.md + tiktok-period-report.md(分别读)
94
+ 执行顺序:先 Google,再 TikTok,最后汇总章节
95
+ ```
96
+
97
+ **执行命令链(分两段)**:
98
+ ```bash
99
+ # Google 段
100
+ siluzan-tso google-analysis overview -a <google_id> --start 2025-04-14 --end 2025-04-20 --json
101
+
102
+ # TikTok 段
103
+ siluzan-tso report tiktok-overview -a <tiktok_id> --start 2025-04-14 --end 2025-04-20 --json
104
+ siluzan-tso report tiktok-campaigns -a <tiktok_id> --start 2025-04-14 --end 2025-04-20 --json
105
+ ```
106
+
107
+ **账户 ID 确认(必须先执行)**:
108
+ ```bash
109
+ siluzan-tso list-accounts -m Google
110
+ siluzan-tso list-accounts -m TikTok
111
+ ```
112
+
113
+ **汇总时的报告结构**:
114
+ - 第一节:跨媒体总消耗对比(表格形式,一行一媒体)
115
+ - 第二节:各媒体独立章节(每个媒体按自身 `.md` 要求)
116
+ - 不要把两个媒体的数据混在同一个章节里,口径不同
117
+
118
+ ---
119
+
120
+ ## 示例 4:用户提供不存在的报告类型
121
+
122
+ **用户输入**:给我做一份 Yandex 关键词效果分析报告
123
+
124
+ **决策过程**:
125
+ ```
126
+ 意图识别:周期型(关键词聚焦)+ Yandex
127
+ 内容合同查找:Yandex 无专用 .md,list-accounts 支持 Yandex,但 reporting.md 无关键词接口
128
+ 能力校验:Yandex 当前仅有账户基础数据(stats)
129
+ 降级:用 google-period-report.md 骨架中的"关键词分析"章节,但说明数据来源受限
130
+ ```
131
+
132
+ **声明(必须先告知用户)**:
133
+ ```
134
+ 报告类型判断:
135
+ - 用户意图:关键词效果分析(归属周期型)
136
+ - 媒体:Yandex
137
+ - 内容合同:google-period-report.md 中关键词章节(降级适配)
138
+ - 能覆盖:账户整体消耗、余额(via stats / balance)
139
+ - 当前无法覆盖:Yandex 关键词级数据 — 原因:TSO reporting API 当前未开放 Yandex 关键词细分
140
+ - 建议:若需关键词数据,请直接登录 Yandex Ads 后台查看
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 示例 5:快速路径(数据已就绪)
146
+
147
+ **场景**:用户在同一对话中已经执行过 `google-analysis overview --json`,现在说"帮我整理成报告"
148
+
149
+ **决策过程**:
150
+ ```
151
+ 快速路径条件检查:
152
+ ✅ 报告类型明确(周期型 Google)
153
+ ✅ 账户 ID 已确认(上下文中有)
154
+ ✅ 数据已拉取(overview JSON 在上下文中)
155
+ → 跳过门禁一声明,直接填充章节
156
+ → 仍需输出门禁三覆盖声明(数据仅含 overview,其他章节标注待补充)
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 这些示例不告诉你的事
162
+
163
+ - 不要照抄示例中的数字(那些是说明格式用的占位符)
164
+ - 不要把"示例 B 的覆盖声明格式"原文粘贴到真实报告里
165
+ - 示例展示的是**推理路径**,真实报告中的结论必须来自实际命令输出
@@ -0,0 +1,144 @@
1
+ # 报告生成工作流(未知报告名处理流程)
2
+
3
+ 本文档定义当用户请求的报告名不在已知模板列表中时的**标准处理流程**,以及每种情况下的降级规则与覆盖声明格式。
4
+
5
+ ---
6
+
7
+ ## 六步处理流程
8
+
9
+ ```
10
+ 步骤 1 识别意图
11
+
12
+ 步骤 2 原型映射
13
+
14
+ 步骤 3 数据能力校验
15
+
16
+ 步骤 4 声明(门禁一)
17
+
18
+ 步骤 5 执行 + 降级生成
19
+
20
+ 步骤 6 输出 + 覆盖声明(门禁三)
21
+ ```
22
+
23
+ ---
24
+
25
+ ### 步骤 1:识别意图
26
+
27
+ 从用户措辞中提取两个维度:
28
+
29
+ | 维度 | 识别方法 | 示例 |
30
+ |------|---------|------|
31
+ | **媒体** | 关键词:Google / Meta / TikTok / Bing / Kwai | "Meta 广告分析" → MetaAd |
32
+ | **报告类型** | 关键词映射(见下表) | "健康检查" → 诊断型 |
33
+
34
+ **类型关键词映射表**
35
+
36
+ | 用户说的词 | 映射到的原型 |
37
+ |-----------|------------|
38
+ | 月报、周报、日报、阶段分析、投放总结、效果回顾 | **周期型** |
39
+ | 健康检查、诊断、体检、问题排查、账户分析 | **诊断型** |
40
+ | 对比、AB测试、竞品、同期比较 | **对比型** |
41
+ | 汇报、PPT、给客户、精华版、摘要 | **汇报摘要型** |
42
+
43
+ > 若仍无法确定,默认为**周期型**,并在声明中注明推断依据。
44
+
45
+ ---
46
+
47
+ ### 步骤 2:原型映射
48
+
49
+ 根据「媒体 + 类型」找到最近似的内容合同(`.md`):
50
+
51
+ | 媒体 | 周期型 | 诊断型 | 对比型 | 汇报摘要型 |
52
+ |------|--------|--------|--------|-----------|
53
+ | Google | `google-period-report.md` | `google-account-diagnosis-report.md` | 无专用,以周期型为骨架 | 以诊断型精简 |
54
+ | Meta | `meta-period-report.md` | 无专用 → 以诊断纲要降级 | 无专用 | 以周期型精简 |
55
+ | TikTok | `tiktok-period-report.md` | 无专用 → 以诊断纲要降级 | 无专用 | 以周期型精简 |
56
+ | Bing | `bing-period-report.md` | 无专用 → 以诊断纲要降级 | 无专用 | 以周期型精简 |
57
+ | 未知媒体 | 以 Google 对应原型为骨架 | → | → | → |
58
+
59
+ **降级规则(从高到低)**:
60
+ 1. 有精确对应 `.md` → 直接使用
61
+ 2. 有同媒体其他类型 `.md` → 用最接近的,声明已适配
62
+ 3. 有同类型其他媒体 `.md` → 用 Google 对应型,声明能力受限
63
+ 4. 完全无对应 → 以 `google-period-report.md` 为最小骨架,声明原因
64
+
65
+ ---
66
+
67
+ ### 步骤 3:数据能力校验
68
+
69
+ 打开 `references/reporting.md`,查询该媒体**当前支持的 CLI 命令**。
70
+
71
+ 按章节检查哪些能覆盖,哪些无法覆盖:
72
+
73
+ ```
74
+ [能覆盖] = 有对应 CLI 命令且参数已确认
75
+ [受限] = 有命令但需额外参数(用户未提供 accountId 等)
76
+ [无法覆盖] = 当前无对应接口 / 命令不存在
77
+ ```
78
+
79
+ ---
80
+
81
+ ### 步骤 4:声明(门禁一,不可跳过)
82
+
83
+ 在开始拉数据前,向用户输出以下声明:
84
+
85
+ ```
86
+ 报告类型判断:
87
+ - 用户意图:[从步骤 1 识别出的类型]
88
+ - 媒体:[媒体名]
89
+ - 内容合同:[选用的 .md 文件名](若为降级,注明"适配自 xxx"])
90
+ - 样式模板:[.html 文件名,若未指定写"默认"]
91
+ - 能覆盖的章节:[列表]
92
+ - 当前无法覆盖:[章节名 + 原因](若全覆盖写"全部覆盖")
93
+ ```
94
+
95
+ ---
96
+
97
+ ### 步骤 5:执行 + 降级生成
98
+
99
+ **正常执行路径**:
100
+ 1. 按 `.md` 中列出的 CLI 命令依次执行
101
+ 2. 每个命令加 `--json` 获取结构化数据
102
+ 3. 用实际数据填充对应章节
103
+
104
+ **降级生成规则**:
105
+ - 章节数据缺失 → 用 `[ 数据不可用:{原因} ]` 占位,不写任何推测性内容
106
+ - 命令执行失败 → 记录错误信息,注明此章节基于上次已有数据或跳过
107
+ - 账户 ID 未确认 → 暂停,提示用户先执行 `list-accounts` 确认
108
+
109
+ **禁止**:
110
+ - 使用 HTML 模板中的假数字填充真实报告
111
+ - 在没有执行命令的情况下凭印象写出具体数字
112
+ - 把无数据章节写成"暂无异常"(这是误导性结论,需说明原因)
113
+
114
+ ---
115
+
116
+ ### 步骤 6:输出 + 覆盖声明(门禁三,每份报告必须附)
117
+
118
+ 报告正文末尾追加:
119
+
120
+ ```
121
+ ---
122
+ ## 覆盖说明
123
+
124
+ ✅ 已覆盖:
125
+ - [章节一]
126
+ - [章节二]
127
+ - ...
128
+
129
+ ⚠️ 未覆盖:
130
+ - [章节名] — 原因:[缺接口 / 缺参数 / 账户未授权 / 用户未提供 ID]
131
+
132
+ 📌 本报告数据来源:[实际执行的命令列表]
133
+ ```
134
+
135
+ ---
136
+
137
+ ## 常见场景速查
138
+
139
+ | 场景 | 处理方式 |
140
+ |------|---------|
141
+ | 用户说"帮我看下 Kwai 账户" | 无 Kwai 专用模板 → 用 `google-period-report.md` 骨架 → 查 `reporting.md` 确认 Kwai 支持哪些命令 → 声明降级 |
142
+ | 用户说"生成一份竞品对比报告" | 对比型 → 当前无专用模板 → 降级为周期型 → 声明:当前无竞品数据接口,仅输出本账户数据 |
143
+ | 用户未提供账户 ID | 步骤 5 暂停 → 提示:请先运行 `siluzan-tso list-accounts -m <媒体>` 获取 mediaCustomerId |
144
+ | 用户要求"尽量完整"但接口有限 | 声明能力边界,在能覆盖的章节写实,在无数据章节用占位标注 — 不要为了"看起来完整"编造数据 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.0.0-beta.29",
3
+ "version": "1.0.0-beta.30",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "type": "module",
6
6
  "bin": {