siluzan-tso-cli 1.1.26-beta.1 → 1.1.26-beta.2
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 +150 -23
- package/dist/skill/SKILL.md +5 -3
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/README.md +2 -1
- package/dist/skill/references/accounts/accounts.md +2 -2
- package/dist/skill/references/accounts/open-account-by-media.md +4 -2
- package/dist/skill/references/analytics/account-analytics.md +2 -2
- package/dist/skill/references/core/agent-conventions.md +20 -5
- package/dist/skill/references/core/deliverable-preflight.md +107 -0
- package/dist/skill/references/core/playbooks.md +4 -2
- package/dist/skill/references/core/subagent-orchestration.md +1 -1
- package/dist/skill/references/core/tips.md +14 -11
- package/dist/skill/references/core/workflows.md +2 -2
- package/dist/skill/report-templates/REPORT-WORKFLOW.md +8 -0
- package/dist/skill/scripts/check-deliverable-snap.mjs +170 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/eval/stub-fixtures/meta-overview.json +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.26-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.26-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -4014,7 +4014,7 @@ var init_cli_json = __esm({
|
|
|
4014
4014
|
CLI_SNAPSHOT_MANIFEST_FILE = "cli-manifest.json";
|
|
4015
4015
|
CLI_PACKAGE = "siluzan-tso-cli";
|
|
4016
4016
|
SCHEMA_VERSION = 1;
|
|
4017
|
-
OUTLINE_AGENT_HINT = "\
|
|
4017
|
+
OUTLINE_AGENT_HINT = "\u7981\u6B62\u7528 Read/cat \u6253\u5F00 writtenFiles \u4E2D\u7684\u5B8C\u6574\u4E1A\u52A1 *.json\u3002\u987A\u5E8F\uFF1A1) \u89E3\u6790\u672C\u884C stdout \u6458\u8981\uFF1B2) \u4EC5\u53EF\u8BFB outlineFile\uFF08*.outline.txt\uFF0Cschema\uFF09\uFF1B3) \u5FC5\u987B\u7528 node/python \u811A\u672C require/readFileSync \u8BFB JSON \u5E76\u6253\u5370\u6240\u9700\u5B57\u6BB5\u3002\u52FF\u628A JSON \u8D34\u8FDB\u5BF9\u8BDD\u3002";
|
|
4018
4018
|
DEFAULT_FIELD_GUIDE = {
|
|
4019
4019
|
markdownRefs: ["references/accounts/currency.md", "references/core/tips.md", "references/accounts/accounts.md"]
|
|
4020
4020
|
};
|
|
@@ -120073,7 +120073,133 @@ init_auth();
|
|
|
120073
120073
|
import * as fs9 from "fs";
|
|
120074
120074
|
import * as path14 from "path";
|
|
120075
120075
|
init_cli_json_snapshot();
|
|
120076
|
-
|
|
120076
|
+
|
|
120077
|
+
// src/commands/open-account/bing-industry-dict.ts
|
|
120078
|
+
init_auth();
|
|
120079
|
+
var ORPHAN_PARENT_KEY = "__orphan__";
|
|
120080
|
+
function normalizeBingTradeReadList(readResponse) {
|
|
120081
|
+
let raw = readResponse;
|
|
120082
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
120083
|
+
const o = raw;
|
|
120084
|
+
if ("value" in o && Array.isArray(o.value)) {
|
|
120085
|
+
raw = o.value;
|
|
120086
|
+
} else if ("data" in o) {
|
|
120087
|
+
raw = o.data;
|
|
120088
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
120089
|
+
const inner = raw;
|
|
120090
|
+
if (Array.isArray(inner.data)) raw = inner.data;
|
|
120091
|
+
else if (Array.isArray(inner.value)) raw = inner.value;
|
|
120092
|
+
}
|
|
120093
|
+
}
|
|
120094
|
+
}
|
|
120095
|
+
return Array.isArray(raw) ? raw : [];
|
|
120096
|
+
}
|
|
120097
|
+
function applyBingTradeTranslations(records, translationRows) {
|
|
120098
|
+
for (const item of records) {
|
|
120099
|
+
for (const row of translationRows) {
|
|
120100
|
+
if (item.name == row.name) {
|
|
120101
|
+
item.TranslationLanguage = row.value;
|
|
120102
|
+
break;
|
|
120103
|
+
}
|
|
120104
|
+
}
|
|
120105
|
+
}
|
|
120106
|
+
}
|
|
120107
|
+
function extractTradeListRecords(raw) {
|
|
120108
|
+
if (Array.isArray(raw)) return raw;
|
|
120109
|
+
if (!raw || typeof raw !== "object") return [];
|
|
120110
|
+
const o = raw;
|
|
120111
|
+
const data = o.data;
|
|
120112
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
120113
|
+
const records = data.records;
|
|
120114
|
+
if (Array.isArray(records)) return records;
|
|
120115
|
+
}
|
|
120116
|
+
if (Array.isArray(o.records)) return o.records;
|
|
120117
|
+
return [];
|
|
120118
|
+
}
|
|
120119
|
+
function buildBingIndustryOptions(records) {
|
|
120120
|
+
const byId = new Map(records.map((r) => [String(r.id), r]));
|
|
120121
|
+
const level2 = records.filter(
|
|
120122
|
+
(r) => String(r.tradeLevel) === "2" || r.tradeLevel === 2
|
|
120123
|
+
);
|
|
120124
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
120125
|
+
for (const row of level2) {
|
|
120126
|
+
const pid = row.parentId == null ? ORPHAN_PARENT_KEY : String(row.parentId);
|
|
120127
|
+
if (!byParent.has(pid)) byParent.set(pid, []);
|
|
120128
|
+
byParent.get(pid).push(row);
|
|
120129
|
+
}
|
|
120130
|
+
const cmpCode = (a, b) => String(a.code ?? "").localeCompare(String(b.code ?? ""), void 0, { numeric: true });
|
|
120131
|
+
const groups = [];
|
|
120132
|
+
for (const [parentKey, children] of byParent) {
|
|
120133
|
+
const parent = parentKey === ORPHAN_PARENT_KEY ? null : byId.get(parentKey);
|
|
120134
|
+
const label = parent?.TranslationLanguage || parent?.name || (parentKey === ORPHAN_PARENT_KEY ? "\u2014" : parentKey);
|
|
120135
|
+
groups.push({
|
|
120136
|
+
parentId: parentKey === ORPHAN_PARENT_KEY ? "" : parentKey,
|
|
120137
|
+
label,
|
|
120138
|
+
children: [...children].sort(cmpCode)
|
|
120139
|
+
});
|
|
120140
|
+
}
|
|
120141
|
+
const emptyParent = { id: "", code: "" };
|
|
120142
|
+
groups.sort((a, b) => {
|
|
120143
|
+
const pa = a.parentId ? byId.get(a.parentId) : null;
|
|
120144
|
+
const pb = b.parentId ? byId.get(b.parentId) : null;
|
|
120145
|
+
return cmpCode(pa ?? emptyParent, pb ?? emptyParent);
|
|
120146
|
+
});
|
|
120147
|
+
const out = [];
|
|
120148
|
+
for (const g of groups) {
|
|
120149
|
+
for (const row of g.children) {
|
|
120150
|
+
const id = String(row.id ?? "");
|
|
120151
|
+
const label = String(row.TranslationLanguage || row.name || id);
|
|
120152
|
+
out.push({
|
|
120153
|
+
id,
|
|
120154
|
+
label,
|
|
120155
|
+
group: g.label,
|
|
120156
|
+
name: String(row.name ?? "")
|
|
120157
|
+
});
|
|
120158
|
+
}
|
|
120159
|
+
}
|
|
120160
|
+
return out;
|
|
120161
|
+
}
|
|
120162
|
+
function filterBingIndustryOptions(options, keyword) {
|
|
120163
|
+
const kw = keyword.trim().toLowerCase();
|
|
120164
|
+
if (!kw) return options;
|
|
120165
|
+
return options.filter((o) => {
|
|
120166
|
+
const hay = `${o.id} ${o.label} ${o.group} ${o.name}`.toLowerCase();
|
|
120167
|
+
return hay.includes(kw);
|
|
120168
|
+
});
|
|
120169
|
+
}
|
|
120170
|
+
function resolveBingTradeIdForSubmit(tradeIdOrLabel, options) {
|
|
120171
|
+
const raw = tradeIdOrLabel.trim();
|
|
120172
|
+
if (!raw) return raw;
|
|
120173
|
+
const byId = options.find((o) => o.id === raw);
|
|
120174
|
+
if (byId) return byId.id;
|
|
120175
|
+
const byLabel = options.find(
|
|
120176
|
+
(o) => o.label === raw || o.name === raw || `${o.group}/${o.label}` === raw
|
|
120177
|
+
);
|
|
120178
|
+
if (byLabel) return byLabel.id;
|
|
120179
|
+
return raw;
|
|
120180
|
+
}
|
|
120181
|
+
async function fetchBingIndustryOptions(config, verbose) {
|
|
120182
|
+
const rawList = await apiFetch2(
|
|
120183
|
+
`${config.apiBaseUrl}/BingV2Account/Management/TradeList`,
|
|
120184
|
+
config,
|
|
120185
|
+
{ method: "POST", body: JSON.stringify({ page: 1, pageSize: 9999 }) },
|
|
120186
|
+
verbose
|
|
120187
|
+
);
|
|
120188
|
+
const records = extractTradeListRecords(rawList).map((r) => ({
|
|
120189
|
+
...r,
|
|
120190
|
+
id: String(r.id ?? "")
|
|
120191
|
+
}));
|
|
120192
|
+
const rawRead = await apiFetch2(
|
|
120193
|
+
`${config.apiBaseUrl}/query/media-account/bing/BingTradeList/Read`,
|
|
120194
|
+
config,
|
|
120195
|
+
{},
|
|
120196
|
+
verbose
|
|
120197
|
+
);
|
|
120198
|
+
applyBingTradeTranslations(records, normalizeBingTradeReadList(rawRead));
|
|
120199
|
+
return buildBingIndustryOptions(records);
|
|
120200
|
+
}
|
|
120201
|
+
|
|
120202
|
+
// src/commands/open-account/bing.ts
|
|
120077
120203
|
async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
|
|
120078
120204
|
const fileName = path14.basename(filePath);
|
|
120079
120205
|
const fileBuffer = fs9.readFileSync(filePath);
|
|
@@ -120152,13 +120278,19 @@ async function runOpenAccountBing(opts) {
|
|
|
120152
120278
|
opts.advertiserId
|
|
120153
120279
|
);
|
|
120154
120280
|
const accountCount = Math.min(Math.max(opts.accountCount ?? 1, 1), 6);
|
|
120281
|
+
let tradeId = opts.tradeId;
|
|
120282
|
+
try {
|
|
120283
|
+
const industryOptions = await fetchBingIndustryOptions(config, opts.verbose);
|
|
120284
|
+
tradeId = resolveBingTradeIdForSubmit(opts.tradeId, industryOptions);
|
|
120285
|
+
} catch {
|
|
120286
|
+
}
|
|
120155
120287
|
const body = {
|
|
120156
120288
|
mediaAccountGroupId: magKey,
|
|
120157
120289
|
accountCount,
|
|
120158
120290
|
bingCustomerInfo: {
|
|
120159
120291
|
name: opts.advertiserName,
|
|
120160
120292
|
province: opts.province,
|
|
120161
|
-
tradeId
|
|
120293
|
+
tradeId,
|
|
120162
120294
|
nameShort: opts.nameShort,
|
|
120163
120295
|
postcode: opts.postcode ?? "",
|
|
120164
120296
|
city: opts.city,
|
|
@@ -120178,7 +120310,7 @@ async function runOpenAccountBing(opts) {
|
|
|
120178
120310
|
companyName: opts.advertiserName,
|
|
120179
120311
|
postcode: opts.postcode ?? "",
|
|
120180
120312
|
province: opts.province,
|
|
120181
|
-
tradeId
|
|
120313
|
+
tradeId,
|
|
120182
120314
|
nameShort: opts.nameShort,
|
|
120183
120315
|
fileId,
|
|
120184
120316
|
fileName,
|
|
@@ -120207,13 +120339,7 @@ async function runOpenAccountBingIndustries(opts) {
|
|
|
120207
120339
|
const config = loadConfig(opts.token);
|
|
120208
120340
|
let list;
|
|
120209
120341
|
try {
|
|
120210
|
-
|
|
120211
|
-
`${config.apiBaseUrl}/query/media-account/bing/BingTradeList/Read`,
|
|
120212
|
-
config,
|
|
120213
|
-
{},
|
|
120214
|
-
opts.verbose
|
|
120215
|
-
);
|
|
120216
|
-
list = Array.isArray(raw) ? raw : raw.value ?? [];
|
|
120342
|
+
list = await fetchBingIndustryOptions(config, opts.verbose);
|
|
120217
120343
|
} catch (err) {
|
|
120218
120344
|
console.error(
|
|
120219
120345
|
`
|
|
@@ -120222,10 +120348,7 @@ async function runOpenAccountBingIndustries(opts) {
|
|
|
120222
120348
|
);
|
|
120223
120349
|
process.exit(1);
|
|
120224
120350
|
}
|
|
120225
|
-
|
|
120226
|
-
if (kw) {
|
|
120227
|
-
list = list.filter((r) => r.name.toLowerCase().includes(kw));
|
|
120228
|
-
}
|
|
120351
|
+
list = filterBingIndustryOptions(list, opts.keyword ?? "");
|
|
120229
120352
|
const n = list.length;
|
|
120230
120353
|
const bingIndPayload = wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items: list });
|
|
120231
120354
|
if (await emitCliJsonOrSnapshot(opts, {
|
|
@@ -120237,17 +120360,21 @@ async function runOpenAccountBingIndustries(opts) {
|
|
|
120237
120360
|
}
|
|
120238
120361
|
console.log(
|
|
120239
120362
|
`
|
|
120240
|
-
BingV2 \u884C\u4E1A\u5217\u8868\uFF08\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${list.length} \u6761\uFF0C\u5171 ${list.length} \u6761\uFF0C\
|
|
120363
|
+
BingV2 \u884C\u4E1A\u5217\u8868\uFF08\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${list.length} \u6761\uFF0C\u5171 ${list.length} \u6761\uFF0C--trade-id \u4F20 id \u5217\uFF0C\u4E0E\u7F51\u9875\u4E0B\u62C9\u4E00\u81F4\uFF09
|
|
120241
120364
|
`
|
|
120242
120365
|
);
|
|
120243
120366
|
if (list.length === 0) {
|
|
120244
|
-
console.log(" \u65E0\u6570\u636E\u3002\n");
|
|
120367
|
+
console.log(" \u65E0\u6570\u636E\u3002\u53EF\u53BB\u6389 --keyword \u67E5\u770B\u5168\u91CF\uFF0C\u6216\u6362\u66F4\u77ED\u7684\u4E2D\u6587\u5173\u952E\u8BCD\u3002\n");
|
|
120245
120368
|
return;
|
|
120246
120369
|
}
|
|
120247
|
-
|
|
120248
|
-
|
|
120249
|
-
|
|
120250
|
-
);
|
|
120370
|
+
let lastGroup = "";
|
|
120371
|
+
for (const row of list) {
|
|
120372
|
+
if (row.group !== lastGroup) {
|
|
120373
|
+
console.log(` \u25B8 ${row.group}`);
|
|
120374
|
+
lastGroup = row.group;
|
|
120375
|
+
}
|
|
120376
|
+
console.log(` ${row.label} (id: ${row.id})`);
|
|
120377
|
+
}
|
|
120251
120378
|
console.log();
|
|
120252
120379
|
}
|
|
120253
120380
|
|
|
@@ -121106,7 +121233,7 @@ function register24(program2) {
|
|
|
121106
121233
|
});
|
|
121107
121234
|
}
|
|
121108
121235
|
);
|
|
121109
|
-
openAccountCmd.command("bing").description("\u63D0\u4EA4 Bing/BingV2 \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--pattern <Direct|Agency>", "\u76F4\u63A5/\u4EE3\u7406\uFF1ADirect\uFF08\u76F4\u63A5\uFF09\u6216 Agency\uFF08\u4EE3\u7406\uFF09").requiredOption("--advertiser-name <name>", "\u516C\u53F8\u540D\u79F0\uFF08\u5E7F\u544A\u4E3B\u5168\u79F0\uFF0C\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--name-short <short>", "\u516C\u53F8\u7B80\u79F0").requiredOption("--name-remark-list <name>", "\u5F00\u6237\u540D\u79F0\uFF08\u8D26\u6237\u663E\u793A\u540D\uFF09").requiredOption("--province <province>", "\u516C\u53F8\u6240\u5728\u7701\u4EFD").requiredOption("--city <city>", "\u516C\u53F8\u6240\u5728\u57CE\u5E02").requiredOption("--address <address>", "\u516C\u53F8\u8BE6\u7EC6\u5730\u5740").requiredOption("--postcode <code>", "\u516C\u53F8\u6240\u5728\u5730\u90AE\u7F16").requiredOption("--trade-id <
|
|
121236
|
+
openAccountCmd.command("bing").description("\u63D0\u4EA4 Bing/BingV2 \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--pattern <Direct|Agency>", "\u76F4\u63A5/\u4EE3\u7406\uFF1ADirect\uFF08\u76F4\u63A5\uFF09\u6216 Agency\uFF08\u4EE3\u7406\uFF09").requiredOption("--advertiser-name <name>", "\u516C\u53F8\u540D\u79F0\uFF08\u5E7F\u544A\u4E3B\u5168\u79F0\uFF0C\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--name-short <short>", "\u516C\u53F8\u7B80\u79F0").requiredOption("--name-remark-list <name>", "\u5F00\u6237\u540D\u79F0\uFF08\u8D26\u6237\u663E\u793A\u540D\uFF09").requiredOption("--province <province>", "\u516C\u53F8\u6240\u5728\u7701\u4EFD").requiredOption("--city <city>", "\u516C\u53F8\u6240\u5728\u57CE\u5E02").requiredOption("--address <address>", "\u516C\u53F8\u8BE6\u7EC6\u5730\u5740").requiredOption("--postcode <code>", "\u516C\u53F8\u6240\u5728\u5730\u90AE\u7F16").requiredOption("--trade-id <id>", "\u884C\u4E1A tradeId\uFF08\u6765\u81EA open-account bing-industries \u7684 id\uFF0C\u4E0E\u7F51\u9875\u4E0B\u62C9 value \u4E00\u81F4\uFF09").requiredOption("--promotion-link <url>", "\u5F00\u6237\u7F51\u5740\uFF08\u63A8\u5E7F\u94FE\u63A5\uFF09").requiredOption("--license-file <path>", "\u8425\u4E1A\u6267\u7167\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF08JPG/PNG/PDF\uFF09").option("--account-count <n>", "\u5F00\u6237\u6570\u91CF\uFF081~6\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).option("--advertiser-cid <cid>", "\u7ECF\u7406\u8D26\u53F7 ID\uFF08\u53EF\u9009\uFF09").option("--advertiser-name2 <name>", "\u7ECF\u7406\u8D26\u53F7\u540D\u79F0\uFF08\u53EF\u9009\uFF0C\u4E0E --advertiser-cid \u914D\u5BF9\uFF09").option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 magKey\uFF08\u4E00\u822C\u65E0\u9700\u586B\u5199\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
121110
121237
|
async (opts) => {
|
|
121111
121238
|
await runOpenAccountBing({
|
|
121112
121239
|
token: opts.token,
|
|
@@ -121171,7 +121298,7 @@ function register24(program2) {
|
|
|
121171
121298
|
});
|
|
121172
121299
|
}
|
|
121173
121300
|
);
|
|
121174
|
-
openAccountCmd.command("bing-industries").description("\u5217\u51FA BingV2 \u884C\u4E1A\u5206\u7C7B\uFF08\u5C06
|
|
121301
|
+
openAccountCmd.command("bing-industries").description("\u5217\u51FA BingV2 \u884C\u4E1A\u5206\u7C7B\uFF08\u4E0E\u7F51\u9875\u540C\u6E90\uFF1B\u5C06 id \u4F20\u7ED9 open-account bing \u7684 --trade-id\uFF09").option("-k, --keyword <text>", "\u6309\u4E2D\u6587\u884C\u4E1A\u540D\u3001\u5206\u7EC4\u540D\u3001\u82F1\u6587 name \u6216 id \u8FC7\u6EE4").option(
|
|
121175
121302
|
"--json-out <path>",
|
|
121176
121303
|
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
121177
121304
|
void 0
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -15,7 +15,9 @@ allowed-tools: Bash(siluzan-tso:*) Read Write
|
|
|
15
15
|
|
|
16
16
|
<!-- 注入到 SKILL.md.tmpl 的 {{AGENT_PREAMBLE}};构建时由 gen-skill-docs.mjs 合并 -->
|
|
17
17
|
|
|
18
|
-
> **Agent 纪律(每个新任务必读)**:Read `references/core/agent-conventions.md`,再按 `SKILL.md` 任务表 Read 域文档。禁止跨话题复用参数记忆;数据类任务一律 `--json-out` +
|
|
18
|
+
> **Agent 纪律(每个新任务必读)**:Read `references/core/agent-conventions.md`,再按 `SKILL.md` 任务表 Read 域文档。禁止跨话题复用参数记忆;数据类任务一律 `--json-out` + **仅用代码**读落盘 JSON(见 `references/core/tips.md`)——**禁止**用 Read 工具打开 `writtenFiles` 里的完整 `*.json`。
|
|
19
|
+
>
|
|
20
|
+
> **报告/Excel 交付前**:Read `references/core/deliverable-preflight.md`,运行 `node scripts/check-deliverable-snap.mjs <snapDir>`;币种只认 `currencyCode`,禁止默认 Google=美金。
|
|
19
21
|
>
|
|
20
22
|
> **开户**:首次进入开户话题须先向用户罗列该媒体(或未指明媒体时六平台)**全部必填项**,见 `references/accounts/open-account-by-media.md` §「首次响应硬规范」。
|
|
21
23
|
>
|
|
@@ -70,7 +72,7 @@ Windows:部分 Agent 通过 PowerShell 代执行时可能失败,改在 [Git
|
|
|
70
72
|
宿主具备 **Task / subagent**(或内置 Bash/Explore)时:
|
|
71
73
|
|
|
72
74
|
1. **复杂报告或批处理**(P5 / P6 OKKI / P7 询盘)、预计 CLI 输出很长 → 先 Read `references/core/subagent-orchestration.md`,按**决策矩阵**选择主会话或子会话。
|
|
73
|
-
2. Handoff 模板在 skill 根目录 `snippets/handoff-p{5,6,7}-*.md`;子会话只回传 manifest/exitCode
|
|
75
|
+
2. Handoff 模板在 skill 根目录 `snippets/handoff-p{5,6,7}-*.md`;子会话只回传 manifest/exitCode 摘要,**禁止** Read 落盘业务 JSON、**禁止**把 MB 级 JSON 贴回主对话(须脚本读盘后只回传脚本输出)。
|
|
74
76
|
3. **无 subagent 能力** → 跳过本节,按 `core/playbooks.md` 在主会话执行。
|
|
75
77
|
|
|
76
78
|
写操作、`--commit`、401/batch `resume` 策略由**主 Agent**负责,不默认下放子会话。
|
|
@@ -83,7 +85,7 @@ Windows:部分 Agent 通过 PowerShell 代执行时可能失败,改在 [Git
|
|
|
83
85
|
| ------------------ | ----------------------------- |
|
|
84
86
|
| 任意任务(首次) | `core/agent-conventions.md` |
|
|
85
87
|
| 账户列表 / 余额 / 消耗 / 分享 / MCC / 多账户汇总 | `accounts/accounts.md`;金额加 `accounts/currency.md` |
|
|
86
|
-
| 拉数 / 报告 / 周报 / `google-analysis` | `analytics/account-analytics.md` + `core/tips.md
|
|
88
|
+
| 拉数 / 报告 / 周报 / `google-analysis` | `analytics/account-analytics.md` + `core/tips.md` + **`core/deliverable-preflight.md`**;多账户加 `analytics/google-analysis-batch.md` |
|
|
87
89
|
| Meta/Facebook 周期或诊断报告 / `facebook-analysis` | `analytics/facebook-analysis-guide.md` + `report-templates/meta-period-report.md`(或 `meta-account-diagnosis-report.md`)+ `core/playbooks.md` P4-FB |
|
|
88
90
|
| 网站诊断 / 落地页评分 / `website-diagnosis` | `analytics/website-diagnosis-guide.md` + `assets/website-diagnosis-rules.md` + `report-templates/website-diagnosis-report.md`(**终稿 HTML**)+ `core/playbooks.md` P8 |
|
|
89
91
|
| Google 新建搜索系列 | `google-ads/google-ads-campaign-plan.md` |
|
package/dist/skill/_meta.json
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
| ---- | ---- |
|
|
9
9
|
| `core/setup.md` | 安装、登录(手机验证码优先)、配置、更新 |
|
|
10
10
|
| `core/agent-conventions.md` | **Agent 必读**:加载纪律、硬规范、数据转换、时间/币种/批量 |
|
|
11
|
-
| `core/tips.md` | `--json-out` 协议、outline
|
|
11
|
+
| `core/tips.md` | `--json-out` 协议、outline→脚本读 JSON |
|
|
12
|
+
| `core/deliverable-preflight.md` | **交付前自检**:币种 + manifest 完整性;`scripts/check-deliverable-snap.mjs` |
|
|
12
13
|
| `core/playbooks.md` | P1–P7 高频任务标准动作 |
|
|
13
14
|
| `core/workflows.md` | 多命令串联业务流程 |
|
|
14
15
|
| `core/skill-authoring.md` | Skill 文档维护最佳实践(维护者) |
|
|
@@ -517,7 +517,7 @@ siluzan-tso open-account yandex \
|
|
|
517
517
|
### Bing/BingV2 开户(需要营业执照图片)
|
|
518
518
|
|
|
519
519
|
```bash
|
|
520
|
-
# 前置:查询 Bing
|
|
520
|
+
# 前置:查询 Bing 行业(--trade-id 传 bing-industries 输出的 id,与网页下拉 value 一致)
|
|
521
521
|
siluzan-tso open-account bing-industries --keyword "科技"
|
|
522
522
|
|
|
523
523
|
siluzan-tso open-account bing \
|
|
@@ -530,7 +530,7 @@ siluzan-tso open-account bing \
|
|
|
530
530
|
--address "南山区科技园XX路XX号XX大厦" \
|
|
531
531
|
--postcode "518000" \
|
|
532
532
|
--promotion-link "https://www.brand-a.com" \
|
|
533
|
-
--trade-id "
|
|
533
|
+
--trade-id "<bing-industries 输出的 id>" \
|
|
534
534
|
--license-file "/path/to/license.jpg"
|
|
535
535
|
```
|
|
536
536
|
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
| 城市 | `--city` | |
|
|
85
85
|
| 详细地址 | `--address` | |
|
|
86
86
|
| 邮编 | `--postcode` | |
|
|
87
|
-
| 行业 | `--trade-id` | `bing-industries
|
|
87
|
+
| 行业 | `--trade-id` | 先 `bing-industries`;传输出 **id**(与网页下拉 value 一致),勿猜中文名 |
|
|
88
88
|
| 推广链接 | `--promotion-link` | |
|
|
89
89
|
| 执照图片 | `--license-file` | JPG/PNG/PDF 本地路径 |
|
|
90
90
|
|
|
@@ -199,6 +199,8 @@ siluzan-tso account-history -m Yandex
|
|
|
199
199
|
|
|
200
200
|
**必填**:见上表 § BingV2。
|
|
201
201
|
|
|
202
|
+
> **行业(Agent 硬规范)**:与网页一致,数据源为 `TradeList` + `BingTradeList/Read` 合并后的**二级**选项。必须先执行 `open-account bing-industries`(`--keyword` 可匹配中文行业名、分组名、英文 name 或 id),从输出取 **`id`** 填入 `--trade-id`。**禁止**凭记忆写行业名或编造「一级/二级」拼接字符串。
|
|
203
|
+
|
|
202
204
|
```bash
|
|
203
205
|
siluzan-tso open-account bing-industries --keyword "科技"
|
|
204
206
|
|
|
@@ -212,7 +214,7 @@ siluzan-tso open-account bing \
|
|
|
212
214
|
--address "南山区科技园XX路XX号" \
|
|
213
215
|
--postcode "518000" \
|
|
214
216
|
--promotion-link "https://www.brand-a.com" \
|
|
215
|
-
--trade-id "
|
|
217
|
+
--trade-id "<bing-industries 输出的 id>" \
|
|
216
218
|
--license-file "/path/to/license.jpg"
|
|
217
219
|
|
|
218
220
|
siluzan-tso account-history -m BingV2
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
1. **先确认统计区间**:用户未给明确起止日期时**必须反问**(参见 SKILL.md 时间范围强制反问)。
|
|
33
33
|
2. 确定报告维度(默认含:执行摘要、每日趋势、月度汇总、系列表现、设备分布、地域分布、关键词表现、优化建议),详见 `report-templates/README.md`。若用户使用 **「使用 okki 周报模板…」「OKKI 周报」** 等固定话术发客户,改走 **`report-templates/okki-weekly-google-client.md`**(与 SKILL **P6** 一致),不再按 `google-period-report.md` 的默认 8 维追问。
|
|
34
34
|
3. **拉数**:使用 `google-analysis … --json-out <dir>`(Google)或对应 `report <media>-*` 命令落盘。
|
|
35
|
-
4. **编写并执行代码**从磁盘读取 `manifest-<accountId>.json` 与各 `<section>-<accountId>.json`
|
|
36
|
-
-
|
|
35
|
+
4. **编写并执行代码**从磁盘读取 `manifest-<accountId>.json` 与各 `<section>-<accountId>.json` 来完成筛选、聚合、排序等计算;**禁止**用宿主 `Read` 打开业务 `*.json`;**禁止**在对话里心算或手填报告数字。
|
|
36
|
+
- **写脚本前**用 Read 仅打开 `<section>-<accountId>.outline.txt`(或 `node -e` 读最后一行类型字面量)了解字段结构;**业务 JSON 只许在脚本里** `require` / `readFileSync`,脚本 `console.log` 打印所需汇总。
|
|
37
37
|
- 真实数据始终从 `<section>-<accountId>.json` 通过代码获取,**不要**把 outline 当作业务数据贴给用户。
|
|
38
38
|
5. **由代码写出最终文件**(HTML/Excel/PDF/PPT/Markdown/word 等)。**禁止**在报告脚本中以源码字面量写死应从 JSON 读取的业务数据(消耗金额、系列名、日期区间等)。允许的常量仅限:快照目录路径、JSON 字段键名、版式/结构占位。
|
|
39
39
|
6. **报告首行**须标注:`统计区间:YYYY-MM-DD ~ YYYY-MM-DD(货币:XXX)`
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
|
|
25
25
|
- **禁止**「上次已经读过 `accounts/accounts.md` / `google-ads/google-ads.md`,本任务直接 `siluzan-tso …`」
|
|
26
26
|
- **禁止**用对话记忆中的示例 ID、金额、命令 flags 代替当次 Read 文档
|
|
27
|
-
- **禁止**跳过 `references/core/tips.md` 里的 outline
|
|
27
|
+
- **禁止**跳过 `references/core/tips.md` 里的 outline→脚本读 JSON 顺序
|
|
28
|
+
- **禁止**用 Read / `cat` / `type` 打开 `--json-out` 落盘的**业务数据** `*.json`(`campaigns-*.json`、`list-accounts-*.json` 等);只允许 Read 体积小的 `*.outline.txt` 与 `references/` 文档
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
2. 涉及写入/修改/删除的操作必须与用户确认
|
|
37
38
|
3. 按计划执行,说明每步意图
|
|
38
39
|
4. 用成对的读命令复核写入结果;异步任务每 5s 轮询直到完成
|
|
39
|
-
5.
|
|
40
|
+
5. **交付前**(报告/Excel/含金额话术):Read `references/core/deliverable-preflight.md`,运行 `node scripts/check-deliverable-snap.mjs`;币种须来自 `list-accounts` / `overview` 的 `currencyCode`
|
|
41
|
+
6. 全部完成后预测用户下一步操作
|
|
40
42
|
|
|
41
43
|
### 执行模式速查
|
|
42
44
|
|
|
@@ -77,15 +79,27 @@
|
|
|
77
79
|
|
|
78
80
|
本 Skill 下**所有**业务数据均以 CLI `--json-out`(或用户提供的同构 JSON)落盘为唯一真相源。Agent **不得**在对话或脚本里「代替」磁盘数据。
|
|
79
81
|
|
|
82
|
+
### 禁止 Read 完整业务 JSON(最高优先级)
|
|
83
|
+
|
|
84
|
+
`--json-out` 落盘的 `writtenFiles[0]`(及同目录 `campaigns-*.json`、`keywords-*.json` 等)往往为 **MB 级**。Agent **永远不得**用宿主 **Read** 工具、`cat`、`type`、`Get-Content` 等方式把整文件读进对话上下文。
|
|
85
|
+
|
|
86
|
+
| 允许 Read 的文件 | 必须用代码读取的文件 |
|
|
87
|
+
| ---------------- | -------------------- |
|
|
88
|
+
| `references/**/*.md`、`assets/**/*.md`(Skill 文档) | 所有 `--json-out` 业务 `*.json` |
|
|
89
|
+
| 当次 `*.outline.txt`(通常小于 2KB,仅 schema) | `cli-manifest*.json` / `manifest-*.json` 中的路径索引(应用脚本 `JSON.parse` 读,勿 Read 整份 manifest 若很大) |
|
|
90
|
+
| stdout **一行**摘要 JSON(命令回显,体积小) | 用户提供的同构大 JSON 文件 |
|
|
91
|
+
|
|
92
|
+
**正确流程**:解析 stdout 摘要 → Read **仅** `outlineFile` 了解字段类型 → **编写并执行** `node -e` / `node script.mjs` / `python`:`require` 或 `readFileSync` 读 JSON → `console.log` / 写 xlsx/html **只输出所需字段** → 用脚本打印结果回答用户。
|
|
93
|
+
|
|
80
94
|
| 必须 | 禁止 |
|
|
81
95
|
| ---- | ---- |
|
|
82
|
-
| 编写并**执行** Node/Python
|
|
83
|
-
| **先 outline
|
|
96
|
+
| 编写并**执行** Node/Python 脚本读落盘 JSON → 计算 → 写出 | 用 **Read** 打开 `writtenFiles` 业务 JSON;在回复里手填、改数、心算汇总 |
|
|
97
|
+
| **先 outline 后脚本读 JSON**:`outlineFile` → 代码读 `writtenFiles[0]` | 跳过 outline 猜字段名;把 outline 当数据贴给用户 |
|
|
84
98
|
| 字段路径以 `outlineFile` + 当次 `writtenFiles` / manifest 为准 | 把国家名、ID、金额、词表等**业务值**写成源码字面量 |
|
|
85
99
|
| 映射表 / 模板契约在运行时加载(`analytics/geo-continents.json`、`campaign-create-template.json`) | 复制配置条目进源码常量 |
|
|
86
100
|
| 脚本输出与落盘 JSON 可复核 | 用「上次对话记住的数」「占位数」写入交付物 |
|
|
87
101
|
|
|
88
|
-
**允许的字面量**:输出目录、Sheet/列标题、技术格式、`focusCountries` 等**用户当轮明确给出**的配置(建议落盘为 `config.json`
|
|
102
|
+
**允许的字面量**:输出目录、Sheet/列标题、技术格式、`focusCountries` 等**用户当轮明确给出**的配置(建议落盘为 `config.json` 再**用脚本**读)。
|
|
89
103
|
|
|
90
104
|
完整协议见 `references/core/tips.md` § 处理顺序、§ 已有 JSON 时。
|
|
91
105
|
|
|
@@ -122,6 +136,7 @@
|
|
|
122
136
|
- **符号**:`CNY` → **¥**;`USD` → **$**。多账户按 `currencyCode` 分表,**禁止**跨币种求和。
|
|
123
137
|
- **金额单位统一为「元」**:报告保留 2 位小数。
|
|
124
138
|
- **Google 广告诊断报告**:`report-templates/google-ads-diagnosis.md` § 撰写硬约束 — 每日趋势金额/CPA 必须 2 位小数;每个模块除表格外须有「分析」+「建议」,禁止只贴数据。
|
|
139
|
+
- **交付前自检**:报告/Excel 交付前运行 `scripts/check-deliverable-snap.mjs`(见 `deliverable-preflight.md`);自检失败禁止交付。
|
|
125
140
|
- **品牌名优先级**:(1) 用户明确提供 → (2) `list-accounts.mag.advertiserName` → (3) 网址域名占位 `[待确认品牌名]`。**严禁**把英文域名翻译为虚构中文品牌。
|
|
126
141
|
|
|
127
142
|
---
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# 交付前自检(币种 + 数据完整)
|
|
2
|
+
|
|
3
|
+
> 用户反馈 Agent 产出物常见两类问题:**币种错误**(Google 默认美金、符号混用)、**报告缺章/缺数**(少拉维度、Read JSON 截断后手填)。本节给出**可执行**防线。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 根因(简要)
|
|
8
|
+
|
|
9
|
+
| 问题 | 典型原因 |
|
|
10
|
+
| ---- | -------- |
|
|
11
|
+
| 币种错误 | 未先 `list-accounts` 读 `currencyCode`;报告脚本写死 `USD`;CNY 用 `$` |
|
|
12
|
+
| 数据缺失 | 未按模板拉全 `--sections`;未读 `manifest` 就写报告;Read 大 JSON 截断后编造;多账户漏 ID |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 强制流程(报告/Excel 交付前)
|
|
17
|
+
|
|
18
|
+
与 `references/core/agent-conventions.md`「计划 → 确认 → 执行 → **验证**」一致,**交付前增加验证步**:
|
|
19
|
+
|
|
20
|
+
### 1. 币种(第一步就做)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
siluzan-tso list-accounts -m Google -k <mediaCustomerId> --json-out ./snap
|
|
24
|
+
node -e "
|
|
25
|
+
const d=require('./snap/list-accounts-google.json');
|
|
26
|
+
const ma=(d.items||[]).map(x=>x.ma||x).find(m=>String(m.mediaCustomerId)===process.argv[1]);
|
|
27
|
+
if(!ma){console.error('账户未找到');process.exit(1);}
|
|
28
|
+
console.log(ma.currencyCode, ma.mediaCustomerName);
|
|
29
|
+
" <mediaCustomerId>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- 将输出的 **`currencyCode`** 写入报告生成脚本(从 JSON 读,勿写死)。
|
|
33
|
+
- 首行必须有:`统计区间:YYYY-MM-DD ~ YYYY-MM-DD(货币:CNY|USD)`。
|
|
34
|
+
- 符号:**CNY → ¥**,**USD → $**。详见 `references/accounts/currency.md`。
|
|
35
|
+
|
|
36
|
+
### 2. 拉数完整(对照模板 + manifest)
|
|
37
|
+
|
|
38
|
+
- 周期报告:按 `report-templates/google-period-report.md` 默认 7 维(或用户追加维)一次 `google-analysis --sections … --json-out`。
|
|
39
|
+
- 拉数后打开 **`manifest-<accountId>.json`**(用脚本 `JSON.parse`,勿 Read 整份若很大),核对 `artifacts[].section` 与模板一致。
|
|
40
|
+
|
|
41
|
+
### 3. 写报告只用脚本读 JSON
|
|
42
|
+
|
|
43
|
+
见 `references/core/tips.md`:**禁止** Read 业务 `*.json`;用 `node`/Python 聚合后写 HTML/xlsx。
|
|
44
|
+
|
|
45
|
+
### 4. 交付前运行自检脚本(必做)
|
|
46
|
+
|
|
47
|
+
Skill 自带脚本(安装后在 skill 根目录):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node scripts/check-deliverable-snap.mjs ./snap --preset p4-google
|
|
51
|
+
# 或自定义:--required overview,campaigns,geographic
|
|
52
|
+
# 已知账户为 CNY 时:--expect-currency CNY
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| 参数 | 说明 |
|
|
56
|
+
| ---- | ---- |
|
|
57
|
+
| `<snapDir>` | `--json-out` 目录 |
|
|
58
|
+
| `--preset p4-google` | 对齐 Google 周期报告默认 7 维 |
|
|
59
|
+
| `--preset p1-google` | 单账户画像:list-accounts + stats + ad-campaigns |
|
|
60
|
+
| `--required a,b` | 额外必填 section(逗号分隔,前缀匹配) |
|
|
61
|
+
| `--expect-currency CNY\|USD` | 与 `list-accounts` / `overview` 中 `currencyCode` 对照 |
|
|
62
|
+
|
|
63
|
+
**退出码 1** → 补拉缺失维度或修币种后再交付。**禁止**在自检失败时仍把报告发给用户。
|
|
64
|
+
|
|
65
|
+
### 5. 报告正文抽检(脚本输出对照)
|
|
66
|
+
|
|
67
|
+
交付前用脚本打印 3 个汇总(示例):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
node -e "
|
|
71
|
+
const o=require('./snap/overview-<id>_<start>-<end>.json');
|
|
72
|
+
const r=o.record||{};
|
|
73
|
+
const p=r.currentPeriod||{};
|
|
74
|
+
console.log('spend',p.spend,'clicks',p.clicks,'currency',r.currencyCode);
|
|
75
|
+
"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
报告中的总消耗、币种须与上述一致(允许四舍五入,**不允许数量级或币种错误**)。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Playbook 挂钩
|
|
83
|
+
|
|
84
|
+
| Playbook | 自检命令 |
|
|
85
|
+
| -------- | -------- |
|
|
86
|
+
| P1 | `--preset p1-google` + `--expect-currency`(来自 list-accounts) |
|
|
87
|
+
| P4 | `--preset p4-google` |
|
|
88
|
+
| P6 / P7 | 按 `okki-weekly-google-client.md` / `google-inquiry-analysis.md` 自建 `--required`(含各月 `campaigns`、`geographic` 等) |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 长期改进(可选)
|
|
93
|
+
|
|
94
|
+
| 手段 | 说明 |
|
|
95
|
+
| ---- | ---- |
|
|
96
|
+
| **skill-eval 场景** | 断言 Agent 先 `list-accounts` 且报告含 `(货币:XXX)` |
|
|
97
|
+
| **CLI `snap check`** | 将本脚本收编为 `siluzan-tso snap check`(待开发) |
|
|
98
|
+
| **报告模板内嵌币种占位** | 脚本写 `{currencyCode}` 而非手写符号 |
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 相关文档
|
|
103
|
+
|
|
104
|
+
- `references/accounts/currency.md`
|
|
105
|
+
- `report-templates/REPORT-WORKFLOW.md` § 步骤 4–5
|
|
106
|
+
- `references/core/tips.md`
|
|
107
|
+
- `references/core/playbooks.md`
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
| 编号 | 场景 | 必读 references |
|
|
6
6
|
| ---- | ---- | --------------- |
|
|
7
|
-
| P1 | 单账户投放画像 | `analytics/account-analytics.md`、`accounts/currency.md`、`core/tips.md` |
|
|
7
|
+
| P1 | 单账户投放画像 | `analytics/account-analytics.md`、`accounts/currency.md`、`core/tips.md`、`core/deliverable-preflight.md` |
|
|
8
8
|
| P2 | 多账户余额扫描 | `accounts/accounts.md`(`balance-scan`)、`accounts/currency.md` |
|
|
9
9
|
| P3 | 多账户投放画像汇总 | `accounts/accounts.md` § accounts-digest、`accounts/currency.md`、`core/tips.md` |
|
|
10
|
-
| P4 | Google 账户周期报告 | `report-templates/google-period-report.md`、`analytics/account-analytics.md`、`core/tips.md` |
|
|
10
|
+
| P4 | Google 账户周期报告 | `report-templates/google-period-report.md`、`analytics/account-analytics.md`、`core/tips.md`、`core/deliverable-preflight.md` |
|
|
11
11
|
| P4-FB | Meta/Facebook 周期报告 | `report-templates/meta-period-report.md`、`analytics/facebook-analysis-guide.md`、`analytics/account-analytics.md`、`core/tips.md` |
|
|
12
12
|
| P5 | 多账户多维度报告 | `analytics/google-analysis-batch.md`、`analytics/account-analytics.md`、`core/tips.md`;可选 `core/subagent-orchestration.md` |
|
|
13
13
|
| P6 | OKKI 周报 | `report-templates/okki-weekly-google-client.md`(全文)、`analytics/account-analytics.md`、`core/tips.md`;可选 `core/subagent-orchestration.md` |
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
4. `ad campaigns -a <mediaCustomerId> --start <S> --end <D> --json-out ./snap-p1`
|
|
25
25
|
5. 诊断/画像报告:Read `report-templates/google-ads-diagnosis.md`(**硬约束:每日趋势 2 位小数、每模块必有分析**)+ `google-account-diagnosis-report.md`(章节与 CLI);`google-analysis --json-out` 须含 `daily-metrics`(按日趋势)。
|
|
26
26
|
6. 首行标注统计区间和货币;HTML 可对齐 `report-template.html` + `section-*` 区块。
|
|
27
|
+
7. 交付前:`node scripts/check-deliverable-snap.mjs ./snap-p1 --preset p1-google --expect-currency <来自 list-accounts>`。
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -66,6 +67,7 @@ siluzan-tso accounts-digest -m Google -a id1,id2,... --start <S> --end <D> --jso
|
|
|
66
67
|
1. 确认时间范围;区间 > 3 个月时分段(季度/月)。
|
|
67
68
|
2. 按 `google-period-report.md` 默认维度执行 `google-analysis --json-out <dir>`。
|
|
68
69
|
3. 报告须含:账户概览、投放趋势、Top 关键词/系列/地区分布、优化建议。
|
|
70
|
+
4. 交付前:`node scripts/check-deliverable-snap.mjs <dir> --preset p4-google --expect-currency <currencyCode>`。
|
|
69
71
|
|
|
70
72
|
---
|
|
71
73
|
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
| `mediaCustomerId` | 来自当次 `list-accounts`,禁止猜 |
|
|
59
59
|
| `dateRange` | `start` / `end`(YYYY-MM-DD);P7 另附 `m1`/`m2`/`m3` |
|
|
60
60
|
| `commands[]` | 可复制 bash 块(来自 playbook 或 report-template) |
|
|
61
|
-
| `forbidden[]` | 至少包含:禁止 Read
|
|
61
|
+
| `forbidden[]` | 至少包含:禁止 Read/cat 打开落盘业务 `*.json`(须 node/python 脚本读盘);禁止把 JSON 贴进回复;禁止编造 ID/金额;禁止 batch 重新 `run`(仅 `resume`) |
|
|
62
62
|
| `returnSchema` | 子会话**只**回传下列内容 |
|
|
63
63
|
|
|
64
64
|
### returnSchema(子会话结束时的回复格式)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **文档重读(与落盘无关,但同样硬)**:**每个新的用户任务**或**同对话内换话题**时,须按 `references/core/agent-conventions.md` § 文档加载纪律 **重新 Read** 当次任务对应的 `references/*.md` / `report-templates/*.md`,再执行 CLI。
|
|
4
4
|
|
|
5
|
-
> **AI 助手(本 Skill
|
|
5
|
+
> **AI 助手(本 Skill)**:凡需结构化数据,一律 **`--json-out <路径>`** 落盘,再用 **Node/Python 脚本**(`require` / `readFileSync`)读 JSON 做筛选与聚合。**禁止**用宿主 **Read** 工具打开落盘业务 `*.json`(会占满上下文且易截断)。命令成功时 **stdout 仅一行摘要 JSON**(体积小);**`outlineFile`**(`*.outline.txt`,通常小于 2KB)是唯一允许用 Read 查看的「数据结构」文件——最后一行是 TS 类型字面量。业务数据**只**通过脚本访问,脚本 stdout 打印你需要的行/汇总即可。
|
|
6
6
|
>
|
|
7
7
|
> **文件命名规则(重要)**:当一条命令具备明确的"查询 id"(mediaCustomerId / entityId / ruleId / auditId 等)时,目录模式落盘的 `*.json`、`*.outline.txt` 与对应 manifest **都会带上 `-<查询id>` 后缀**:
|
|
8
8
|
>
|
|
@@ -21,23 +21,26 @@
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
## 处理顺序(**Agent
|
|
24
|
+
## 处理顺序(**Agent 必读**):摘要 → outline(schema)→ **脚本**读 JSON
|
|
25
25
|
|
|
26
26
|
每条 `--json-out` 命令成功后,**必须**按以下顺序处理,不要跳步、不要把 outline 当数据:
|
|
27
27
|
|
|
28
|
-
1.
|
|
29
|
-
2.
|
|
28
|
+
1. **解析 stdout 一行摘要 JSON**(禁止 Read 打开 `writtenFiles[0]` 的 `.json`):拿到 `outlineFile`、`writtenFiles[0]`、`manifestFile`、`agentHint`。**不要硬编码 `<section>.json` 文件名**。
|
|
29
|
+
2. **了解字段结构**(二选一,推荐 A):
|
|
30
|
+
- **A**:宿主 **Read** 仅 `outlineFile`(`*.outline.txt`,体积小);或
|
|
31
|
+
- **B**:`node -e "const fs=require('fs'); const o=fs.readFileSync(process.argv[1],'utf8').trimEnd().split('\\n').filter(l=>!l.startsWith('//')); console.log(o.pop())" <outlineFile>`
|
|
30
32
|
- 第 1 行(注释):`// outline of \`<xxx>.json\` — schema-only, NOT the data.` —— **明确告诉你它是"数据结构描述文件"**,不要把它当业务数据使用。
|
|
31
33
|
- 第 2 行(注释):中文用法说明。
|
|
32
34
|
- 第 3 行(注释):类型推断口径(数组取前 8 项去重并集;环或重复对象记为 any)。
|
|
33
35
|
- 其后**可能**还有若干行 `//` 字段提示(例如 `google-analysis` 的 **`campaigns-*.outline.txt`** 会插入金额字段已统一为元的说明)。
|
|
34
36
|
- **类型字面量**:**最后一个不以 `//` 开头的行**(单行 TS,如 `{ a: number; items: { ... }[] }`)—— 写筛选/聚合脚本时取这一行即可。
|
|
35
37
|
- 提取写法:`outlineRaw.trimEnd().split('\n').filter(l => !l.startsWith('//')).pop()`(**不要**用 `lines[lines.length - 1]`:当最后一行是空行时会错)。
|
|
36
|
-
3.
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
38
|
+
3. **必须用 Bash 执行脚本**读 `writtenFiles[0]`(及 manifest 中的路径)做筛选、聚合、计算——脚本内 `require` / `readFileSync`,**禁止**宿主 Read / cat / type 打开该 `.json`。**禁止**:
|
|
39
|
+
- 用 **Read** 读 `campaigns-*.json`、`list-accounts-*.json` 等落盘业务 JSON;
|
|
40
|
+
- 把 outline 当 JSON `require`(它是 `.txt`);
|
|
41
|
+
- 跳过 outline 猜字段名;
|
|
42
|
+
- 把 outline 或整段 JSON 贴进对话当结论。
|
|
43
|
+
4. **交付物**用代码写出(HTML / Excel / PDF / PPT / Markdown 等);向用户展示的数字须来自**脚本 stdout**,不要在对话里手填数。
|
|
41
44
|
|
|
42
45
|
> 这个顺序对所有三类快照统一生效:通用业务命令(`cli-manifest[-<id>].json`)、`google-analysis`(`manifest-<accountId>.json`)、`report …` 分析(`report-manifest[-<accountId>].json`)。三种 summary 都带 `outlineFile`、`agentHint` 字段。
|
|
43
46
|
|
|
@@ -324,8 +327,8 @@ console.log('可用字段:', first && Object.keys(first));
|
|
|
324
327
|
|
|
325
328
|
## AI Agent 使用规范
|
|
326
329
|
|
|
327
|
-
- **优先用 `--json-out` 与清单文件**:同一运行目录、同一"查询 id"的多次拉数会合并到 `cli-manifest-<id>.json` 的 `artifacts[]`;不同账户/实体写入各自的 manifest
|
|
328
|
-
-
|
|
330
|
+
- **优先用 `--json-out` 与清单文件**:同一运行目录、同一"查询 id"的多次拉数会合并到 `cli-manifest-<id>.json` 的 `artifacts[]`;不同账户/实体写入各自的 manifest,互不覆盖。读具体数据**只**在脚本里 `require(writtenFiles[0])`,**禁止 Read 工具**打开该 json。
|
|
331
|
+
- **中间结果一律落盘**:跨步骤数据不要靠对话记忆,写到工作目录并在后续 **`node`/`python` 脚本**中读盘。
|
|
329
332
|
- **Windows 上避免直接管道 JSON**:PowerShell 管道可能改变编码;优先 `--json-out` + `node -e` 读文件。
|
|
330
333
|
- **用 `process.stdout.write` 而不是 `console.log` 提取单个值**:前者不带换行符,方便直接赋给 shell 变量。
|
|
331
334
|
- **节点代码复杂时拆分写法**:不要写超过 10 行的 `node -e` 单行,改用 `node -e "$(cat <<'EOF'\n...\nEOF\n)"` 或多行 `.mjs` 脚本文件。
|
|
@@ -135,7 +135,7 @@ siluzan-tso account-history -m Yandex
|
|
|
135
135
|
**必填清单**:见 `open-account-by-media.md` § BingV2(含 `--pattern`、`--name-remark-list`、`--postcode`)。
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
|
-
#
|
|
138
|
+
# 前置:查询行业(将 bing-industries 输出的 id 传给 --trade-id,勿猜行业名)
|
|
139
139
|
siluzan-tso open-account bing-industries --keyword "科技"
|
|
140
140
|
|
|
141
141
|
# 提交开户(CLI 自动按公司名创建/关联广告主组,无需 magKey)
|
|
@@ -149,7 +149,7 @@ siluzan-tso open-account bing \
|
|
|
149
149
|
--address "南山区科技园XX路XX号XX大厦" \
|
|
150
150
|
--postcode "518000" \
|
|
151
151
|
--promotion-link "https://www.brand-a.com" \
|
|
152
|
-
--trade-id "
|
|
152
|
+
--trade-id "<bing-industries 输出的 id>" \
|
|
153
153
|
--license-file "/path/to/license.jpg"
|
|
154
154
|
|
|
155
155
|
# 轮询审核进度
|
|
@@ -51,6 +51,14 @@
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
+
### 步骤 4b:交付前自检(**必做**)
|
|
55
|
+
|
|
56
|
+
- Read `references/core/deliverable-preflight.md`。
|
|
57
|
+
- 运行 `node scripts/check-deliverable-snap.mjs <snapDir> --preset p4-google`(或按模板 `--required` / `--expect-currency`)。
|
|
58
|
+
- 退出码非 0:**补拉数据或修正币种**,不得交付。
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
54
62
|
### 步骤 5:撰写报告
|
|
55
63
|
|
|
56
64
|
- 按 `*.md` 章节结构组织内容。
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 交付前快照自检(币种 + 缺文件/空数据)
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* node scripts/check-deliverable-snap.mjs <snapDir> [--preset p4-google] [--required a,b]
|
|
7
|
+
* node scripts/check-deliverable-snap.mjs ./snap --expect-currency CNY
|
|
8
|
+
*
|
|
9
|
+
* 退出码:0 通过;1 有 error;2 用法错误
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
|
|
14
|
+
const PRESETS = {
|
|
15
|
+
"p4-google": [
|
|
16
|
+
"overview",
|
|
17
|
+
"daily-metrics",
|
|
18
|
+
"dimension-summary",
|
|
19
|
+
"campaigns",
|
|
20
|
+
"devices",
|
|
21
|
+
"geographic",
|
|
22
|
+
"keywords",
|
|
23
|
+
],
|
|
24
|
+
"p1-google": ["list-accounts", "stats", "ad-campaigns"],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function parseArgs(argv) {
|
|
28
|
+
const snapDir = argv[2];
|
|
29
|
+
if (!snapDir) return { error: "缺少 snapDir" };
|
|
30
|
+
let preset;
|
|
31
|
+
let required = [];
|
|
32
|
+
let expectCurrency;
|
|
33
|
+
for (let i = 3; i < argv.length; i++) {
|
|
34
|
+
if (argv[i] === "--preset" && argv[i + 1]) {
|
|
35
|
+
preset = argv[++i];
|
|
36
|
+
} else if (argv[i] === "--required" && argv[i + 1]) {
|
|
37
|
+
required = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
38
|
+
} else if (argv[i] === "--expect-currency" && argv[i + 1]) {
|
|
39
|
+
expectCurrency = argv[++i].toUpperCase();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (preset) {
|
|
43
|
+
const p = PRESETS[preset];
|
|
44
|
+
if (!p) return { error: `未知 preset:${preset},可选:${Object.keys(PRESETS).join(", ")}` };
|
|
45
|
+
required = [...new Set([...required, ...p])];
|
|
46
|
+
}
|
|
47
|
+
return { snapDir: path.resolve(snapDir), required, expectCurrency };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readJson(filePath) {
|
|
51
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function listManifests(dir) {
|
|
55
|
+
return fs.readdirSync(dir).filter((f) => /^manifest(-.*)?\.json$/.test(f) || /^report-manifest(-.*)?\.json$/.test(f) || /^cli-manifest(-.*)?\.json$/.test(f));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function sectionMatches(artifactSection, required) {
|
|
59
|
+
return required.some((r) => artifactSection === r || artifactSection.startsWith(`${r}-`) || artifactSection.includes(r));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function itemCount(payload) {
|
|
63
|
+
if (!payload || typeof payload !== "object") return null;
|
|
64
|
+
if (Array.isArray(payload.items)) return payload.items.length;
|
|
65
|
+
if (Array.isArray(payload.data?.items)) return payload.data.items.length;
|
|
66
|
+
if (payload.record && typeof payload.record === "object") return 1;
|
|
67
|
+
if (Array.isArray(payload.data)) return payload.data.length;
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function extractCurrencyFromPayload(payload, section) {
|
|
72
|
+
if (!payload || typeof payload !== "object") return null;
|
|
73
|
+
const rec = payload.record ?? payload.data?.record;
|
|
74
|
+
if (rec?.currencyCode) return String(rec.currencyCode);
|
|
75
|
+
const items = payload.items ?? payload.data?.items;
|
|
76
|
+
if (Array.isArray(items) && items.length > 0) {
|
|
77
|
+
const ma = items[0].ma ?? items[0];
|
|
78
|
+
if (ma?.currencyCode) return String(ma.currencyCode);
|
|
79
|
+
if (items[0].currencyCode) return String(items[0].currencyCode);
|
|
80
|
+
}
|
|
81
|
+
if (section.includes("list-accounts") && Array.isArray(items) && items[0]?.ma?.currencyCode) {
|
|
82
|
+
return String(items[0].ma.currencyCode);
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function main() {
|
|
88
|
+
const parsed = parseArgs(process.argv);
|
|
89
|
+
if (parsed.error) {
|
|
90
|
+
console.error(parsed.error);
|
|
91
|
+
process.exit(2);
|
|
92
|
+
}
|
|
93
|
+
const { snapDir, required, expectCurrency } = parsed;
|
|
94
|
+
if (!fs.existsSync(snapDir)) {
|
|
95
|
+
console.error(`目录不存在:${snapDir}`);
|
|
96
|
+
process.exit(2);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const manifests = listManifests(snapDir);
|
|
100
|
+
if (manifests.length === 0) {
|
|
101
|
+
console.error(`未找到 manifest / report-manifest / cli-manifest:${snapDir}`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const errors = [];
|
|
106
|
+
const warnings = [];
|
|
107
|
+
let currencySeen = null;
|
|
108
|
+
|
|
109
|
+
for (const mf of manifests) {
|
|
110
|
+
const man = readJson(path.join(snapDir, mf));
|
|
111
|
+
const arts = man.artifacts ?? [];
|
|
112
|
+
console.log(`\n📋 ${mf}(${arts.length} 个 artifact)`);
|
|
113
|
+
|
|
114
|
+
if (required.length > 0) {
|
|
115
|
+
for (const req of required) {
|
|
116
|
+
const hit = arts.some((a) => sectionMatches(String(a.section ?? ""), [req]));
|
|
117
|
+
if (!hit) errors.push(`[${mf}] 缺少维度/命令:${req}`);
|
|
118
|
+
else console.log(` ✓ 已拉取:${req}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const art of arts) {
|
|
123
|
+
const fp = path.join(snapDir, art.file);
|
|
124
|
+
if (!fs.existsSync(fp)) {
|
|
125
|
+
errors.push(`[${mf}] 文件缺失:${art.file}`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
let payload;
|
|
129
|
+
try {
|
|
130
|
+
payload = readJson(fp);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
errors.push(`[${mf}] JSON 解析失败:${art.file}`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const n = itemCount(payload);
|
|
136
|
+
const sec = String(art.section ?? "");
|
|
137
|
+
if (n === 0) warnings.push(`[${mf}] ${art.file} items 为空(章节可能写「无数据」)`);
|
|
138
|
+
const ccy = extractCurrencyFromPayload(payload, sec);
|
|
139
|
+
if (ccy) {
|
|
140
|
+
if (!currencySeen) currencySeen = ccy;
|
|
141
|
+
else if (currencySeen !== ccy) warnings.push(`[${mf}] 币种不一致:${ccy} vs 先前 ${currencySeen}`);
|
|
142
|
+
if (sec.includes("overview") || sec.includes("list-accounts")) {
|
|
143
|
+
console.log(` 💱 币种来源 ${art.file}:${ccy}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (expectCurrency) {
|
|
150
|
+
if (!currencySeen) warnings.push(`未从快照解析到 currencyCode,无法对照 --expect-currency ${expectCurrency}`);
|
|
151
|
+
else if (currencySeen !== expectCurrency) {
|
|
152
|
+
errors.push(`币种不符:快照为 ${currencySeen},期望 ${expectCurrency}(报告须用 ¥/ $ 与代码一致)`);
|
|
153
|
+
} else console.log(`\n✓ 币种与期望一致:${expectCurrency}`);
|
|
154
|
+
} else if (currencySeen) {
|
|
155
|
+
console.log(`\n💱 建议在报告首行写:统计区间:…(货币:${currencySeen})`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (warnings.length) {
|
|
159
|
+
console.log("\n⚠️ 警告:");
|
|
160
|
+
warnings.forEach((w) => console.log(` - ${w}`));
|
|
161
|
+
}
|
|
162
|
+
if (errors.length) {
|
|
163
|
+
console.log("\n❌ 错误:");
|
|
164
|
+
errors.forEach((e) => console.log(` - ${e}`));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
console.log("\n✅ 交付前自检通过(仍须确认报告正文金额来自脚本、非手填)\n");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main();
|
|
@@ -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.26-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.26-beta.2'
|
|
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.26-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.26-beta.2"
|
|
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"
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
"writtenFiles": ["meta-overview-3995107670753101.json", "meta-overview-3995107670753101.outline.txt"],
|
|
7
7
|
"section": "meta-overview",
|
|
8
8
|
"outlineFile": "meta-overview-3995107670753101.outline.txt",
|
|
9
|
-
"agentHint": "Read outline
|
|
9
|
+
"agentHint": "禁止 Read 业务 *.json;先 outline schema,再用脚本读 JSON。"
|
|
10
10
|
}
|