siluzan-cso-cli 1.1.27-beta.3 → 1.1.27-beta.5
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 +242 -21
- package/dist/skill/SKILL.md +22 -20
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/validate-content.md +114 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/dist/skill/three-lib-content-workflow/content-writer.workflow.md +9 -16
- package/dist/skill/three-lib-content-workflow/packs//347/237/255/350/247/206/351/242/221/345/217/243/346/222/255/overview.md +4 -4
- package/dist/skill/three-lib-content-workflow/packs//347/237/255/350/247/206/351/242/221/345/217/243/346/222/255//345/217/243/346/222/255-sop.md +16 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ siluzan-cso init -d /path/to/skills # 写入自定义目录
|
|
|
54
54
|
siluzan-cso init --force # 强制覆盖已存在文件
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
> **注意**:当前为测试版(1.1.27-beta.
|
|
57
|
+
> **注意**:当前为测试版(1.1.27-beta.5),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
|
|
58
58
|
|
|
59
59
|
| 助手 | 建议 `--ai` |
|
|
60
60
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -5091,6 +5091,202 @@ async function runUpload(options) {
|
|
|
5091
5091
|
}
|
|
5092
5092
|
}
|
|
5093
5093
|
|
|
5094
|
+
// src/commands/validate-content.ts
|
|
5095
|
+
import * as fs9 from "fs";
|
|
5096
|
+
import * as path9 from "path";
|
|
5097
|
+
var CJK_REGEX = /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/g;
|
|
5098
|
+
var WORD_REGEX = /[A-Za-z0-9]+(?:['’\-][A-Za-z0-9]+)*/g;
|
|
5099
|
+
function countMetrics(text) {
|
|
5100
|
+
const chars = [...text].length;
|
|
5101
|
+
const charsNoSpace = [...text.replace(/\s+/g, "")].length;
|
|
5102
|
+
const cjk = (text.match(CJK_REGEX) ?? []).length;
|
|
5103
|
+
const words = (text.match(WORD_REGEX) ?? []).length;
|
|
5104
|
+
const lines = text.length === 0 ? 0 : text.split(/\r?\n/).length;
|
|
5105
|
+
const paragraphs = text.split(/\n\s*\n/).map((p) => p.trim()).filter((p) => p.length > 0).length;
|
|
5106
|
+
return { chars, charsNoSpace, cjk, words, lines, paragraphs };
|
|
5107
|
+
}
|
|
5108
|
+
function pickCount(metrics, countBy) {
|
|
5109
|
+
switch (countBy) {
|
|
5110
|
+
case "chars":
|
|
5111
|
+
return metrics.chars;
|
|
5112
|
+
case "cjk":
|
|
5113
|
+
return metrics.cjk;
|
|
5114
|
+
case "words":
|
|
5115
|
+
return metrics.words;
|
|
5116
|
+
case "chars-no-space":
|
|
5117
|
+
default:
|
|
5118
|
+
return metrics.charsNoSpace;
|
|
5119
|
+
}
|
|
5120
|
+
}
|
|
5121
|
+
function stripMarkdown(input) {
|
|
5122
|
+
let text = input;
|
|
5123
|
+
text = text.replace(/^\uFEFF?---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
5124
|
+
text = text.replace(/^[ \t]*(```|~~~).*$/gm, "");
|
|
5125
|
+
text = text.replace(/!\[[^\]]*\]\([^)]*\)/g, "");
|
|
5126
|
+
text = text.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
5127
|
+
text = text.replace(/^[ \t]*#{1,6}[ \t]+/gm, "");
|
|
5128
|
+
text = text.replace(/^[ \t]*>[ \t]?/gm, "");
|
|
5129
|
+
text = text.replace(/^[ \t]*[-*+][ \t]+/gm, "");
|
|
5130
|
+
text = text.replace(/^[ \t]*\d+\.[ \t]+/gm, "");
|
|
5131
|
+
text = text.replace(/^[ \t]*\|?[ \t]*:?-{3,}.*$/gm, "");
|
|
5132
|
+
text = text.replace(/\|/g, " ");
|
|
5133
|
+
text = text.replace(/(\*\*|__|\*|_|`|~~)/g, "");
|
|
5134
|
+
return text;
|
|
5135
|
+
}
|
|
5136
|
+
var LEAK_RULES = [
|
|
5137
|
+
{ regex: /(?:TF|PA|MF)-?\d{2,}/g, reason: "\u4E09\u5E93\u5185\u90E8\u7F16\u7801\uFF08TF/PA/MF\uFF09\u4E0D\u5E94\u51FA\u73B0\u5728\u6210\u7A3F" },
|
|
5138
|
+
{ regex: /三库溯源/g, reason: "\u4E09\u5E93\u6EAF\u6E90\u5C5E\u4E8E\u8FC7\u7A0B\u8BB0\u5F55\uFF0C\u4E0D\u5E94\u51FA\u73B0\u5728\u6210\u7A3F" },
|
|
5139
|
+
{ regex: /流量因子库|产品资产库|烹调方法库/g, reason: "\u4E09\u5E93\u540D\u79F0\u5C5E\u4E8E\u5185\u90E8\u9AA8\u67B6\uFF0C\u4E0D\u5E94\u51FA\u73B0\u5728\u6210\u7A3F" },
|
|
5140
|
+
{ regex: /SOP\s*(?:执行|步骤|记录)/gi, reason: "SOP \u8FC7\u7A0B\u5185\u5BB9\u4E0D\u5E94\u51FA\u73B0\u5728\u6210\u7A3F" },
|
|
5141
|
+
{ regex: /焊点/g, reason: "\u710A\u70B9\u4E3A\u5185\u90E8\u63D0\u70BC\u672F\u8BED\uFF0C\u4E0D\u5E94\u51FA\u73B0\u5728\u6210\u7A3F" }
|
|
5142
|
+
];
|
|
5143
|
+
function findLeaks(text, extraForbidden = []) {
|
|
5144
|
+
const hits = [];
|
|
5145
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5146
|
+
for (const rule of LEAK_RULES) {
|
|
5147
|
+
const matches = text.match(rule.regex);
|
|
5148
|
+
if (!matches) continue;
|
|
5149
|
+
for (const m of matches) {
|
|
5150
|
+
const key = `${rule.reason}::${m}`;
|
|
5151
|
+
if (seen.has(key)) continue;
|
|
5152
|
+
seen.add(key);
|
|
5153
|
+
hits.push({ match: m, reason: rule.reason });
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
for (const word of extraForbidden) {
|
|
5157
|
+
const w = word.trim();
|
|
5158
|
+
if (!w) continue;
|
|
5159
|
+
if (text.includes(w)) {
|
|
5160
|
+
const key = `\u81EA\u5B9A\u4E49\u7981\u7528\u8BCD::${w}`;
|
|
5161
|
+
if (seen.has(key)) continue;
|
|
5162
|
+
seen.add(key);
|
|
5163
|
+
hits.push({ match: w, reason: "\u547D\u4E2D\u81EA\u5B9A\u4E49\u7981\u7528\u8BCD\uFF08--forbidden\uFF09" });
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
return hits;
|
|
5167
|
+
}
|
|
5168
|
+
function parseForbidden(raw) {
|
|
5169
|
+
if (!raw) return [];
|
|
5170
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
5171
|
+
}
|
|
5172
|
+
var COUNT_BY_LABEL = {
|
|
5173
|
+
chars: "\u603B\u5B57\u7B26\u6570\uFF08\u542B\u7A7A\u767D\uFF09",
|
|
5174
|
+
"chars-no-space": "\u5B57\u7B26\u6570\uFF08\u4E0D\u542B\u7A7A\u767D\uFF09",
|
|
5175
|
+
cjk: "\u6C49\u5B57\u6570",
|
|
5176
|
+
words: "\u82F1\u6587\u5355\u8BCD\u6570"
|
|
5177
|
+
};
|
|
5178
|
+
function readStdin() {
|
|
5179
|
+
try {
|
|
5180
|
+
return fs9.readFileSync(0, "utf8");
|
|
5181
|
+
} catch {
|
|
5182
|
+
return "";
|
|
5183
|
+
}
|
|
5184
|
+
}
|
|
5185
|
+
function resolveContent(options) {
|
|
5186
|
+
if (options.text !== void 0) {
|
|
5187
|
+
return { raw: options.text, sourceLabel: "--text \u6587\u672C" };
|
|
5188
|
+
}
|
|
5189
|
+
if (options.file) {
|
|
5190
|
+
const filePath = path9.resolve(options.file);
|
|
5191
|
+
if (!fs9.existsSync(filePath) || !fs9.statSync(filePath).isFile()) {
|
|
5192
|
+
console.error(`
|
|
5193
|
+
\u274C \u6587\u6848\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${filePath}`);
|
|
5194
|
+
process.exit(1);
|
|
5195
|
+
}
|
|
5196
|
+
try {
|
|
5197
|
+
return { raw: fs9.readFileSync(filePath, "utf8"), sourceLabel: path9.basename(filePath) };
|
|
5198
|
+
} catch (e) {
|
|
5199
|
+
console.error(`
|
|
5200
|
+
\u274C \u8BFB\u53D6\u6587\u4EF6\u5931\u8D25\uFF1A${e.message}`);
|
|
5201
|
+
process.exit(1);
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
if (!process.stdin.isTTY) {
|
|
5205
|
+
const piped = readStdin();
|
|
5206
|
+
if (piped.length > 0) return { raw: piped, sourceLabel: "stdin" };
|
|
5207
|
+
}
|
|
5208
|
+
console.error("\n\u274C \u672A\u63D0\u4F9B\u5F85\u6821\u9A8C\u6587\u6848\u3002\u8BF7\u7528\u4EE5\u4E0B\u4EFB\u4E00\u65B9\u5F0F\u4F20\u5165\uFF08\u5BBF\u4E3B\u65E0\u6587\u4EF6\u5DE5\u5177\u65F6\u7528\u540E\u4E24\u79CD\uFF09\uFF1A");
|
|
5209
|
+
console.error(" 1) \u6587\u4EF6\uFF1A siluzan-cso validate-content -f draft.md --max 800");
|
|
5210
|
+
console.error(' 2) \u7BA1\u9053\uFF1A echo "\u6587\u6848\u5185\u5BB9" | siluzan-cso validate-content --max 800');
|
|
5211
|
+
console.error(' 3) \u884C\u5185\u6587\u672C\uFF1Asiluzan-cso validate-content --text "\u6587\u6848\u5185\u5BB9" --max 800');
|
|
5212
|
+
process.exit(1);
|
|
5213
|
+
}
|
|
5214
|
+
async function runValidateContent(options) {
|
|
5215
|
+
const { raw, sourceLabel } = resolveContent(options);
|
|
5216
|
+
const countBy = options.countBy ?? "chars-no-space";
|
|
5217
|
+
const checkLeak = options.checkLeak !== false;
|
|
5218
|
+
const extraForbidden = parseForbidden(options.forbidden);
|
|
5219
|
+
const textForCount = options.stripMarkdown ? stripMarkdown(raw) : raw;
|
|
5220
|
+
const metrics = countMetrics(textForCount);
|
|
5221
|
+
const value = pickCount(metrics, countBy);
|
|
5222
|
+
const limitProblems = [];
|
|
5223
|
+
if (options.min !== void 0 && value < options.min) {
|
|
5224
|
+
limitProblems.push(
|
|
5225
|
+
`\u5B57\u6570\u4E0D\u8DB3\uFF1A${COUNT_BY_LABEL[countBy]} ${value} < \u4E0B\u9650 ${options.min}\uFF08\u8FD8\u5DEE ${options.min - value}\uFF09`
|
|
5226
|
+
);
|
|
5227
|
+
}
|
|
5228
|
+
if (options.max !== void 0 && value > options.max) {
|
|
5229
|
+
limitProblems.push(
|
|
5230
|
+
`\u5B57\u6570\u8D85\u9650\uFF1A${COUNT_BY_LABEL[countBy]} ${value} > \u4E0A\u9650 ${options.max}\uFF08\u8D85\u51FA ${value - options.max}\uFF09`
|
|
5231
|
+
);
|
|
5232
|
+
}
|
|
5233
|
+
const leaks = checkLeak || extraForbidden.length > 0 ? findLeaks(raw, extraForbidden) : [];
|
|
5234
|
+
const passed = limitProblems.length === 0 && leaks.length === 0;
|
|
5235
|
+
if (options.json) {
|
|
5236
|
+
console.log(
|
|
5237
|
+
JSON.stringify(
|
|
5238
|
+
{
|
|
5239
|
+
source: sourceLabel,
|
|
5240
|
+
countBy,
|
|
5241
|
+
strippedMarkdown: Boolean(options.stripMarkdown),
|
|
5242
|
+
limit: { min: options.min ?? null, max: options.max ?? null, value },
|
|
5243
|
+
metrics,
|
|
5244
|
+
limitProblems,
|
|
5245
|
+
leaks,
|
|
5246
|
+
passed
|
|
5247
|
+
},
|
|
5248
|
+
null,
|
|
5249
|
+
2
|
|
5250
|
+
)
|
|
5251
|
+
);
|
|
5252
|
+
process.exit(passed ? 0 : 1);
|
|
5253
|
+
}
|
|
5254
|
+
console.log(`
|
|
5255
|
+
\u{1F4C4} \u6587\u6848\u6821\u9A8C\uFF1A${sourceLabel}`);
|
|
5256
|
+
if (options.stripMarkdown) console.log(" \uFF08\u5DF2\u53BB\u9664 markdown \u8BED\u6CD5 / frontmatter \u540E\u7EDF\u8BA1\uFF09");
|
|
5257
|
+
console.log("");
|
|
5258
|
+
console.log(` \u603B\u5B57\u7B26\u6570\uFF08\u542B\u7A7A\u767D\uFF09 ${metrics.chars}`);
|
|
5259
|
+
console.log(` \u5B57\u7B26\u6570\uFF08\u4E0D\u542B\u7A7A\u767D\uFF09 ${metrics.charsNoSpace}`);
|
|
5260
|
+
console.log(` \u6C49\u5B57\u6570 ${metrics.cjk}`);
|
|
5261
|
+
console.log(` \u82F1\u6587\u5355\u8BCD\u6570 ${metrics.words}`);
|
|
5262
|
+
console.log(` \u884C\u6570 / \u6BB5\u843D\u6570 ${metrics.lines} / ${metrics.paragraphs}`);
|
|
5263
|
+
if (options.min !== void 0 || options.max !== void 0) {
|
|
5264
|
+
const range = `${options.min ?? "\u2014"} ~ ${options.max ?? "\u2014"}`;
|
|
5265
|
+
console.log(`
|
|
5266
|
+
\u5B57\u6570\u9650\u5236\uFF08${COUNT_BY_LABEL[countBy]}\uFF09\uFF1A\u5F53\u524D ${value}\uFF0C\u8981\u6C42 ${range}`);
|
|
5267
|
+
}
|
|
5268
|
+
console.log("\n\u6821\u9A8C\u7ED3\u679C\uFF1A");
|
|
5269
|
+
if (limitProblems.length === 0 && (options.min !== void 0 || options.max !== void 0)) {
|
|
5270
|
+
console.log(" \u2713 \u5B57\u6570\u5728\u9650\u5236\u8303\u56F4\u5185");
|
|
5271
|
+
}
|
|
5272
|
+
for (const p of limitProblems) console.log(` \u2717 ${p}`);
|
|
5273
|
+
if (checkLeak || extraForbidden.length > 0) {
|
|
5274
|
+
if (leaks.length === 0) {
|
|
5275
|
+
console.log(" \u2713 \u672A\u53D1\u73B0\u5185\u90E8\u9AA8\u67B6\u8D44\u4EA7 / \u7981\u7528\u8BCD\u6CC4\u6F0F");
|
|
5276
|
+
} else {
|
|
5277
|
+
console.log(` \u2717 \u53D1\u73B0 ${leaks.length} \u5904\u7591\u4F3C\u6CC4\u6F0F\uFF08\u4E0D\u5E94\u51FA\u73B0\u5728\u4EA4\u4ED8\u6210\u7A3F\uFF09\uFF1A`);
|
|
5278
|
+
for (const l of leaks) console.log(` \u300C${l.match}\u300D\u2014 ${l.reason}`);
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
if (passed) {
|
|
5282
|
+
console.log("\n\u2705 \u6587\u6848\u6821\u9A8C\u901A\u8FC7");
|
|
5283
|
+
process.exit(0);
|
|
5284
|
+
} else {
|
|
5285
|
+
console.log("\n\u274C \u6587\u6848\u6821\u9A8C\u672A\u901A\u8FC7\uFF0C\u8BF7\u6309\u4E0A\u8FF0\u95EE\u9898\u4FEE\u8BA2\u540E\u91CD\u65B0\u6821\u9A8C");
|
|
5286
|
+
process.exit(1);
|
|
5287
|
+
}
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5094
5290
|
// src/commands/report/_shared.ts
|
|
5095
5291
|
var DEFAULT_METHOD = "Day";
|
|
5096
5292
|
var DEFAULT_WORKS_ORDER = "play";
|
|
@@ -5408,8 +5604,8 @@ function printMarkdownFetch(runtime, includeModules, sections) {
|
|
|
5408
5604
|
}
|
|
5409
5605
|
|
|
5410
5606
|
// src/commands/report/commands.ts
|
|
5411
|
-
import * as
|
|
5412
|
-
import * as
|
|
5607
|
+
import * as fs10 from "fs";
|
|
5608
|
+
import * as path10 from "path";
|
|
5413
5609
|
async function runReportFetch(options) {
|
|
5414
5610
|
const runtime = buildRuntime(options);
|
|
5415
5611
|
const includeModules = parseModules(options.include);
|
|
@@ -5593,7 +5789,7 @@ function defaultDownloadPath(recordId) {
|
|
|
5593
5789
|
const now = /* @__PURE__ */ new Date();
|
|
5594
5790
|
const pad = (n) => String(n).padStart(2, "0");
|
|
5595
5791
|
const name = `operations-report-${recordId}-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.pdf`;
|
|
5596
|
-
return
|
|
5792
|
+
return path10.resolve(process.cwd(), name);
|
|
5597
5793
|
}
|
|
5598
5794
|
async function downloadFile(url, output) {
|
|
5599
5795
|
const res = await fetch(url);
|
|
@@ -5601,7 +5797,7 @@ async function downloadFile(url, output) {
|
|
|
5601
5797
|
throw new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF0CHTTP ${res.status}`);
|
|
5602
5798
|
}
|
|
5603
5799
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
5604
|
-
|
|
5800
|
+
fs10.writeFileSync(output, buffer);
|
|
5605
5801
|
}
|
|
5606
5802
|
async function runReportDownload(options) {
|
|
5607
5803
|
if (!options.id) {
|
|
@@ -5621,7 +5817,7 @@ async function runReportDownload(options) {
|
|
|
5621
5817
|
const msg = error.message;
|
|
5622
5818
|
exitWithError(`\u83B7\u53D6\u4E0B\u8F7D\u5730\u5740\u5931\u8D25\uFF1A${msg}`);
|
|
5623
5819
|
}
|
|
5624
|
-
const output =
|
|
5820
|
+
const output = path10.resolve(options.output ? options.output : defaultDownloadPath(options.id));
|
|
5625
5821
|
try {
|
|
5626
5822
|
await downloadFile(pdfUrl, output);
|
|
5627
5823
|
} catch (error) {
|
|
@@ -5852,8 +6048,8 @@ async function requestPlanning(config, endpoint, init = {}, verbose = false) {
|
|
|
5852
6048
|
}
|
|
5853
6049
|
|
|
5854
6050
|
// src/commands/planning/commands.ts
|
|
5855
|
-
import * as
|
|
5856
|
-
import * as
|
|
6051
|
+
import * as fs11 from "fs";
|
|
6052
|
+
import * as path11 from "path";
|
|
5857
6053
|
async function runPlanningEnterprises(options) {
|
|
5858
6054
|
const config = loadConfig(options.token);
|
|
5859
6055
|
const verbose = Boolean(options.verbose);
|
|
@@ -5981,7 +6177,7 @@ async function watchPlanTask(config, taskId, maxWaitMs, onProgress) {
|
|
|
5981
6177
|
const url = `${baseUrl}/api/plans/tasks/${encodeURIComponent(taskId)}/progress`;
|
|
5982
6178
|
const controller = new AbortController();
|
|
5983
6179
|
let finished = false;
|
|
5984
|
-
return new Promise((
|
|
6180
|
+
return new Promise((resolve7, reject) => {
|
|
5985
6181
|
const settle = (fn) => {
|
|
5986
6182
|
if (finished) return;
|
|
5987
6183
|
finished = true;
|
|
@@ -6028,7 +6224,7 @@ async function watchPlanTask(config, taskId, maxWaitMs, onProgress) {
|
|
|
6028
6224
|
}
|
|
6029
6225
|
onProgress?.(event);
|
|
6030
6226
|
if (event.status === "completed") {
|
|
6031
|
-
settle(() =>
|
|
6227
|
+
settle(() => resolve7(event.plan ?? null));
|
|
6032
6228
|
return;
|
|
6033
6229
|
}
|
|
6034
6230
|
if (event.status === "failed" || event.status === "cancelled") {
|
|
@@ -6359,13 +6555,13 @@ function inferEnterpriseNameFromPlan(plan) {
|
|
|
6359
6555
|
}
|
|
6360
6556
|
async function loadPlanFromSource(options, config) {
|
|
6361
6557
|
if (options.input) {
|
|
6362
|
-
const inputPath =
|
|
6363
|
-
if (!
|
|
6558
|
+
const inputPath = path11.resolve(options.input);
|
|
6559
|
+
if (!fs11.existsSync(inputPath)) {
|
|
6364
6560
|
exitWithError2(`\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${inputPath}`);
|
|
6365
6561
|
}
|
|
6366
6562
|
let raw = "";
|
|
6367
6563
|
try {
|
|
6368
|
-
raw =
|
|
6564
|
+
raw = fs11.readFileSync(inputPath, "utf-8");
|
|
6369
6565
|
} catch (error) {
|
|
6370
6566
|
exitWithError2(`\u8BFB\u53D6\u8F93\u5165\u6587\u4EF6\u5931\u8D25\uFF1A${error.message}`);
|
|
6371
6567
|
}
|
|
@@ -6399,9 +6595,9 @@ async function runPlanningExportTxt(options) {
|
|
|
6399
6595
|
const defaultName = sanitizeFilename(
|
|
6400
6596
|
`\u5185\u5BB9\u9009\u9898\u65B9\u5411\u89C4\u5212_${enterpriseName}_${plan.yearMonth ?? "unknown"}.txt`
|
|
6401
6597
|
);
|
|
6402
|
-
const outputPath =
|
|
6598
|
+
const outputPath = path11.resolve(options.output ? options.output : defaultName);
|
|
6403
6599
|
try {
|
|
6404
|
-
|
|
6600
|
+
fs11.writeFileSync(outputPath, text, "utf-8");
|
|
6405
6601
|
} catch (error) {
|
|
6406
6602
|
exitWithError2(`\u5199\u5165\u5BFC\u51FA\u6587\u4EF6\u5931\u8D25\uFF1A${error.message}`);
|
|
6407
6603
|
}
|
|
@@ -6898,8 +7094,8 @@ async function runAuthorize(opts) {
|
|
|
6898
7094
|
}
|
|
6899
7095
|
|
|
6900
7096
|
// src/commands/persona.ts
|
|
6901
|
-
import
|
|
6902
|
-
import
|
|
7097
|
+
import fs12 from "fs";
|
|
7098
|
+
import path12 from "path";
|
|
6903
7099
|
var MAX_PERSONA_NAME = 60;
|
|
6904
7100
|
function unwrapGetPersonas(raw) {
|
|
6905
7101
|
if (!raw || typeof raw !== "object") return null;
|
|
@@ -7002,15 +7198,15 @@ ${hint}`);
|
|
|
7002
7198
|
console.log("\n\u63D0\u793A\uFF1A\u5B8C\u6574 styleGuide\uFF08Markdown\uFF09\u8BF7\u4F7F\u7528 --json \u67E5\u770B\u6BCF\u6761\u8BB0\u5F55\u7684 styleGuide \u5B57\u6BB5\u3002");
|
|
7003
7199
|
}
|
|
7004
7200
|
function readStyleGuideFromFile(filePath) {
|
|
7005
|
-
const resolved =
|
|
7006
|
-
if (!
|
|
7201
|
+
const resolved = path12.resolve(process.cwd(), filePath);
|
|
7202
|
+
if (!fs12.existsSync(resolved)) {
|
|
7007
7203
|
throw new Error(`styleGuide \u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${resolved}`);
|
|
7008
7204
|
}
|
|
7009
|
-
const stat =
|
|
7205
|
+
const stat = fs12.statSync(resolved);
|
|
7010
7206
|
if (!stat.isFile()) {
|
|
7011
7207
|
throw new Error(`styleGuide \u6587\u4EF6\u8DEF\u5F84\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${resolved}`);
|
|
7012
7208
|
}
|
|
7013
|
-
return
|
|
7209
|
+
return fs12.readFileSync(resolved, "utf-8");
|
|
7014
7210
|
}
|
|
7015
7211
|
function unwrapAddPersona(raw) {
|
|
7016
7212
|
if (!raw || typeof raw !== "object") return null;
|
|
@@ -7545,7 +7741,7 @@ async function runRagList(options) {
|
|
|
7545
7741
|
}
|
|
7546
7742
|
|
|
7547
7743
|
// src/commands/config.ts
|
|
7548
|
-
import * as
|
|
7744
|
+
import * as fs13 from "fs";
|
|
7549
7745
|
function cmdConfigShow() {
|
|
7550
7746
|
const shared = readSharedConfig();
|
|
7551
7747
|
const envApiKey = process.env.SILUZAN_API_KEY;
|
|
@@ -7893,6 +8089,31 @@ function registerCsoCommands(program2) {
|
|
|
7893
8089
|
});
|
|
7894
8090
|
}
|
|
7895
8091
|
);
|
|
8092
|
+
program2.command("validate-content").description(
|
|
8093
|
+
"\u6821\u9A8C\u6587\u6848\uFF1A\u7EDF\u8BA1\u5B57\u6570\uFF08\u6C49\u5B57/\u5B57\u7B26/\u5355\u8BCD\uFF09\uFF0C\u6309 --min/--max \u68C0\u67E5\u5B57\u6570\u9650\u5236\uFF0C\u5E76\u68C0\u67E5\u4E09\u5E93\u7F16\u7801/\u6EAF\u6E90\u7B49\u5185\u90E8\u5185\u5BB9\u662F\u5426\u6CC4\u6F0F\u5230\u6210\u7A3F\u3002\u6587\u6848\u6765\u6E90\u652F\u6301 -f \u6587\u4EF6\u3001--text \u884C\u5185\u6587\u672C\u3001\u6216 stdin \u7BA1\u9053\uFF08\u5BBF\u4E3B\u65E0\u6587\u4EF6\u5DE5\u5177\u65F6\u7528\u540E\u4E24\u79CD\uFF09"
|
|
8094
|
+
).option("-f, --file <path>", "\u5F85\u6821\u9A8C\u7684\u6587\u6848\u6587\u4EF6\u8DEF\u5F84\uFF08.md / .txt\uFF09\uFF1B\u4E0E --text / stdin \u4E09\u9009\u4E00").option(
|
|
8095
|
+
"--text <content>",
|
|
8096
|
+
"\u76F4\u63A5\u4F20\u5165\u6587\u6848\u6B63\u6587\uFF08\u5BBF\u4E3B\u65E0\u6587\u4EF6\u5DE5\u5177\u65F6\u7528\uFF09\uFF1B\u4F18\u5148\u7EA7\u9AD8\u4E8E -f \u4E0E stdin"
|
|
8097
|
+
).option("--min <n>", "\u5B57\u6570\u4E0B\u9650\uFF08\u6309 --count-by \u53E3\u5F84\uFF09", parseInt).option("--max <n>", "\u5B57\u6570\u4E0A\u9650\uFF08\u6309 --count-by \u53E3\u5F84\uFF09", parseInt).option(
|
|
8098
|
+
"--count-by <mode>",
|
|
8099
|
+
"\u9650\u5236\u53E3\u5F84\uFF1Achars-no-space\uFF08\u4E0D\u542B\u7A7A\u767D\uFF0C\u9ED8\u8BA4\uFF09| chars\uFF08\u542B\u7A7A\u767D\uFF09| cjk\uFF08\u6C49\u5B57\uFF09| words\uFF08\u82F1\u6587\u5355\u8BCD\uFF09",
|
|
8100
|
+
"chars-no-space"
|
|
8101
|
+
).option("--strip-markdown", "\u7EDF\u8BA1\u524D\u53BB\u9664 markdown \u8BED\u6CD5\u4E0E frontmatter\uFF08\u66F4\u8D34\u8FD1\u6B63\u6587\u5B57\u6570\uFF09", false).option("--no-check-leak", "\u8DF3\u8FC7\u5185\u90E8\u9AA8\u67B6\u8D44\u4EA7\uFF08\u4E09\u5E93\u7F16\u7801/\u6EAF\u6E90/SOP \u7B49\uFF09\u6CC4\u6F0F\u68C0\u67E5").option("--forbidden <words>", "\u989D\u5916\u7981\u7528\u5B50\u4E32\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u547D\u4E2D\u5373\u6821\u9A8C\u4E0D\u901A\u8FC7\uFF09").option("--json", "\u4EE5 JSON \u8F93\u51FA\u6821\u9A8C\u7ED3\u679C\uFF08\u9002\u5408\u811A\u672C/\u81EA\u52A8\u5316\uFF09", false).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
8102
|
+
async (opts) => {
|
|
8103
|
+
await runValidateContent({
|
|
8104
|
+
file: opts.file,
|
|
8105
|
+
text: opts.text,
|
|
8106
|
+
min: opts.min,
|
|
8107
|
+
max: opts.max,
|
|
8108
|
+
countBy: opts.countBy,
|
|
8109
|
+
stripMarkdown: opts.stripMarkdown,
|
|
8110
|
+
checkLeak: opts.checkLeak,
|
|
8111
|
+
forbidden: opts.forbidden,
|
|
8112
|
+
json: opts.json,
|
|
8113
|
+
verbose: opts.verbose
|
|
8114
|
+
});
|
|
8115
|
+
}
|
|
8116
|
+
);
|
|
7896
8117
|
program2.command("publish").description("\u6309 JSON \u914D\u7F6E\u6587\u4EF6\u63D0\u4EA4\u53D1\u5E03\u4EFB\u52A1\u5230 CSO").requiredOption("-c, --config <path>", "\u53D1\u5E03\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08JSON\uFF09").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--dry-run", "\u4EC5\u9884\u89C8\u8BF7\u6C42\u4F53\uFF0C\u4E0D\u5B9E\u9645\u63D0\u4EA4", false).option("--verbose", "\u663E\u793A\u5B8C\u6574\u8BF7\u6C42\u4F53\u53CA\u8BE6\u7EC6\u9519\u8BEF\uFF08\u542B\u654F\u611F\u5B57\u6BB5\uFF0C\u9ED8\u8BA4\u8131\u654F\uFF09", false).action(
|
|
7897
8118
|
async (opts) => {
|
|
7898
8119
|
await runPublish({
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: siluzan-cso
|
|
3
|
-
description: 当用户提问的内容涉及以下三类业务时需加载本skill:(1)
|
|
3
|
+
description: 当用户提问的内容涉及以下三类业务时需加载本skill:(1) 内容文案生产与人设管理:选题、爆款拆解、文案撰写(生成任何文案——包括但不限于:视频脚本、博客、公众号文章、口播稿等,必须优先使用本skill,不可直接输出)、人设卡生成/反推/查询/保存、改稿润色;(2) 支持YouTube、TikTok、Instagram、LinkedIn、X、Facebook 媒体平台的内容发布与运营:平台账号绑定与 OAuth 授权、视频图文发布、任务管理与失败重试、播放粉丝绩效报表、**账号分组(本 skill 独有;广告账户体系不存在分组概念,用户只说"账号分组"也默认走本 skill,不要进 siluzan-tso)**、素材上传、封面截取、AI 内容规划;(3) RAG 知识库检索:品牌/产品知识库问答与文案事实依据。**如涉及账号相关操作,若账号类型(广告账号/运营媒体账号)不明确,请先询问用户,仅当确认为"运营媒体账号"时,才可使用本 skill。**
|
|
4
4
|
compatibility: Requires siluzan-cso-cli installed and authenticated via `siluzan-cso login`
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -29,7 +29,7 @@ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令
|
|
|
29
29
|
|
|
30
30
|
- **只读**:查询媒体账号列表、账号分组、运营报表、发布任务状态、人设列表、RAG 知识库检索、AI 内容规划详情
|
|
31
31
|
- **写入**(需用户确认):上传素材、提交发布任务、创建/更新账号分组、生成 AI 内容规划、站内信回复
|
|
32
|
-
- **本地文件操作**:`extract-cover` 在本地截取视频帧并输出图片文件;`init` 将 Skill 文件写入 AI 助手目录
|
|
32
|
+
- **本地文件操作**:`extract-cover` 在本地截取视频帧并输出图片文件;`validate-content` 在本地校验文案文件(字数限制 / 内部内容泄漏);`init` 将 Skill 文件写入 AI 助手目录
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
@@ -45,9 +45,9 @@ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令
|
|
|
45
45
|
|
|
46
46
|
## 能力范围
|
|
47
47
|
|
|
48
|
-
| 业务流程 | 手段 | 说明
|
|
49
|
-
| ---------------------- | ------------------------------------------------------- |
|
|
50
|
-
| **发布与运营** | 下方 CLI 命令 + `references/*.md` | 上传、发布、任务、报表、账号、规划等
|
|
48
|
+
| 业务流程 | 手段 | 说明 |
|
|
49
|
+
| ---------------------- | ------------------------------------------------------- | ---------------------------------------- |
|
|
50
|
+
| **发布与运营** | 下方 CLI 命令 + `references/*.md` | 上传、发布、任务、报表、账号、规划等 |
|
|
51
51
|
| **文案生产(子流程)** | `three-lib-content-workflow/content-writer.workflow.md` | 选题、三库、口播/公众号/成稿、审稿、改稿 |
|
|
52
52
|
|
|
53
53
|
两类流程同属 CSO 业务。文案生产流程嵌套在本 skill 内,见下文「三库内容工作流」。
|
|
@@ -68,6 +68,7 @@ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令
|
|
|
68
68
|
| `siluzan-cso account-group list/create/add-accounts/remove-accounts/update/delete` | 账号分组管理 | `references/account-group.md` |
|
|
69
69
|
| `siluzan-cso upload -f <file>` | 上传视频 / 图片到素材库 | `references/upload.md` |
|
|
70
70
|
| `siluzan-cso extract-cover -f <video> -p <平台>` | 从视频截取封面帧 | `references/extract-cover.md` |
|
|
71
|
+
| `siluzan-cso validate-content -f <file>` | 文案落盘后校验:字数统计 / 字数限制(`--min`/`--max`)/ 内部内容泄漏检查 | `references/validate-content.md` |
|
|
71
72
|
| `siluzan-cso publish -c config.json` | 提交多平台发布任务 | `references/publish.md` |
|
|
72
73
|
| `siluzan-cso task list/detail/item` | 查看任务状态 / 处理失败 / 重试 | `references/task.md` |
|
|
73
74
|
| `siluzan-cso report fetch --media <平台>` | 运营报表(核心指标 / 视频排行 / 趋势) | `references/report.md` |
|
|
@@ -78,21 +79,22 @@ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令
|
|
|
78
79
|
|
|
79
80
|
## 常见业务场景 → 阅读哪个文件
|
|
80
81
|
|
|
81
|
-
| 用户在做什么
|
|
82
|
-
|
|
|
83
|
-
| 首次安装 / 登录 / 更新
|
|
84
|
-
| 发布视频或图文
|
|
85
|
-
| 上传素材
|
|
86
|
-
| 截取视频封面
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
82
|
+
| 用户在做什么 | 先阅读 |
|
|
83
|
+
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
84
|
+
| 首次安装 / 登录 / 更新 | `references/setup.md` |
|
|
85
|
+
| 发布视频或图文 | `references/publish.md` |
|
|
86
|
+
| 上传素材 | `references/upload.md` |
|
|
87
|
+
| 截取视频封面 | `references/extract-cover.md` |
|
|
88
|
+
| 文案写完落盘后校验字数 / 检查内部内容泄漏 | `references/validate-content.md` |
|
|
89
|
+
| 查发布记录 / 处理失败 | `references/task.md` |
|
|
90
|
+
| 查账号数据 / 运营报表 | `references/report.md` |
|
|
91
|
+
| 查找账号 ID 或账号详情 | `references/list-accounts.md` |
|
|
92
|
+
| 账号 Token 失效 / 重新授权 | `references/authorize.md` |
|
|
93
|
+
| 管理账号分组 | `references/account-group.md` |
|
|
94
|
+
| AI 内容规划 | `references/planning.md` |
|
|
95
|
+
| 需要给用户提供后台页面链接 | `references/web-pages.md` |
|
|
96
|
+
| 拉取人设 / styleGuide(写稿前)/ 保存人设 | `references/persona.md` |
|
|
97
|
+
| 写稿时检索素材库 RAG 片段(三库拆素材等) | `references/rag.md` |
|
|
96
98
|
| 选题 / 三库拆解 / 口播文案/公众号文章/其他文案 / 人设卡 / 代表作品反推人设 / 文章审稿打分 / 精准改稿 | `three-lib-content-workflow/content-writer.workflow.md` |
|
|
97
99
|
|
|
98
100
|
---
|
package/dist/skill/_meta.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"slug": "siluzan-cso",
|
|
3
|
-
"version": "1.1.27-beta.
|
|
4
|
-
"publishedAt":
|
|
3
|
+
"version": "1.1.27-beta.5",
|
|
4
|
+
"publishedAt": 1781085763387,
|
|
5
5
|
"homepage": "https://www.siluzan.com",
|
|
6
6
|
"source": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill",
|
|
7
7
|
"requiredBinaries": [
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# validate-content — 文案校验(字数限制 + 内部内容泄漏)
|
|
2
|
+
|
|
3
|
+
> 对文案做**本地**校验:统计字数、按字数限制检查上下限、并检查三库编码 / 溯源 / SOP 等内部骨架资产是否泄漏到成稿。
|
|
4
|
+
> 纯本地操作,不调用任何接口、不需要鉴权。校验**通过 exit 0,不通过 exit 1**,便于在工作流中卡点。
|
|
5
|
+
|
|
6
|
+
## 文案来源(三选一)
|
|
7
|
+
|
|
8
|
+
优先级:`--text` > `-f/--file` > stdin(管道)。**宿主没有文件读写工具时**,用后两种方式直接把对话里的文案传进来,无需落盘:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# 1) 文件(宿主有文件工具时最常用)
|
|
12
|
+
siluzan-cso validate-content -f draft.md --max 800
|
|
13
|
+
|
|
14
|
+
# 2) 管道 / heredoc(推荐:长文不受命令行长度限制)
|
|
15
|
+
cat <<'EOF' | siluzan-cso validate-content --max 800
|
|
16
|
+
这里是要校验的整篇文案……
|
|
17
|
+
可以包含多行、markdown 等。
|
|
18
|
+
EOF
|
|
19
|
+
|
|
20
|
+
# 3) 行内文本(短文案;过长会受 shell 参数长度限制)
|
|
21
|
+
siluzan-cso validate-content --text "这里是文案内容" --max 280
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> 三者都没提供且 stdin 非管道时,命令会报错并打印上述三种用法指引。
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 何时使用
|
|
29
|
+
|
|
30
|
+
- 生成口播稿 / 公众号文章 / 博客等成稿并**落盘后**,交付给用户**之前**做一次质检。
|
|
31
|
+
- 用户给出了**字数限制**(如「不超过 800 字」「至少 1500 字」「标题 ≤ 20 字」)时,用 `--min` / `--max` 核对。
|
|
32
|
+
- 担心内部骨架资产(三库内部编码 `TF-/PA-/MF-`、`三库溯源`、`SOP执行`、三库名称、`焊点` 等过程内容)误写进成稿时(默认开启该检查)。
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 用法
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 最简:只统计字数 + 默认泄漏检查
|
|
40
|
+
siluzan-cso validate-content -f draft.md
|
|
41
|
+
|
|
42
|
+
# 字数上限 800(默认按「不含空白字符数」口径)
|
|
43
|
+
siluzan-cso validate-content -f draft.md --max 800
|
|
44
|
+
|
|
45
|
+
# 字数区间 1500~3000,按汉字数统计
|
|
46
|
+
siluzan-cso validate-content -f article.md --min 1500 --max 3000 --count-by cjk
|
|
47
|
+
|
|
48
|
+
# 去除 markdown 语法后再统计(更贴近用户感知的正文字数)
|
|
49
|
+
siluzan-cso validate-content -f article.md --max 800 --strip-markdown
|
|
50
|
+
|
|
51
|
+
# 关闭内部内容泄漏检查(仅看字数)
|
|
52
|
+
siluzan-cso validate-content -f draft.md --max 280 --no-check-leak
|
|
53
|
+
|
|
54
|
+
# 追加自定义禁用词(命中即不通过)
|
|
55
|
+
siluzan-cso validate-content -f draft.md --forbidden "竞品名,内部代号,占位符"
|
|
56
|
+
|
|
57
|
+
# 脚本/自动化:JSON 输出 + 退出码判定
|
|
58
|
+
siluzan-cso validate-content -f draft.md --max 800 --json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 参数
|
|
64
|
+
|
|
65
|
+
| 参数 | 说明 |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| `-f, --file <path>` | 待校验文案文件(`.md` / `.txt`);与 `--text` / stdin 三选一 |
|
|
68
|
+
| `--text <content>` | 直接传入文案正文(宿主无文件工具时用);优先级最高 |
|
|
69
|
+
| `--min <n>` | 字数下限(按 `--count-by` 口径) |
|
|
70
|
+
| `--max <n>` | 字数上限(按 `--count-by` 口径) |
|
|
71
|
+
| `--count-by <mode>` | 限制口径:`chars-no-space`(不含空白,**默认**)· `chars`(含空白)· `cjk`(汉字)· `words`(英文单词) |
|
|
72
|
+
| `--strip-markdown` | 统计前去除 markdown 语法与 frontmatter,默认 `false` |
|
|
73
|
+
| `--no-check-leak` | 跳过内部骨架资产泄漏检查(默认开启检查) |
|
|
74
|
+
| `--forbidden <words>` | 额外禁用子串,逗号分隔,命中即校验不通过 |
|
|
75
|
+
| `--json` | JSON 输出(含全部指标、问题列表、`passed`) |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 字数口径选择建议
|
|
80
|
+
|
|
81
|
+
- **中文成稿(公众号 / 博客 / 口播)**:默认 `chars-no-space` 或 `cjk` 都可;用户说「字数」一般指不含空白的字符数。
|
|
82
|
+
- **标题 / 简介等带英文混排**:`chars-no-space`。
|
|
83
|
+
- **纯英文内容**:用 `words` 统计单词数。
|
|
84
|
+
- **平台硬上限(如 X/Twitter 280 字符)**:用 `chars`(含空白)最贴近平台计数。
|
|
85
|
+
|
|
86
|
+
> markdown 文件里 `#`、`*`、`>` 等符号会被计入原始字符数。需要「正文字数」时加 `--strip-markdown`。
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 泄漏检查命中项
|
|
91
|
+
|
|
92
|
+
默认检查并视为**不应出现在成稿**的内部内容(与 `three-lib-content-workflow` 第 2 步「内容保密」一致):
|
|
93
|
+
|
|
94
|
+
- 三库内部编码:`TF-xxxx` / `PA-xxxx` / `MF-xxxx`
|
|
95
|
+
- `三库溯源`、`SOP执行 / SOP步骤 / SOP记录`、`焊点`
|
|
96
|
+
- 三库名称:`流量因子库` / `产品资产库` / `烹调方法库`
|
|
97
|
+
|
|
98
|
+
命中后会逐条列出片段与原因;AI 应据此删改成稿后重新校验,**不要**把这些内部内容交付给用户。
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 退出码
|
|
103
|
+
|
|
104
|
+
| 退出码 | 含义 |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `0` | 校验通过(字数在范围内 + 无泄漏 / 禁用词) |
|
|
107
|
+
| `1` | 文件不存在、字数超/欠限、或命中泄漏 / 禁用词 |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 交叉引用
|
|
112
|
+
|
|
113
|
+
- 成稿创作流程 → `three-lib-content-workflow/content-writer.workflow.md`
|
|
114
|
+
- 成稿落盘与呈现约定 → 同上「输出」一节
|
|
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
$PKG_NAME = 'siluzan-cso-cli'
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
$PKG_VERSION = '1.1.27-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.27-beta.5'
|
|
13
13
|
$CLI_BIN = 'siluzan-cso'
|
|
14
14
|
$SKILL_LABEL = 'Siluzan CSO'
|
|
15
15
|
$INSTALL_CMD = 'npm install -g siluzan-cso-cli@beta'
|
|
@@ -9,7 +9,7 @@ set -euo pipefail
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
readonly PKG_NAME="siluzan-cso-cli"
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
readonly PKG_VERSION="1.1.27-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.27-beta.5"
|
|
13
13
|
readonly CLI_BIN="siluzan-cso"
|
|
14
14
|
readonly SKILL_LABEL="Siluzan CSO"
|
|
15
15
|
readonly INSTALL_CMD="npm install -g siluzan-cso-cli@beta"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- **人设卡** = 注入骨架的"风味与边界",**统一走 CSO 平台**(`siluzan-cso persona`)
|
|
18
18
|
- **赛道包**(`packs/`)= 特定平台的专属扩展,按人设 `platform_focus` 挂载
|
|
19
19
|
|
|
20
|
-
> **AI
|
|
20
|
+
> **AI 行为规范**:务必逐步、严格执行下述全部流程步骤,严禁跳过或简化;流程在**内部**走完,**不得**把各步过程(三库溯源、TF/PA/MF 编码、SOP 记录、焊点 / 自检等)主动展示给用户——只交付最终成稿;用户未索要过程前,不能提前输出最终结果。
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -125,21 +125,14 @@
|
|
|
125
125
|
|
|
126
126
|
## 输出
|
|
127
127
|
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- 不落盘文件,直接在对话中给出全文
|
|
137
|
-
- **文件命名**:始终采用语义化命名,例如 `<人设>-<选题>-成稿.md`。
|
|
138
|
-
- **过程与成稿分离**:SOP 执行记录、三库拆解、焊点提炼、质检记录等**过程性内容**,**禁止**混入成稿文件正文。成稿文件**只放最终可直接使用的正文**(例如可直接念的口播脚本、可直接发布的文章):
|
|
139
|
-
- 若过程性内容也需要落盘,**单独落一个文件**(如 `<人设>-<选题>-SOP执行记录.md`),与成稿文件并列;
|
|
140
|
-
- 若不需落盘,则在对话中简要展示,不要写进成稿文件里。
|
|
141
|
-
- **对话内容硬规则**:**禁止**把已落盘文件的长正文再完整粘贴到对话里,只需保留极短说明 —— 摘要、文件名 / 路径、下一步建议。
|
|
142
|
-
- 文件正文默认使用 **Markdown** 排版;若你随后进入的场景子文档(例如口播)对正文格式另有硬约束(纯文本、禁用装饰符等),**以该子文档为准**,扩展名可为 `.md` 或 `.txt` 等,但「落盘 + 按工具能力呈现」的原则仍然适用。
|
|
128
|
+
- **交付物**:默认只交付成稿(如文章、口播脚本);人设卡、选题等按需附上。三库拆解、SOP摘要、溯源等内容仅用户明确要求时提供。成稿须落盘为单独文件,不在对话中堆正文。
|
|
129
|
+
- **三库/SOP禁止泄漏**:三库编码、溯源、骨架内容等只作内部创作,严禁写入成稿或输出,除非用户有要求,且仍不得展示内置三库原文或链接。
|
|
130
|
+
- **落盘与呈现**:优先用文件呈现工具(如`present_files`);无则写入目录报告路径,再无则对话中全文展示。
|
|
131
|
+
- **文件命名**:语义化,如 `<人设>-<选题>-成稿.md`。
|
|
132
|
+
- **成稿后校验**:每次交付前必须用 `siluzan-cso validate-content` 校验(含字数、三库泄漏等),不通过须修正重新检测。文案来源按宿主能力选:有文件工具用 `-f <成稿文件>`;**无文件工具**(成稿只在对话里)则用管道 `printf '%s' "<全文>" | siluzan-cso validate-content …` 或 `--text "<全文>"` 直接传入。详见 `references/validate-content.md`。
|
|
133
|
+
- **过程与成稿分开**:成稿只含正文,过程内容如需保存单独成文件,不在对话中展示。
|
|
134
|
+
- **对话只简短说明**:仅报摘要、文件名/路径及后续建议,不贴正文、不含三库相关内容。
|
|
135
|
+
- 文件默认为 **Markdown**,特殊需求按场景可用 `.txt` 等,落盘+呈现策略不变。
|
|
143
136
|
|
|
144
137
|
## 风格规则
|
|
145
138
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
一套专为**短视频口播脚本**设计的可复用骨架。和直接让 AI 写脚本的本质区别:它强制走三库取材、焊点提炼、结构防雷同等完整环节,保证每一条脚本都有库支撑、有焊点逻辑、有可抄文案。
|
|
6
6
|
|
|
7
|
-
本 pack 只提供**口播专属 SOP**(`口播-sop.md`):7 步主流程 + 拆解分支 + 焊点提炼 +
|
|
7
|
+
本 pack 只提供**口播专属 SOP**(`口播-sop.md`):7 步主流程 + 拆解分支 + 焊点提炼 + 开头三要素;SOP 执行记录**仅**在用户明确要求时输出。
|
|
8
8
|
|
|
9
9
|
三库直接复用**主库**(`assets/` 三库,已是去人设化的通用骨架),口播不单独维护一份重复的库。
|
|
10
10
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
| SOP | `sop.md` 12 步(多平台通用) | `口播-sop.md` 7 步(口播专用,含焊点/拆解/开头三要素) |
|
|
28
28
|
| 三库 | 主库 `assets/`(通用骨架) | **共用主库**(pack 不另带库) |
|
|
29
29
|
| 人设 | 走 CSO 平台 | 走 CSO 平台(pack 不内置人设) |
|
|
30
|
-
| 特色 | 通用质检 | 焊点提炼 + 焊点质检 +
|
|
30
|
+
| 特色 | 通用质检 | 焊点提炼 + 焊点质检 + 六条输出标准(内部核对,默认不外露) |
|
|
31
31
|
|
|
32
32
|
口播场景**优先走本 pack 的 `口播-sop.md`**,三库取主库。
|
|
33
33
|
|
|
@@ -55,9 +55,9 @@ packs/短视频口播/
|
|
|
55
55
|
|
|
56
56
|
pack 不内置任何人设。写稿前从 CSO 平台 `siluzan-cso persona list --json` 拉取目标人设;没有则按 `three-lib-content-workflow/persona-onboarding.md` 流程建一个。
|
|
57
57
|
|
|
58
|
-
### 2.
|
|
58
|
+
### 2. 三库取材须内部记录来源
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
每一步用到什么因子、句式、结构,须在内部标 TF / PA / 结构名称编号,确保有据可查;**默认不向用户展示**,除非用户明确要求溯源(见 `content-writer.workflow.md`「输出」段)。
|
|
61
61
|
|
|
62
62
|
### 3. 焊点是灵魂
|
|
63
63
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# 口播脚本生成 SOP
|
|
2
2
|
|
|
3
|
-
> 本文件是「短视频口播」赛道包的专属 SOP。人设 `platform_focus` 含 `短视频` 时,由 `content-writer.workflow.md` 第
|
|
3
|
+
> 本文件是「短视频口播」赛道包的专属 SOP。人设 `platform_focus` 含 `短视频` 时,由 `content-writer.workflow.md` 第 3 步挂载,**口播场景优先走本 SOP**(比通用 `sop.md` 更细:多了焊点提炼、拆解分支、开头三要素)。
|
|
4
|
+
>
|
|
5
|
+
> **输出纪律(高于本 SOP 各步「标注来源」表述)**:三库编号、焊点、SOP 步骤等过程信息**只在内部记录**,默认**不**写进对话、不夹带进成稿;仅当用户**明确要求**查看溯源 / SOP 记录时,才按文末「SOP 执行记录」格式输出。详见 `content-writer.workflow.md`「输出」段。
|
|
4
6
|
>
|
|
5
7
|
> 人设统一走 CSO 平台(`siluzan-cso persona`),本 SOP 不使用任何本地人设文件。
|
|
6
8
|
|
|
@@ -14,7 +16,7 @@
|
|
|
14
16
|
|
|
15
17
|
> 口播与其他平台**共用同一套主库**(主库已是去人设化的通用骨架),本 pack 不再单独维护一份重复的库。口播的独有价值在本 SOP 的方法论(焊点 / 拆解分支 / 开头三要素),不在库。
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
**三库取材原则**:每一步用到什么因子、什么句式、什么结构,须在**内部**记录来源(如 TF-0010、PA-0002、烹调方法库·认知重构结构),确保有据可查;**默认不向用户展示**这些编号与溯源,除非用户明确要求。
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
@@ -94,7 +96,7 @@
|
|
|
94
96
|
- 按人设的受众和情绪类型过滤
|
|
95
97
|
- 召回 1-2 个候选因子,选嫁接点最自然的一个
|
|
96
98
|
|
|
97
|
-
然后从库里召回 2-4 个具体 TF
|
|
99
|
+
然后从库里召回 2-4 个具体 TF 条目,**内部记录**(不向用户展示):
|
|
98
100
|
|
|
99
101
|
- 每个因子的编号和名称
|
|
100
102
|
- 为什么这个因子适合这条内容
|
|
@@ -106,11 +108,11 @@
|
|
|
106
108
|
|
|
107
109
|
### 第3步:匹配产品资产
|
|
108
110
|
|
|
109
|
-
从产品资产库里选取对应的 PA
|
|
111
|
+
从产品资产库里选取对应的 PA 模板,**内部记录**(不向用户展示):
|
|
110
112
|
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
113
|
+
- 选用的 PA 编号和名称
|
|
114
|
+
- 从这个模板里调用了哪些具体句式(开头句式、过渡句式、结尾句式)
|
|
115
|
+
- 如果有金句库或高频模块可用,一并记下
|
|
114
116
|
|
|
115
117
|
> 具体句式的腔调由人设卡 `common_phrases` 注入,PA 模板只提供骨架。
|
|
116
118
|
|
|
@@ -154,12 +156,12 @@
|
|
|
154
156
|
|
|
155
157
|
**结构由焊点类型决定,不由"写内容"这个动作决定。**
|
|
156
158
|
|
|
157
|
-
|
|
159
|
+
从烹调方法库明确选取,**内部记录**(不向用户展示):
|
|
158
160
|
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
-
|
|
161
|
+
- 结构模板(库中名称)
|
|
162
|
+
- 推进方式(库中名称)
|
|
163
|
+
- 表现手法(库中名称)
|
|
164
|
+
- 调用的具体句式(从库中取,逐条记下)
|
|
163
165
|
|
|
164
166
|
**防结构雷同**:这条的开头方式、推进方式、结尾方式,和上一条是不是一样?一样就换一个结构。
|
|
165
167
|
|
|
@@ -209,9 +211,9 @@
|
|
|
209
211
|
**专项自检**(人设已加载时必查,从人设卡 `quality_checks` 提取维度):
|
|
210
212
|
根据人设的受众和目标,检查内容是否符合这个账号"听起来该有的感觉"。
|
|
211
213
|
|
|
212
|
-
|
|
214
|
+
**六条输出质量标准**(每次生成完**内部**逐条核对,不向用户展示核对表):
|
|
213
215
|
|
|
214
|
-
1.
|
|
216
|
+
1. 流量因子是否从库里取,内部是否记录了 TF 编号
|
|
215
217
|
2. 同频是否真正带入了受众处境(不是泛泛的情绪,是他们今天正在经历的事)
|
|
216
218
|
3. 开头三要素是否至少命中2个
|
|
217
219
|
4. 是否只拆了 1-2 个点,没有贪多分散
|
package/package.json
CHANGED