siluzan-tso-cli 1.1.35-beta.3 → 1.1.35-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +149 -63
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/report-templates/google-ads-diagnosis.md +12 -3
- package/dist/skill/report-templates/google-ads-diagnosis.md +12 -3
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
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.35-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.35-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -105346,12 +105346,12 @@ init_cli_json_snapshot();
|
|
|
105346
105346
|
|
|
105347
105347
|
// src/utils/list-json-envelope.ts
|
|
105348
105348
|
function wrapListJson(params) {
|
|
105349
|
-
const
|
|
105349
|
+
const itemCount2 = Array.isArray(params.items) ? params.items.length : 0;
|
|
105350
105350
|
return {
|
|
105351
105351
|
page: params.page,
|
|
105352
105352
|
pageSize: params.pageSize,
|
|
105353
105353
|
total: params.total === void 0 || params.total === null ? null : params.total,
|
|
105354
|
-
itemCount,
|
|
105354
|
+
itemCount: itemCount2,
|
|
105355
105355
|
items: params.items
|
|
105356
105356
|
};
|
|
105357
105357
|
}
|
|
@@ -124534,7 +124534,7 @@ function buildGoogleAdsDiagnosisCollectAgentHint(snapDir) {
|
|
|
124534
124534
|
"Google Ads \u8BCA\u65AD collect \u5DF2\u5B8C\u6210\uFF08\u4EC5\u4E8B\u5B9E\u6570\u636E\uFF0C\u4E0D\u542B\u5EFA\u8BAE\u6027\u6587\u6848\uFF09\u3002",
|
|
124535
124535
|
`1) Read ${dir}/${GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME}\uFF08reportData + agentBrief\uFF09\uFF1B`,
|
|
124536
124536
|
"2) Read references/report-templates/google-ads-diagnosis.md\uFF0C\u7531 Agent \u64B0\u5199\u5168\u90E8 analysis/suggestions\u3001diagnosisOverview\u3001summary\u3001accountInfo.businessModel/industry\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation\u3001adCreativeOptimization.items[].suggestion \u7B49\uFF1B",
|
|
124537
|
-
`3) \u5C06 reportData \u4E0E\u53D9\u4E8B\u5B57\u6BB5\u5408\u5E76\u5199\u5165 ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME}\uFF08**\u7981\u6B62\
|
|
124537
|
+
`3) \u5C06 reportData \u4E0E\u53D9\u4E8B\u5B57\u6BB5\u5408\u5E76\u5199\u5165 ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME}\uFF08**\u7981\u6B62\u5199\u5165/\u8986\u76D6** campaigns/geographic/keywords \u7684 items \u884C\uFF0C\u53EA\u586B analysis/suggestions\uFF1Bitems \u4EE5 collect \u4E3A\u51C6\uFF0Crender \u4F1A\u81EA\u52A8\u8986\u76D6 Agent \u6539\u52A8\u7684\u5BF9\u6BD4\u8868\uFF09\uFF1B`,
|
|
124538
124538
|
`4) siluzan-tso google-ads-diagnosis render --data ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME} --out ${dir}/google-ads-diagnosis-report.html\uFF08\u9ED8\u8BA4\u540C\u76EE\u5F55 collect \u81EA\u52A8\u6062\u590D\u88AB\u6539\u574F\u7684 items\uFF09\u3002`,
|
|
124539
124539
|
"\u7981\u6B62\u5728\u672A\u7ECF\u8FC7 Agent \u64B0\u5199\u7684\u60C5\u51B5\u4E0B\u76F4\u63A5 render collect \u4EA7\u7269\uFF1B\u7F3A\u4EFB\u4E00\u5EFA\u8BAE\u5B57\u6BB5 render \u4F1A\u5931\u8D25\u3002"
|
|
124540
124540
|
].join("\n");
|
|
@@ -124587,6 +124587,20 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
|
|
|
124587
124587
|
campaigns: input.campaignHints,
|
|
124588
124588
|
keywords: input.keywordHints
|
|
124589
124589
|
},
|
|
124590
|
+
comparisonTableItemCounts: input.comparisonTableItemCounts,
|
|
124591
|
+
agentRules: {
|
|
124592
|
+
doNotModifyItemRows: [
|
|
124593
|
+
"campaigns.items",
|
|
124594
|
+
"geographic.items",
|
|
124595
|
+
"keywords.items"
|
|
124596
|
+
],
|
|
124597
|
+
renderRestoresComparisonItemsFromCollect: "render \u9ED8\u8BA4\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u4F1A\u4EE5 collect \u8986\u76D6\u4E0A\u8FF0 items\uFF08Agent \u53EA\u586B analysis/suggestions\uFF09",
|
|
124598
|
+
mayEditItemFields: [
|
|
124599
|
+
"biddingStrategy.items[].recommendationReason",
|
|
124600
|
+
"newFeatures.items[].optimizerRecommendation",
|
|
124601
|
+
"adCreativeOptimization.items[].suggestion"
|
|
124602
|
+
]
|
|
124603
|
+
},
|
|
124590
124604
|
agentDeliverable: {
|
|
124591
124605
|
outputFile: "google-ads-diagnosis.json",
|
|
124592
124606
|
structure: "\u4E0E reportData \u540C\u7ED3\u6784\u7684 sectionData\uFF1B\u987B\u586B diagnosisOverview\u3001metrics.analysis/suggestions\u3001\u5404\u6A21\u5757 analysis/suggestions\u3001summary.keyIssues\u3001summary.optimizationRoadmap[]\uFF08priority/focusArea/actionItems/expectedOutcome\uFF09\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation \u7B49",
|
|
@@ -125380,11 +125394,58 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125380
125394
|
profile.industry,
|
|
125381
125395
|
...campaignCurrent.map((r) => String(r.campaignName ?? ""))
|
|
125382
125396
|
].filter(Boolean),
|
|
125383
|
-
keywordHints: zeroConvKw.slice(0, 5).map((r) => String(r.keyword ?? ""))
|
|
125397
|
+
keywordHints: zeroConvKw.slice(0, 5).map((r) => String(r.keyword ?? "")),
|
|
125398
|
+
comparisonTableItemCounts: {
|
|
125399
|
+
campaigns: campaignItems.length,
|
|
125400
|
+
geographic: geoItems.length,
|
|
125401
|
+
keywords: kwItems.length
|
|
125402
|
+
}
|
|
125384
125403
|
});
|
|
125385
125404
|
return { reportData: data, agentBrief };
|
|
125386
125405
|
}
|
|
125387
125406
|
|
|
125407
|
+
// src/commands/google-ads-diagnosis/collect-integrity.ts
|
|
125408
|
+
function asRecord5(value) {
|
|
125409
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125410
|
+
return value;
|
|
125411
|
+
}
|
|
125412
|
+
return null;
|
|
125413
|
+
}
|
|
125414
|
+
function itemCount(block) {
|
|
125415
|
+
const items = asRecord5(block)?.items;
|
|
125416
|
+
return Array.isArray(items) ? items.length : 0;
|
|
125417
|
+
}
|
|
125418
|
+
function getCollectIntegrityWarnings(reportData) {
|
|
125419
|
+
const warnings = [];
|
|
125420
|
+
const kwItems = itemCount(reportData.keywords);
|
|
125421
|
+
const fullKwItems = itemCount(reportData.fullKeywords);
|
|
125422
|
+
const keywordCount = Number(asRecord5(reportData.structure)?.keywordCount ?? 0);
|
|
125423
|
+
if (kwItems === 0 && fullKwItems > 0) {
|
|
125424
|
+
warnings.push(
|
|
125425
|
+
`keywords.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 fullKeywords.items \u6709 ${fullKwItems} \u884C\uFF1B\u8BF7\u786E\u8BA4 collect \u662F\u5426\u6210\u529F\u62C9\u53D6\u4E0A\u4E00\u5468\u671F keywords \u5FEB\u7167\uFF08\u73AF\u6BD4\u5217\u4F9D\u8D56 prev \u5468\u671F JSON\uFF09\u3002`
|
|
125426
|
+
);
|
|
125427
|
+
} else if (kwItems === 0 && keywordCount > 0) {
|
|
125428
|
+
warnings.push(
|
|
125429
|
+
`keywords.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 structure.keywordCount=${keywordCount}\uFF1B\u8BF7\u786E\u8BA4\u4E0A\u4E00\u5468\u671F keywords \u5FEB\u7167\u662F\u5426\u62C9\u53D6\u6210\u529F\u3002`
|
|
125430
|
+
);
|
|
125431
|
+
}
|
|
125432
|
+
const campaignItems = itemCount(reportData.campaigns);
|
|
125433
|
+
const campaignCount = Number(asRecord5(reportData.structure)?.campaignCount ?? 0);
|
|
125434
|
+
if (campaignItems === 0 && campaignCount > 0) {
|
|
125435
|
+
warnings.push(
|
|
125436
|
+
`campaigns.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 structure.campaignCount=${campaignCount}\uFF1B\u8BF7\u786E\u8BA4\u4E0A\u4E00\u5468\u671F campaigns \u5FEB\u7167\u662F\u5426\u62C9\u53D6\u6210\u529F\u3002`
|
|
125437
|
+
);
|
|
125438
|
+
}
|
|
125439
|
+
const geoItems = itemCount(reportData.geographic);
|
|
125440
|
+
const fullGeoItems = itemCount(reportData.fullGeographic);
|
|
125441
|
+
if (geoItems === 0 && fullGeoItems > 0) {
|
|
125442
|
+
warnings.push(
|
|
125443
|
+
`geographic.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 fullGeographic.items \u6709 ${fullGeoItems} \u884C\uFF1B\u8BF7\u786E\u8BA4\u4E0A\u4E00\u5468\u671F geographic \u5FEB\u7167\u662F\u5426\u62C9\u53D6\u6210\u529F\u3002`
|
|
125444
|
+
);
|
|
125445
|
+
}
|
|
125446
|
+
return warnings;
|
|
125447
|
+
}
|
|
125448
|
+
|
|
125388
125449
|
// src/commands/google-ads-diagnosis/invoke-google-analysis.ts
|
|
125389
125450
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
125390
125451
|
function invokeGoogleAnalysisCli(opts) {
|
|
@@ -125527,6 +125588,12 @@ async function runGoogleAdsDiagnosisCollect(opts) {
|
|
|
125527
125588
|
website: websiteUrl || opts.website,
|
|
125528
125589
|
landingPage
|
|
125529
125590
|
});
|
|
125591
|
+
for (const warning of getCollectIntegrityWarnings(reportData)) {
|
|
125592
|
+
console.error(`
|
|
125593
|
+
\u26A0\uFE0F collect \u5B8C\u6574\u6027\uFF1A${warning}
|
|
125594
|
+
`);
|
|
125595
|
+
degraded = true;
|
|
125596
|
+
}
|
|
125530
125597
|
const collectPayload = {
|
|
125531
125598
|
kind: "siluzan-tso-google-ads-diagnosis-collect",
|
|
125532
125599
|
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -125567,7 +125634,7 @@ import fs18 from "fs/promises";
|
|
|
125567
125634
|
import path23 from "path";
|
|
125568
125635
|
|
|
125569
125636
|
// src/commands/google-ads-diagnosis/report-content.ts
|
|
125570
|
-
function
|
|
125637
|
+
function asRecord6(value) {
|
|
125571
125638
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125572
125639
|
return value;
|
|
125573
125640
|
}
|
|
@@ -125576,7 +125643,7 @@ function asRecord5(value) {
|
|
|
125576
125643
|
function getNestedValue(root, path34) {
|
|
125577
125644
|
let cur = root;
|
|
125578
125645
|
for (const key of path34) {
|
|
125579
|
-
const rec =
|
|
125646
|
+
const rec = asRecord6(cur);
|
|
125580
125647
|
if (!rec) return void 0;
|
|
125581
125648
|
cur = rec[key];
|
|
125582
125649
|
}
|
|
@@ -125593,7 +125660,7 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
125593
125660
|
}
|
|
125594
125661
|
const warnings = [];
|
|
125595
125662
|
for (let i = 0; i < items.length; i++) {
|
|
125596
|
-
const row =
|
|
125663
|
+
const row = asRecord6(items[i]);
|
|
125597
125664
|
const name2 = String(row?.name ?? row?.metric ?? i);
|
|
125598
125665
|
if (!hasNarrativeContent(row?.strategy)) {
|
|
125599
125666
|
warnings.push(`budgetCompetitiveness[${name2}].strategy \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -125602,13 +125669,13 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
125602
125669
|
return warnings;
|
|
125603
125670
|
}
|
|
125604
125671
|
function validateNewFeatureRecommendations(data) {
|
|
125605
|
-
const nf =
|
|
125672
|
+
const nf = asRecord6(data.newFeatures);
|
|
125606
125673
|
if (!nf) return [];
|
|
125607
125674
|
const items = nf.items;
|
|
125608
125675
|
if (!Array.isArray(items)) return [];
|
|
125609
125676
|
const warnings = [];
|
|
125610
125677
|
for (let i = 0; i < items.length; i++) {
|
|
125611
|
-
const row =
|
|
125678
|
+
const row = asRecord6(items[i]);
|
|
125612
125679
|
const name2 = String(row?.strategyName ?? row?.feature ?? row?.strategy ?? i);
|
|
125613
125680
|
if (!hasNarrativeContent(row?.optimizerRecommendation)) {
|
|
125614
125681
|
warnings.push(`newFeatures.items[${name2}].optimizerRecommendation \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -125617,13 +125684,13 @@ function validateNewFeatureRecommendations(data) {
|
|
|
125617
125684
|
return warnings;
|
|
125618
125685
|
}
|
|
125619
125686
|
function validateAdCreativeItemSuggestions(data) {
|
|
125620
|
-
const creative =
|
|
125687
|
+
const creative = asRecord6(data.adCreativeOptimization);
|
|
125621
125688
|
if (!creative) return [];
|
|
125622
125689
|
const items = creative.items;
|
|
125623
125690
|
if (!Array.isArray(items) || items.length === 0) return [];
|
|
125624
125691
|
const warnings = [];
|
|
125625
125692
|
for (let i = 0; i < items.length; i++) {
|
|
125626
|
-
const row =
|
|
125693
|
+
const row = asRecord6(items[i]);
|
|
125627
125694
|
const title = String(row?.adTitle ?? i);
|
|
125628
125695
|
if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
|
|
125629
125696
|
warnings.push(
|
|
@@ -125638,7 +125705,7 @@ function validateGoogleAdsDiagnosisContent(data) {
|
|
|
125638
125705
|
(key) => data[key] === void 0
|
|
125639
125706
|
);
|
|
125640
125707
|
const warnings = [];
|
|
125641
|
-
const accountInfo =
|
|
125708
|
+
const accountInfo = asRecord6(data.accountInfo);
|
|
125642
125709
|
if (accountInfo && !accountInfo.companyName && !accountInfo.period) {
|
|
125643
125710
|
warnings.push("accountInfo \u7F3A\u5C11 companyName \u4E0E period\uFF0C\u9875\u7709\u53EF\u80FD\u4E3A\u7A7A");
|
|
125644
125711
|
}
|
|
@@ -125686,7 +125753,7 @@ var GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES = [
|
|
|
125686
125753
|
GOOGLE_ADS_DIAGNOSIS_BIDDING_STRATEGY_MODULE,
|
|
125687
125754
|
GOOGLE_ADS_DIAGNOSIS_NEW_FEATURES_MODULE
|
|
125688
125755
|
];
|
|
125689
|
-
function
|
|
125756
|
+
function asRecord7(value) {
|
|
125690
125757
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125691
125758
|
return value;
|
|
125692
125759
|
}
|
|
@@ -125697,7 +125764,7 @@ function isValidRateChange(value) {
|
|
|
125697
125764
|
return typeof value === "number" && Number.isFinite(value);
|
|
125698
125765
|
}
|
|
125699
125766
|
function isComparisonTableRowValid(row) {
|
|
125700
|
-
const rec =
|
|
125767
|
+
const rec = asRecord7(row);
|
|
125701
125768
|
if (!rec) return false;
|
|
125702
125769
|
if (!String(rec.title ?? "").trim()) return false;
|
|
125703
125770
|
if (!Number.isFinite(Number(rec.currentCost))) return false;
|
|
@@ -125709,12 +125776,12 @@ function isComparisonTableRowValid(row) {
|
|
|
125709
125776
|
return true;
|
|
125710
125777
|
}
|
|
125711
125778
|
function isNewFeatureRowValid(row) {
|
|
125712
|
-
const rec =
|
|
125779
|
+
const rec = asRecord7(row);
|
|
125713
125780
|
if (!rec) return false;
|
|
125714
125781
|
return Boolean(String(rec.strategyName ?? rec.feature ?? rec.strategy ?? "").trim());
|
|
125715
125782
|
}
|
|
125716
125783
|
function isBiddingStrategyRowValid(row) {
|
|
125717
|
-
const rec =
|
|
125784
|
+
const rec = asRecord7(row);
|
|
125718
125785
|
if (!rec) return false;
|
|
125719
125786
|
if (!String(rec.campaignName ?? "").trim()) return false;
|
|
125720
125787
|
if (!Number.isFinite(Number(rec.duration)) || Number(rec.duration) <= 0) return false;
|
|
@@ -125739,14 +125806,32 @@ function moduleItemsNeedRestore(items, validator) {
|
|
|
125739
125806
|
if (!Array.isArray(items) || items.length === 0) return false;
|
|
125740
125807
|
return items.some((row) => !validator(row));
|
|
125741
125808
|
}
|
|
125809
|
+
function isComparisonTableModule(mod) {
|
|
125810
|
+
return GOOGLE_ADS_DIAGNOSIS_COMPARISON_MODULES.includes(mod);
|
|
125811
|
+
}
|
|
125812
|
+
function comparisonItemsDifferFromCollect(finalItems, collectItems) {
|
|
125813
|
+
if (!Array.isArray(finalItems)) return true;
|
|
125814
|
+
if (finalItems.length !== collectItems.length) return true;
|
|
125815
|
+
return JSON.stringify(finalItems) !== JSON.stringify(collectItems);
|
|
125816
|
+
}
|
|
125742
125817
|
function getFactItemsErrors(data) {
|
|
125743
125818
|
const errors = [];
|
|
125744
125819
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
125745
125820
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
125746
125821
|
const hint = MODULE_ITEM_ERROR_HINT[mod];
|
|
125747
|
-
const block =
|
|
125822
|
+
const block = asRecord7(data[mod]);
|
|
125748
125823
|
const items = block?.items;
|
|
125749
|
-
if (!Array.isArray(items) || items.length === 0)
|
|
125824
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
125825
|
+
if (mod === "keywords") {
|
|
125826
|
+
const fullKw = asRecord7(data.fullKeywords)?.items;
|
|
125827
|
+
if (Array.isArray(fullKw) && fullKw.length > 0) {
|
|
125828
|
+
errors.push(
|
|
125829
|
+
"keywords.items \u4E3A\u7A7A\u4F46 fullKeywords.items \u6709\u6570\u636E\uFF1B\u8BF7\u4FDD\u7559\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u540E\u91CD\u65B0 render\uFF08\u4F1A\u81EA\u52A8\u6062\u590D\u5BF9\u6BD4\u8868\uFF09"
|
|
125830
|
+
);
|
|
125831
|
+
}
|
|
125832
|
+
}
|
|
125833
|
+
continue;
|
|
125834
|
+
}
|
|
125750
125835
|
for (let i = 0; i < items.length; i++) {
|
|
125751
125836
|
if (!validator(items[i])) {
|
|
125752
125837
|
errors.push(`${mod}.items[${i}] ${hint}`);
|
|
@@ -125763,15 +125848,15 @@ var LANDING_PAGE_SPEED_KEYS = [
|
|
|
125763
125848
|
"speedIndex"
|
|
125764
125849
|
];
|
|
125765
125850
|
function isLandingPageSpeedValid(value) {
|
|
125766
|
-
const rec =
|
|
125851
|
+
const rec = asRecord7(value);
|
|
125767
125852
|
if (!rec) return false;
|
|
125768
125853
|
return LANDING_PAGE_SPEED_KEYS.every(
|
|
125769
125854
|
(key) => typeof rec[key] === "number" && Number.isFinite(rec[key])
|
|
125770
125855
|
);
|
|
125771
125856
|
}
|
|
125772
125857
|
function restoreLandingPageSpeedFromCollect(finalData, collectReportData) {
|
|
125773
|
-
const finalBlock =
|
|
125774
|
-
const collectBlock =
|
|
125858
|
+
const finalBlock = asRecord7(finalData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
125859
|
+
const collectBlock = asRecord7(collectReportData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
125775
125860
|
if (!collectBlock) return { data: finalData, restored: false };
|
|
125776
125861
|
const needsDesktop = !isLandingPageSpeedValid(finalBlock?.desktop) && isLandingPageSpeedValid(collectBlock.desktop);
|
|
125777
125862
|
const needsMobile = !isLandingPageSpeedValid(finalBlock?.mobile) && isLandingPageSpeedValid(collectBlock.mobile);
|
|
@@ -125789,12 +125874,13 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
125789
125874
|
const restoredModules = [];
|
|
125790
125875
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
125791
125876
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
125792
|
-
const finalBlock =
|
|
125793
|
-
const collectBlock =
|
|
125877
|
+
const finalBlock = asRecord7(data[mod]);
|
|
125878
|
+
const collectBlock = asRecord7(collectReportData[mod]);
|
|
125794
125879
|
const finalItems = finalBlock?.items;
|
|
125795
125880
|
const collectItems = collectBlock?.items;
|
|
125796
|
-
if (!moduleItemsNeedRestore(finalItems, validator)) continue;
|
|
125797
125881
|
if (!Array.isArray(collectItems) || collectItems.length === 0) continue;
|
|
125882
|
+
const needsRestore = isComparisonTableModule(mod) ? comparisonItemsDifferFromCollect(finalItems, collectItems) : moduleItemsNeedRestore(finalItems, validator);
|
|
125883
|
+
if (!needsRestore) continue;
|
|
125798
125884
|
data[mod] = {
|
|
125799
125885
|
...finalBlock ?? {},
|
|
125800
125886
|
items: collectItems
|
|
@@ -125805,7 +125891,7 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
125805
125891
|
}
|
|
125806
125892
|
|
|
125807
125893
|
// src/commands/google-ads-diagnosis/narrative-consistency-check.ts
|
|
125808
|
-
function
|
|
125894
|
+
function asRecord8(value) {
|
|
125809
125895
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125810
125896
|
return value;
|
|
125811
125897
|
}
|
|
@@ -125813,14 +125899,14 @@ function asRecord7(value) {
|
|
|
125813
125899
|
}
|
|
125814
125900
|
function collectNarrativeTexts(data) {
|
|
125815
125901
|
const texts = [];
|
|
125816
|
-
const summary =
|
|
125902
|
+
const summary = asRecord8(data.summary);
|
|
125817
125903
|
if (Array.isArray(summary?.keyIssues)) {
|
|
125818
125904
|
for (const item of summary.keyIssues) texts.push(String(item ?? ""));
|
|
125819
125905
|
}
|
|
125820
|
-
const overview =
|
|
125906
|
+
const overview = asRecord8(data.diagnosisOverview);
|
|
125821
125907
|
if (Array.isArray(overview?.disadvantages)) {
|
|
125822
125908
|
for (const item of overview.disadvantages) {
|
|
125823
|
-
const rec =
|
|
125909
|
+
const rec = asRecord8(item);
|
|
125824
125910
|
if (rec) {
|
|
125825
125911
|
texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
|
|
125826
125912
|
} else {
|
|
@@ -125831,16 +125917,16 @@ function collectNarrativeTexts(data) {
|
|
|
125831
125917
|
return texts.filter((t) => t.trim().length > 0);
|
|
125832
125918
|
}
|
|
125833
125919
|
function newFeatureAccountStatus(data, strategy) {
|
|
125834
|
-
const nf =
|
|
125920
|
+
const nf = asRecord8(data.newFeatures);
|
|
125835
125921
|
const items = nf?.items;
|
|
125836
125922
|
if (!Array.isArray(items)) return null;
|
|
125837
|
-
const row = items.find((it) =>
|
|
125838
|
-
const rec =
|
|
125923
|
+
const row = items.find((it) => asRecord8(it)?.strategy === strategy);
|
|
125924
|
+
const rec = asRecord8(row);
|
|
125839
125925
|
if (!rec || typeof rec.accountStatus !== "boolean") return null;
|
|
125840
125926
|
return rec.accountStatus;
|
|
125841
125927
|
}
|
|
125842
125928
|
function goldAccountFlag(data, key) {
|
|
125843
|
-
const gold =
|
|
125929
|
+
const gold = asRecord8(data.goldAccount);
|
|
125844
125930
|
const value = gold?.[key];
|
|
125845
125931
|
return typeof value === "boolean" ? value : null;
|
|
125846
125932
|
}
|
|
@@ -125871,7 +125957,7 @@ function getNarrativeConsistencyErrors(data) {
|
|
|
125871
125957
|
}
|
|
125872
125958
|
|
|
125873
125959
|
// src/commands/google-ads-diagnosis/render-report.ts
|
|
125874
|
-
function
|
|
125960
|
+
function asRecord9(value) {
|
|
125875
125961
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125876
125962
|
return value;
|
|
125877
125963
|
}
|
|
@@ -125880,8 +125966,8 @@ function asRecord8(value) {
|
|
|
125880
125966
|
async function tryLoadCollectReportData(collectPath) {
|
|
125881
125967
|
try {
|
|
125882
125968
|
const raw = JSON.parse(await fs18.readFile(collectPath, "utf8"));
|
|
125883
|
-
const root =
|
|
125884
|
-
const reportData =
|
|
125969
|
+
const root = asRecord9(raw);
|
|
125970
|
+
const reportData = asRecord9(root?.reportData);
|
|
125885
125971
|
return reportData ?? root;
|
|
125886
125972
|
} catch {
|
|
125887
125973
|
return null;
|
|
@@ -125898,7 +125984,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
125898
125984
|
`);
|
|
125899
125985
|
process.exit(1);
|
|
125900
125986
|
}
|
|
125901
|
-
let data =
|
|
125987
|
+
let data = asRecord9(dataRaw);
|
|
125902
125988
|
if (!opts.noMergeCollect) {
|
|
125903
125989
|
const collectPath = path23.resolve(
|
|
125904
125990
|
opts.collectFile ?? path23.join(path23.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
|
|
@@ -126411,7 +126497,7 @@ function ensureScorecardFromSnapshot(healthDiagnosis, merged) {
|
|
|
126411
126497
|
}
|
|
126412
126498
|
|
|
126413
126499
|
// src/commands/facebook-analysis/merge-snapshot.ts
|
|
126414
|
-
function
|
|
126500
|
+
function asRecord10(value) {
|
|
126415
126501
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126416
126502
|
return value;
|
|
126417
126503
|
}
|
|
@@ -126460,7 +126546,7 @@ async function readManifest(snapshotDir) {
|
|
|
126460
126546
|
async function loadSectionFile(snapshotDir, file) {
|
|
126461
126547
|
try {
|
|
126462
126548
|
const raw = await fs21.readFile(path28.join(snapshotDir, file), "utf8");
|
|
126463
|
-
return
|
|
126549
|
+
return asRecord10(JSON.parse(raw));
|
|
126464
126550
|
} catch {
|
|
126465
126551
|
return null;
|
|
126466
126552
|
}
|
|
@@ -126508,7 +126594,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126508
126594
|
if (data) sectionMap.set(art.section, data);
|
|
126509
126595
|
}
|
|
126510
126596
|
const overview = sectionMap.get("overview");
|
|
126511
|
-
const current =
|
|
126597
|
+
const current = asRecord10(overview?.currentPeriod);
|
|
126512
126598
|
const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
|
|
126513
126599
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
126514
126600
|
const kpis = { ...payload.kpis ?? {} };
|
|
@@ -126630,7 +126716,7 @@ function isFiniteNumber(value) {
|
|
|
126630
126716
|
function asArray2(value) {
|
|
126631
126717
|
return Array.isArray(value) ? value : [];
|
|
126632
126718
|
}
|
|
126633
|
-
function
|
|
126719
|
+
function asRecord11(value) {
|
|
126634
126720
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126635
126721
|
return value;
|
|
126636
126722
|
}
|
|
@@ -126642,26 +126728,26 @@ function pushMissing(missing, id, chapter, dimension, hint) {
|
|
|
126642
126728
|
function countAdSetsWithSpend(tables) {
|
|
126643
126729
|
const rows = asArray2(tables?.adSets);
|
|
126644
126730
|
return rows.filter((row) => {
|
|
126645
|
-
const r =
|
|
126731
|
+
const r = asRecord11(row);
|
|
126646
126732
|
const spend = r?.spend;
|
|
126647
126733
|
return isFiniteNumber(spend) && spend > 0;
|
|
126648
126734
|
}).length;
|
|
126649
126735
|
}
|
|
126650
126736
|
function chartLabels(chart) {
|
|
126651
|
-
const c =
|
|
126737
|
+
const c = asRecord11(chart);
|
|
126652
126738
|
return asArray2(c?.labels).length;
|
|
126653
126739
|
}
|
|
126654
126740
|
function validateMetaPeriodReportContent(data) {
|
|
126655
126741
|
const missing = [];
|
|
126656
|
-
const meta =
|
|
126657
|
-
const kpis =
|
|
126658
|
-
const narrative =
|
|
126659
|
-
const tables =
|
|
126660
|
-
const charts =
|
|
126661
|
-
const sections =
|
|
126662
|
-
const health =
|
|
126663
|
-
const priorityPlan =
|
|
126664
|
-
const actionChecklist =
|
|
126742
|
+
const meta = asRecord11(data.meta) ?? {};
|
|
126743
|
+
const kpis = asRecord11(data.kpis) ?? {};
|
|
126744
|
+
const narrative = asRecord11(data.narrative) ?? {};
|
|
126745
|
+
const tables = asRecord11(data.tables);
|
|
126746
|
+
const charts = asRecord11(data.charts);
|
|
126747
|
+
const sections = asRecord11(data.sections);
|
|
126748
|
+
const health = asRecord11(data.healthDiagnosis);
|
|
126749
|
+
const priorityPlan = asRecord11(data.priorityPlan);
|
|
126750
|
+
const actionChecklist = asRecord11(data.actionChecklist);
|
|
126665
126751
|
const adSetsWithSpend2 = countAdSetsWithSpend(tables ?? void 0);
|
|
126666
126752
|
const regionalNarratives = asArray2(narrative.regional).length;
|
|
126667
126753
|
const recommendations = asArray2(narrative.recommendations);
|
|
@@ -126733,7 +126819,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126733
126819
|
"charts.audience.labels \u81F3\u5C11 1 \u9879"
|
|
126734
126820
|
);
|
|
126735
126821
|
}
|
|
126736
|
-
const funnel =
|
|
126822
|
+
const funnel = asRecord11(charts?.funnel);
|
|
126737
126823
|
if (!isFiniteNumber(funnel?.reach)) {
|
|
126738
126824
|
pushMissing(
|
|
126739
126825
|
missing,
|
|
@@ -126797,7 +126883,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126797
126883
|
);
|
|
126798
126884
|
} else {
|
|
126799
126885
|
for (let i = 0; i < regionalNarratives; i++) {
|
|
126800
|
-
const row =
|
|
126886
|
+
const row = asRecord11(asArray2(narrative.regional)[i]);
|
|
126801
126887
|
if (textLen2(row?.text) < 80) {
|
|
126802
126888
|
pushMissing(
|
|
126803
126889
|
missing,
|
|
@@ -126827,7 +126913,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126827
126913
|
"\u5FC5\u987B\u6070\u597D 4 \u6761\uFF08\u7B80\u5316\u8868\u5355/\u533A\u57DF\u8C03\u6574/\u9884\u7B97\u91CD\u6784/\u7D20\u6750\u5EFA\u8BAE\uFF09"
|
|
126828
126914
|
);
|
|
126829
126915
|
} else {
|
|
126830
|
-
const titles = recommendations.map((r) =>
|
|
126916
|
+
const titles = recommendations.map((r) => asRecord11(r)?.title);
|
|
126831
126917
|
for (const title of RECOMMENDATION_TITLES) {
|
|
126832
126918
|
if (!titles.includes(title)) {
|
|
126833
126919
|
pushMissing(
|
|
@@ -126840,7 +126926,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126840
126926
|
}
|
|
126841
126927
|
}
|
|
126842
126928
|
for (let i = 0; i < recommendations.length; i++) {
|
|
126843
|
-
const rec =
|
|
126929
|
+
const rec = asRecord11(recommendations[i]);
|
|
126844
126930
|
if (textLen2(rec?.content) < 150) {
|
|
126845
126931
|
pushMissing(
|
|
126846
126932
|
missing,
|
|
@@ -126862,7 +126948,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126862
126948
|
);
|
|
126863
126949
|
} else {
|
|
126864
126950
|
for (let i = 0; i < supplementary.length; i++) {
|
|
126865
|
-
const row =
|
|
126951
|
+
const row = asRecord11(supplementary[i]);
|
|
126866
126952
|
if (!textLen2(row?.dimension) || !textLen2(row?.issue)) {
|
|
126867
126953
|
pushMissing(
|
|
126868
126954
|
missing,
|
|
@@ -126944,7 +127030,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126944
127030
|
);
|
|
126945
127031
|
} else {
|
|
126946
127032
|
for (let i = 0; i < fourQuestions.length; i++) {
|
|
126947
|
-
const q =
|
|
127033
|
+
const q = asRecord11(fourQuestions[i]);
|
|
126948
127034
|
const evidence = asArray2(q?.evidence).filter((e) => textLen2(e) > 0);
|
|
126949
127035
|
if (evidence.length < 2) {
|
|
126950
127036
|
pushMissing(
|
|
@@ -126968,7 +127054,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126968
127054
|
}
|
|
126969
127055
|
let scorecardValidRows = 0;
|
|
126970
127056
|
for (let i = 0; i < scorecard.length; i++) {
|
|
126971
|
-
const row =
|
|
127057
|
+
const row = asRecord11(scorecard[i]);
|
|
126972
127058
|
const itemOk = textLen2(row?.item) > 0;
|
|
126973
127059
|
const dataOk = textLen2(row?.data) > 0;
|
|
126974
127060
|
const signalOk = SCORECARD_SIGNALS.includes(row?.signal);
|
|
@@ -127010,7 +127096,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127010
127096
|
);
|
|
127011
127097
|
}
|
|
127012
127098
|
for (const key of ["platform", "country", "adSets"]) {
|
|
127013
|
-
const insight = textLen2(
|
|
127099
|
+
const insight = textLen2(asRecord11(sections?.[key])?.insight);
|
|
127014
127100
|
if (insight < 200) {
|
|
127015
127101
|
pushMissing(
|
|
127016
127102
|
missing,
|
|
@@ -127021,7 +127107,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127021
127107
|
);
|
|
127022
127108
|
}
|
|
127023
127109
|
}
|
|
127024
|
-
const audience =
|
|
127110
|
+
const audience = asRecord11(sections?.audience);
|
|
127025
127111
|
if (asArray2(audience?.goldenProfile).filter((s) => textLen2(s) > 0).length < 3) {
|
|
127026
127112
|
pushMissing(
|
|
127027
127113
|
missing,
|
|
@@ -127049,7 +127135,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127049
127135
|
"\u2265150 \u5B57"
|
|
127050
127136
|
);
|
|
127051
127137
|
}
|
|
127052
|
-
const landingRows = asArray2(
|
|
127138
|
+
const landingRows = asArray2(asRecord11(sections?.landingPage)?.rows);
|
|
127053
127139
|
if (landingRows.length < 3) {
|
|
127054
127140
|
pushMissing(
|
|
127055
127141
|
missing,
|
|
@@ -127134,7 +127220,7 @@ async function readJsonFile2(filePath) {
|
|
|
127134
127220
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
127135
127221
|
}
|
|
127136
127222
|
}
|
|
127137
|
-
function
|
|
127223
|
+
function asRecord12(value) {
|
|
127138
127224
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127139
127225
|
return value;
|
|
127140
127226
|
}
|
|
@@ -127149,7 +127235,7 @@ function injectReportData2(html, payload) {
|
|
|
127149
127235
|
}
|
|
127150
127236
|
async function runFacebookAnalysisRender(opts) {
|
|
127151
127237
|
const dataPath = path29.resolve(opts.dataFile);
|
|
127152
|
-
let data =
|
|
127238
|
+
let data = asRecord12(await readJsonFile2(dataPath));
|
|
127153
127239
|
if (opts.snapshotDir?.trim()) {
|
|
127154
127240
|
data = await mergeFacebookSnapshotIntoReport(data, path29.resolve(opts.snapshotDir));
|
|
127155
127241
|
}
|
package/dist/skill/_meta.json
CHANGED
|
@@ -47,7 +47,7 @@ siluzan-tso google-ads-diagnosis render \
|
|
|
47
47
|
- 若环比列全为 `null`,检查落盘目录是否存在上一周期文件(如 `campaigns-<id>_20260503-20260602.json`);不存在则**重新执行 collect**(勿用手动拼装或 Python 脚本绕过 CLI)。
|
|
48
48
|
|
|
49
49
|
- **render 自动合并(默认开启)**:若同目录存在 `google-ads-diagnosis-collect.json`,且 Agent 改坏了 `campaigns` / `geographic` / `keywords` 的 `items`(缺 `title`/`currentCost` 或环比写成字符串)或整体覆盖丢弃了 `landingPageAnalysis.desktop`/`mobile`,CLI **自动从 collect.reportData 恢复**,保留 Agent 的 `analysis`/`suggestions`。可用 `--no-merge-collect` 关闭;`--collect <file>` 指定 collect 路径。
|
|
50
|
-
- **render 硬校验**:合并后对比表仍不完整则 **exit 1**(避免 HTML 表体全空);`summary.keyIssues` / `diagnosisOverview.disadvantages` 与 `newFeatures`/`goldAccount` 等事实字段出现已知矛盾模式(见「撰写硬约束」第 5 条)时同样 **exit 1**。
|
|
50
|
+
- **render 硬校验**:合并后对比表仍不完整则 **exit 1**(避免 HTML 表体全空);`campaigns/geographic/keywords.items` 默认同目录 collect **整表覆盖** Agent 改动(Agent 只填 analysis/suggestions);`summary.keyIssues` / `diagnosisOverview.disadvantages` 与 `newFeatures`/`goldAccount` 等事实字段出现已知矛盾模式(见「撰写硬约束」第 5 条)时同样 **exit 1**。
|
|
51
51
|
|
|
52
52
|
- **禁止**在 `collect` 阶段生成或写入 `analysis` / `suggestions` / `diagnosisOverview` / `summary` 等建议性文案。
|
|
53
53
|
- **禁止**跳过 Agent 直接 `render` collect 产物(`render` 会校验全部叙事 / 建议字段;调试可加 `--lenient`)。
|
|
@@ -130,6 +130,13 @@ siluzan-tso google-ads-diagnosis render \
|
|
|
130
130
|
- `render` 会在同目录存在 `google-ads-diagnosis-collect.json` 时自动从 collect 恢复缺失/损坏的 `desktop`/`mobile`(保留 Agent 写的 `analysis`/`suggestions`),但**不能依赖这个兜底**——终稿应本就保留完整字段。
|
|
131
131
|
- 若 `collect` 里 `desktop`/`mobile` 本身就是空/全 0(说明 Lighthouse 没有抓到数据),`analysis` 必须写「着陆页测速数据未获取」,**禁止**编造评分或分析文字。
|
|
132
132
|
|
|
133
|
+
### 7. 环比对比表 `campaigns/geographic/keywords.items` — Agent 禁止写入
|
|
134
|
+
|
|
135
|
+
- `collect` 已根据当期 + 上一周期快照生成对比行(`title`、`currentCost`/`previousCost`、`costRateChange`、`currentClicks`/`previousClicks`、`clicksRateChange`、`currentCvr`/`previousCvr`、`cvrRateChange`)。Agent 撰写终稿时**只填** `analysis` / `suggestions`,**禁止**写入、清空、删减或「手工重写」`items` 数组
|
|
136
|
+
- `agentBrief.comparisonTableItemCounts` 给出 collect 侧行数(如 `keywords: 15`);终稿 `keywords.items.length` 须与之一致。若 Agent 误改,`render` 会**始终以**同目录 `google-ads-diagnosis-collect.json` **覆盖**上述三个模块的 `items`(保留 Agent 的 narrative)。
|
|
137
|
+
- 分析文案须引用对比表中的具体词/系列/国家名与环比数字(如「fitness mat 消耗 $230.60,环比下降 39.6%」),**禁止**在 `items` 有数据时写「关键词对比表为空」类结论。
|
|
138
|
+
- `biddingStrategy.items` / `newFeatures.items` 行结构由 collect 生成,Agent 可在合法行上填写 `recommendationReason` / `optimizerRecommendation`,但勿篡改 `duration`(须为天数 number)、`biddingStrategyType`、`strategyName` 等事实字段。
|
|
139
|
+
|
|
133
140
|
---
|
|
134
141
|
|
|
135
142
|
## 页眉信息(报告头)
|
|
@@ -246,17 +253,19 @@ siluzan-tso google-ads-diagnosis render \
|
|
|
246
253
|
- 指标列:花费(上期/本期/环比)、点击(上期/本期/环比)、转化率(上期/本期/环比)
|
|
247
254
|
- **数据**:`campaigns.items`(`title`、`previousCost`、`currentCost`、`costRateChange`、`previousClicks`、`currentClicks`、`clicksRateChange`、`previousCvr`、`currentCvr`、`cvrRateChange`)
|
|
248
255
|
- **分析 / 建议**:`campaigns.analysis`、`campaigns.suggestions`
|
|
249
|
-
- **Agent
|
|
256
|
+
- **Agent 禁止写入 `items` 行**(只填 `analysis`/`suggestions`);`render` 会以 collect 为准**自动覆盖** Agent 改动的对比表 items
|
|
250
257
|
|
|
251
258
|
#### 国家地区分析
|
|
252
259
|
|
|
253
260
|
- 维度列:**国家/地区**
|
|
254
261
|
- **数据**:`geographic.items`(同上结构)
|
|
262
|
+
- **Agent 禁止写入 `items` 行**;`render` 自动从 collect 恢复
|
|
255
263
|
|
|
256
264
|
#### 关键词分析
|
|
257
265
|
|
|
258
266
|
- 维度列:**关键词**
|
|
259
|
-
- **数据**:`keywords.items`
|
|
267
|
+
- **数据**:`keywords.items`(字段同上;行数见 `agentBrief.comparisonTableItemCounts.keywords`)
|
|
268
|
+
- **Agent 禁止写入/清空 `items`**;分析须引用表中具体词名与环比,勿写「对比表为空」
|
|
260
269
|
|
|
261
270
|
#### 转化成本 / 按日趋势(若页面已渲染)
|
|
262
271
|
|
|
@@ -47,7 +47,7 @@ siluzan-tso google-ads-diagnosis render \
|
|
|
47
47
|
- 若环比列全为 `null`,检查落盘目录是否存在上一周期文件(如 `campaigns-<id>_20260503-20260602.json`);不存在则**重新执行 collect**(勿用手动拼装或 Python 脚本绕过 CLI)。
|
|
48
48
|
|
|
49
49
|
- **render 自动合并(默认开启)**:若同目录存在 `google-ads-diagnosis-collect.json`,且 Agent 改坏了 `campaigns` / `geographic` / `keywords` 的 `items`(缺 `title`/`currentCost` 或环比写成字符串)或整体覆盖丢弃了 `landingPageAnalysis.desktop`/`mobile`,CLI **自动从 collect.reportData 恢复**,保留 Agent 的 `analysis`/`suggestions`。可用 `--no-merge-collect` 关闭;`--collect <file>` 指定 collect 路径。
|
|
50
|
-
- **render 硬校验**:合并后对比表仍不完整则 **exit 1**(避免 HTML 表体全空);`summary.keyIssues` / `diagnosisOverview.disadvantages` 与 `newFeatures`/`goldAccount` 等事实字段出现已知矛盾模式(见「撰写硬约束」第 5 条)时同样 **exit 1**。
|
|
50
|
+
- **render 硬校验**:合并后对比表仍不完整则 **exit 1**(避免 HTML 表体全空);`campaigns/geographic/keywords.items` 默认同目录 collect **整表覆盖** Agent 改动(Agent 只填 analysis/suggestions);`summary.keyIssues` / `diagnosisOverview.disadvantages` 与 `newFeatures`/`goldAccount` 等事实字段出现已知矛盾模式(见「撰写硬约束」第 5 条)时同样 **exit 1**。
|
|
51
51
|
|
|
52
52
|
- **禁止**在 `collect` 阶段生成或写入 `analysis` / `suggestions` / `diagnosisOverview` / `summary` 等建议性文案。
|
|
53
53
|
- **禁止**跳过 Agent 直接 `render` collect 产物(`render` 会校验全部叙事 / 建议字段;调试可加 `--lenient`)。
|
|
@@ -130,6 +130,13 @@ siluzan-tso google-ads-diagnosis render \
|
|
|
130
130
|
- `render` 会在同目录存在 `google-ads-diagnosis-collect.json` 时自动从 collect 恢复缺失/损坏的 `desktop`/`mobile`(保留 Agent 写的 `analysis`/`suggestions`),但**不能依赖这个兜底**——终稿应本就保留完整字段。
|
|
131
131
|
- 若 `collect` 里 `desktop`/`mobile` 本身就是空/全 0(说明 Lighthouse 没有抓到数据),`analysis` 必须写「着陆页测速数据未获取」,**禁止**编造评分或分析文字。
|
|
132
132
|
|
|
133
|
+
### 7. 环比对比表 `campaigns/geographic/keywords.items` — Agent 禁止写入
|
|
134
|
+
|
|
135
|
+
- `collect` 已根据当期 + 上一周期快照生成对比行(`title`、`currentCost`/`previousCost`、`costRateChange`、`currentClicks`/`previousClicks`、`clicksRateChange`、`currentCvr`/`previousCvr`、`cvrRateChange`)。Agent 撰写终稿时**只填** `analysis` / `suggestions`,**禁止**写入、清空、删减或「手工重写」`items` 数组
|
|
136
|
+
- `agentBrief.comparisonTableItemCounts` 给出 collect 侧行数(如 `keywords: 15`);终稿 `keywords.items.length` 须与之一致。若 Agent 误改,`render` 会**始终以**同目录 `google-ads-diagnosis-collect.json` **覆盖**上述三个模块的 `items`(保留 Agent 的 narrative)。
|
|
137
|
+
- 分析文案须引用对比表中的具体词/系列/国家名与环比数字(如「fitness mat 消耗 $230.60,环比下降 39.6%」),**禁止**在 `items` 有数据时写「关键词对比表为空」类结论。
|
|
138
|
+
- `biddingStrategy.items` / `newFeatures.items` 行结构由 collect 生成,Agent 可在合法行上填写 `recommendationReason` / `optimizerRecommendation`,但勿篡改 `duration`(须为天数 number)、`biddingStrategyType`、`strategyName` 等事实字段。
|
|
139
|
+
|
|
133
140
|
---
|
|
134
141
|
|
|
135
142
|
## 页眉信息(报告头)
|
|
@@ -246,17 +253,19 @@ siluzan-tso google-ads-diagnosis render \
|
|
|
246
253
|
- 指标列:花费(上期/本期/环比)、点击(上期/本期/环比)、转化率(上期/本期/环比)
|
|
247
254
|
- **数据**:`campaigns.items`(`title`、`previousCost`、`currentCost`、`costRateChange`、`previousClicks`、`currentClicks`、`clicksRateChange`、`previousCvr`、`currentCvr`、`cvrRateChange`)
|
|
248
255
|
- **分析 / 建议**:`campaigns.analysis`、`campaigns.suggestions`
|
|
249
|
-
- **Agent
|
|
256
|
+
- **Agent 禁止写入 `items` 行**(只填 `analysis`/`suggestions`);`render` 会以 collect 为准**自动覆盖** Agent 改动的对比表 items
|
|
250
257
|
|
|
251
258
|
#### 国家地区分析
|
|
252
259
|
|
|
253
260
|
- 维度列:**国家/地区**
|
|
254
261
|
- **数据**:`geographic.items`(同上结构)
|
|
262
|
+
- **Agent 禁止写入 `items` 行**;`render` 自动从 collect 恢复
|
|
255
263
|
|
|
256
264
|
#### 关键词分析
|
|
257
265
|
|
|
258
266
|
- 维度列:**关键词**
|
|
259
|
-
- **数据**:`keywords.items`
|
|
267
|
+
- **数据**:`keywords.items`(字段同上;行数见 `agentBrief.comparisonTableItemCounts.keywords`)
|
|
268
|
+
- **Agent 禁止写入/清空 `items`**;分析须引用表中具体词名与环比,勿写「对比表为空」
|
|
260
269
|
|
|
261
270
|
#### 转化成本 / 按日趋势(若页面已渲染)
|
|
262
271
|
|
|
@@ -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.35-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.35-beta.4'
|
|
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.35-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.35-beta.4"
|
|
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"
|