autocrew 0.3.5 → 0.3.6

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.5",
3
+ "version": "0.3.6",
4
4
  "description": "One-person content studio powered by AI — from trending topics to published posts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,23 +1,38 @@
1
1
  import type { CommandDef } from "./index.js";
2
+ import { resolveProjectText } from "./index.js";
2
3
 
3
4
  export const cmd: CommandDef = {
4
5
  name: "humanize",
5
6
  description: "Run Chinese de-AI pass on a content draft",
6
- usage: "autocrew humanize <content-id>",
7
+ usage: "autocrew humanize <content-id-or-project-slug> [--file <path>]",
7
8
  action: async (args, runner) => {
8
- const contentId = args[0];
9
- if (!contentId) {
10
- console.error("Usage: autocrew humanize <content-id>");
9
+ const id = args[0];
10
+ if (!id && !args.includes("--file")) {
11
+ console.error("Usage: autocrew humanize <content-id-or-project-slug> [--file <path>]");
11
12
  process.exitCode = 1;
12
13
  return;
13
14
  }
14
- const result = await runner.execute("autocrew_humanize", { content_id: contentId });
15
+
16
+ const resolved = await resolveProjectText(id, args);
17
+ if (!resolved) {
18
+ console.error(`Not found: "${id}". Provide a content ID, pipeline project slug, or --file <path>.`);
19
+ process.exitCode = 1;
20
+ return;
21
+ }
22
+
23
+ const result = await runner.execute("autocrew_humanize", {
24
+ action: "humanize_zh",
25
+ text: resolved.text,
26
+ });
27
+
15
28
  if (!result.ok) {
16
29
  console.error(`Humanize failed: ${result.error || "unknown error"}`);
17
30
  process.exitCode = 1;
18
31
  return;
19
32
  }
20
- console.log(`De-AI pass complete. Changes: ${result.changeCount || 0}`);
33
+
34
+ console.log(`De-AI pass complete for "${resolved.title}" (${resolved.source})`);
35
+ console.log(`Changes: ${result.changeCount || 0}`);
21
36
  if ((result.changes as any[])?.length > 0) {
22
37
  for (const c of result.changes as string[]) {
23
38
  console.log(` • ${c}`);
@@ -21,6 +21,57 @@ export function getOption(args: string[], flag: string): string | undefined {
21
21
  return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
22
22
  }
23
23
 
24
+ /**
25
+ * Resolve a project identifier to draft text.
26
+ * Supports: legacy content ID, pipeline project slug, or --file path.
27
+ * Returns { text, title, source } or null if not found.
28
+ */
29
+ export async function resolveProjectText(
30
+ idOrSlug: string,
31
+ args: string[],
32
+ ): Promise<{ text: string; title: string; source: string } | null> {
33
+ const filePath = getOption(args, "--file");
34
+
35
+ // --file flag: read directly from file path
36
+ if (filePath) {
37
+ const fs = await import("node:fs/promises");
38
+ try {
39
+ const text = await fs.readFile(filePath, "utf-8");
40
+ const titleMatch = text.match(/^#\s+(.+)/m);
41
+ return { text, title: titleMatch?.[1]?.trim() || filePath, source: `file:${filePath}` };
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ if (!idOrSlug) return null;
48
+
49
+ // Try legacy content ID first
50
+ try {
51
+ const { getContent } = await import("../../storage/local-store.js");
52
+ const content = await getContent(idOrSlug);
53
+ if (content) {
54
+ return { text: content.body, title: content.title, source: `content:${content.id}` };
55
+ }
56
+ } catch { /* ignore */ }
57
+
58
+ // Try pipeline project slug
59
+ try {
60
+ const { findProject } = await import("../../storage/pipeline-store.js");
61
+ const fs = await import("node:fs/promises");
62
+ const path = await import("node:path");
63
+ const found = await findProject(idOrSlug);
64
+ if (found) {
65
+ const draftPath = path.join(found.dir, "draft.md");
66
+ const text = await fs.readFile(draftPath, "utf-8");
67
+ const titleMatch = text.match(/^#\s+(.+)/m);
68
+ return { text, title: titleMatch?.[1]?.trim() || idOrSlug, source: `pipeline:${idOrSlug}` };
69
+ }
70
+ } catch { /* ignore */ }
71
+
72
+ return null;
73
+ }
74
+
24
75
  import { cmd as statusCmd } from "./status.js";
25
76
  import { cmd as topicsCmd } from "./topics.js";
26
77
  import { cmd as contentsCmd } from "./contents.js";
@@ -1,20 +1,40 @@
1
1
  import type { CommandDef } from "./index.js";
2
+ import { resolveProjectText } from "./index.js";
2
3
 
3
4
  export const cmd: CommandDef = {
4
5
  name: "pre-publish",
5
6
  description: "Run pre-publish checklist: 6 checks before allowing publish",
6
- usage: "autocrew pre-publish <content-id>",
7
+ usage: "autocrew pre-publish <content-id-or-project-slug> [--file <path>]",
7
8
  action: async (args, runner) => {
8
- const contentId = args[0];
9
- if (!contentId) {
10
- console.error("Usage: autocrew pre-publish <content-id>");
9
+ const id = args[0];
10
+ if (!id && !args.includes("--file")) {
11
+ console.error("Usage: autocrew pre-publish <id-or-slug> [--file <path>]");
11
12
  process.exitCode = 1;
12
13
  return;
13
14
  }
14
- const result = await runner.execute("autocrew_pre_publish", {
15
- action: "check",
16
- content_id: contentId,
17
- }) as any;
15
+
16
+ const resolved = await resolveProjectText(id, args);
17
+ if (!resolved) {
18
+ console.error(`Not found: "${id}". Provide a content ID, pipeline project slug, or --file <path>.`);
19
+ process.exitCode = 1;
20
+ return;
21
+ }
22
+
23
+ // Try with content_id first (legacy), fall back to text-based check
24
+ let result: any;
25
+ if (resolved.source.startsWith("content:")) {
26
+ const contentId = resolved.source.replace("content:", "");
27
+ result = await runner.execute("autocrew_pre_publish", {
28
+ action: "check",
29
+ content_id: contentId,
30
+ });
31
+ } else {
32
+ // Pipeline project or file — use text-based review as proxy
33
+ result = await runner.execute("autocrew_review", {
34
+ action: "full_review",
35
+ text: resolved.text,
36
+ });
37
+ }
18
38
 
19
39
  if (!result.ok) {
20
40
  console.error(`Pre-publish check failed: ${result.error || "unknown error"}`);
@@ -22,6 +42,7 @@ export const cmd: CommandDef = {
22
42
  return;
23
43
  }
24
44
 
25
- console.log(result.summary);
45
+ console.log(`Pre-publish check for "${resolved.title}" (${resolved.source}):`);
46
+ console.log(result.summary || "Check complete.");
26
47
  },
27
48
  };
@@ -1,14 +1,14 @@
1
1
  import type { CommandDef } from "./index.js";
2
- import { getOption } from "./index.js";
2
+ import { getOption, resolveProjectText } from "./index.js";
3
3
 
4
4
  export const cmd: CommandDef = {
5
5
  name: "review",
6
- description: "Run full content review or auto-fix (use --fix for auto-fix mode)",
7
- usage: "autocrew review <content-id> [--platform <platform>] [--fix]",
6
+ description: "Run full content review or auto-fix",
7
+ usage: "autocrew review <content-id-or-project-slug> [--platform <p>] [--fix] [--file <path>]",
8
8
  action: async (args, runner) => {
9
- const contentId = args[0];
10
- if (!contentId) {
11
- console.error("Usage: autocrew review <content-id> [--platform <platform>] [--fix]");
9
+ const id = args[0];
10
+ if (!id && !args.includes("--file")) {
11
+ console.error("Usage: autocrew review <id-or-slug> [--platform <p>] [--fix] [--file <path>]");
12
12
  process.exitCode = 1;
13
13
  return;
14
14
  }
@@ -16,11 +16,17 @@ export const cmd: CommandDef = {
16
16
  const platform = getOption(args, "--platform");
17
17
  const isFix = args.includes("--fix");
18
18
 
19
+ const resolved = await resolveProjectText(id, args);
20
+ if (!resolved) {
21
+ console.error(`Not found: "${id}". Provide a content ID, pipeline project slug, or --file <path>.`);
22
+ process.exitCode = 1;
23
+ return;
24
+ }
25
+
19
26
  if (isFix) {
20
- // auto-fix mode
21
27
  const result = await runner.execute("autocrew_review", {
22
28
  action: "auto_fix",
23
- content_id: contentId,
29
+ text: resolved.text,
24
30
  platform,
25
31
  });
26
32
 
@@ -30,17 +36,15 @@ export const cmd: CommandDef = {
30
36
  return;
31
37
  }
32
38
 
33
- console.log(`Auto-fix complete for ${contentId}.`);
39
+ console.log(`Auto-fix complete for "${resolved.title}" (${resolved.source})`);
34
40
  console.log(` Sensitive words fixed: ${result.sensitiveWordsFixed || 0}`);
35
41
  console.log(` AI traces fixed: ${result.aiFixesApplied || 0}`);
36
- console.log(` Saved: ${result.saved ? "yes" : "no"}`);
37
42
  return;
38
43
  }
39
44
 
40
- // full review mode
41
45
  const result = await runner.execute("autocrew_review", {
42
46
  action: "full_review",
43
- content_id: contentId,
47
+ text: resolved.text,
44
48
  platform,
45
49
  }) as any;
46
50
 
@@ -50,6 +54,7 @@ export const cmd: CommandDef = {
50
54
  return;
51
55
  }
52
56
 
57
+ console.log(`Review for "${resolved.title}" (${resolved.source}):`);
53
58
  console.log(result.summary);
54
59
  if (result.fixes?.length > 0) {
55
60
  console.log("\nSuggested fixes:");