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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autocrew",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "One-person content studio powered by AI — from trending topics to published posts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -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 <content-id-or-project-slug> [--platform <p>] [--fix] [--file <path>]",
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(`Auto-fix complete for "${resolved.title}" — saved back to ${filePath}`);
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(`Auto-fix complete for "${resolved.title}" — saved back to draft.md`);
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
 
@@ -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, 5)) {
257
+ for (const hit of sensitiveWords.hits.slice(0, 10)) {
254
258
  const fix = hit.suggestion ? `"${hit.word}" → "${hit.suggestion}"` : `删除"${hit.word}"`;
255
- fixes.push(` - ${fix} (${hit.category})`);
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);