autocrew 0.3.8 → 0.3.10
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/package.json +1 -1
- package/src/cli/commands/review.ts +40 -9
- package/src/tools/review.ts +15 -3
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import { getOption, resolveProjectText } from "./index.js";
|
|
|
4
4
|
export const cmd: CommandDef = {
|
|
5
5
|
name: "review",
|
|
6
6
|
description: "Run full content review or auto-fix",
|
|
7
|
-
usage: "autocrew review <
|
|
7
|
+
usage: "autocrew review <id-or-slug> [--platform <p>] [--fix] [--dry-run] [--file <path>]",
|
|
8
8
|
action: async (args, runner) => {
|
|
9
9
|
const id = args[0];
|
|
10
10
|
if (!id && !args.includes("--file")) {
|
|
@@ -15,6 +15,7 @@ export const cmd: CommandDef = {
|
|
|
15
15
|
|
|
16
16
|
const platform = getOption(args, "--platform");
|
|
17
17
|
const isFix = args.includes("--fix");
|
|
18
|
+
const isDryRun = args.includes("--dry-run");
|
|
18
19
|
|
|
19
20
|
const resolved = await resolveProjectText(id, args);
|
|
20
21
|
if (!resolved) {
|
|
@@ -23,7 +24,7 @@ export const cmd: CommandDef = {
|
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
if (isFix) {
|
|
27
|
+
if (isFix || isDryRun) {
|
|
27
28
|
const result = await runner.execute("autocrew_review", {
|
|
28
29
|
action: "auto_fix",
|
|
29
30
|
text: resolved.text,
|
|
@@ -36,13 +37,47 @@ export const cmd: CommandDef = {
|
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
// Write fixed text back to source file
|
|
40
40
|
const fixedText = (result.autoFixedText || result.fixedText || "") as string;
|
|
41
|
+
console.log(` Sensitive words fixed: ${result.sensitiveWordsFixed || 0}`);
|
|
42
|
+
console.log(` AI traces fixed: ${result.aiFixesApplied || 0}`);
|
|
43
|
+
|
|
44
|
+
// Show diff preview
|
|
45
|
+
if (fixedText && fixedText !== resolved.text) {
|
|
46
|
+
// Simple line-by-line diff
|
|
47
|
+
const origLines = resolved.text.split("\n");
|
|
48
|
+
const fixedLines = fixedText.split("\n");
|
|
49
|
+
const diffLines: string[] = [];
|
|
50
|
+
const maxLen = Math.max(origLines.length, fixedLines.length);
|
|
51
|
+
for (let i = 0; i < maxLen; i++) {
|
|
52
|
+
const orig = origLines[i] ?? "";
|
|
53
|
+
const fixed = fixedLines[i] ?? "";
|
|
54
|
+
if (orig !== fixed) {
|
|
55
|
+
if (orig) diffLines.push(` - ${orig}`);
|
|
56
|
+
if (fixed) diffLines.push(` + ${fixed}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (diffLines.length > 0) {
|
|
60
|
+
console.log(`\n Changes preview:`);
|
|
61
|
+
for (const line of diffLines.slice(0, 20)) {
|
|
62
|
+
console.log(line);
|
|
63
|
+
}
|
|
64
|
+
if (diffLines.length > 20) {
|
|
65
|
+
console.log(` ... and ${diffLines.length - 20} more lines`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isDryRun) {
|
|
71
|
+
console.log(`\n (dry-run mode — no files modified)`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Write fixed text back to source file
|
|
41
76
|
if (fixedText && resolved.source.startsWith("file:")) {
|
|
42
77
|
const fs = await import("node:fs/promises");
|
|
43
78
|
const filePath = resolved.source.replace("file:", "");
|
|
44
79
|
await fs.writeFile(filePath, fixedText, "utf-8");
|
|
45
|
-
console.log(
|
|
80
|
+
console.log(`\n Saved back to ${filePath}`);
|
|
46
81
|
} else if (fixedText && resolved.source.startsWith("pipeline:")) {
|
|
47
82
|
const fs = await import("node:fs/promises");
|
|
48
83
|
const { findProject } = await import("../../storage/pipeline-store.js");
|
|
@@ -51,13 +86,9 @@ export const cmd: CommandDef = {
|
|
|
51
86
|
if (found) {
|
|
52
87
|
const path = await import("node:path");
|
|
53
88
|
await fs.writeFile(path.join(found.dir, "draft.md"), fixedText, "utf-8");
|
|
54
|
-
console.log(
|
|
89
|
+
console.log(`\n Saved back to draft.md`);
|
|
55
90
|
}
|
|
56
|
-
} else {
|
|
57
|
-
console.log(`Auto-fix complete for "${resolved.title}" (${resolved.source})`);
|
|
58
91
|
}
|
|
59
|
-
console.log(` Sensitive words fixed: ${result.sensitiveWordsFixed || 0}`);
|
|
60
|
-
console.log(` AI traces fixed: ${result.aiFixesApplied || 0}`);
|
|
61
92
|
return;
|
|
62
93
|
}
|
|
63
94
|
|
package/src/tools/review.ts
CHANGED
|
@@ -212,10 +212,14 @@ export async function executeReview(params: Record<string, unknown>) {
|
|
|
212
212
|
const scanResult = await scanText(fullText, platform, dataDir);
|
|
213
213
|
let fixedText = scanResult.autoFixedText || fullText;
|
|
214
214
|
|
|
215
|
-
// 2. Humanizer pass
|
|
215
|
+
// 2. Humanizer pass (substitution + deletion only, never insertion)
|
|
216
216
|
const humanResult = humanizeZh({ text: fixedText });
|
|
217
217
|
fixedText = humanResult.humanizedText;
|
|
218
218
|
|
|
219
|
+
// 3. Safety guard — strip any hardcoded phrases that old humanizer versions
|
|
220
|
+
// may have inserted. This is a belt-and-suspenders defense.
|
|
221
|
+
fixedText = fixedText.replace(/\n*说白了,这件事拼的不是工具数量,而是表达和执行。\n*/g, "\n\n");
|
|
222
|
+
|
|
219
223
|
// Save back if content_id provided
|
|
220
224
|
if (contentId) {
|
|
221
225
|
await updateContent(contentId, { body: fixedText }, dataDir);
|
|
@@ -250,13 +254,21 @@ export async function executeReview(params: Record<string, unknown>) {
|
|
|
250
254
|
const fixes: string[] = [];
|
|
251
255
|
if (sensitiveWords.hitCount > 0) {
|
|
252
256
|
fixes.push(`修复 ${sensitiveWords.hitCount} 个敏感词`);
|
|
253
|
-
for (const hit of sensitiveWords.hits.slice(0,
|
|
257
|
+
for (const hit of sensitiveWords.hits.slice(0, 10)) {
|
|
254
258
|
const fix = hit.suggestion ? `"${hit.word}" → "${hit.suggestion}"` : `删除"${hit.word}"`;
|
|
255
|
-
|
|
259
|
+
// Show surrounding context for each hit
|
|
260
|
+
const pos = hit.positions[0];
|
|
261
|
+
const contextStart = Math.max(0, pos - 10);
|
|
262
|
+
const contextEnd = Math.min(fullText.length, pos + hit.word.length + 10);
|
|
263
|
+
const context = fullText.slice(contextStart, contextEnd).replace(/\n/g, " ");
|
|
264
|
+
fixes.push(` - ${fix} (${hit.category}) — "...${context}..."`);
|
|
256
265
|
}
|
|
257
266
|
}
|
|
258
267
|
if (aiCheck.hasAiTraces) {
|
|
259
268
|
fixes.push(`去 AI 味:${aiCheck.changeCount} 处需要修改`);
|
|
269
|
+
for (const change of aiCheck.changes.slice(0, 5)) {
|
|
270
|
+
fixes.push(` - ${change}`);
|
|
271
|
+
}
|
|
260
272
|
}
|
|
261
273
|
for (const note of qualityScore.notes) {
|
|
262
274
|
fixes.push(note);
|