autocrew 0.3.3 → 0.3.4

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.3",
3
+ "version": "0.3.4",
4
4
  "description": "One-person content studio powered by AI — from trending topics to published posts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -7,13 +7,12 @@ export interface TikHubResearchQuery {
7
7
  }
8
8
 
9
9
  export async function researchWithTikHub(
10
- query: TikHubResearchQuery,
10
+ _query: TikHubResearchQuery,
11
11
  ): Promise<ResearchItem[]> {
12
- const limit = query.limit || 5;
13
- return Array.from({ length: limit }).map((_, index) => ({
14
- title: `${query.keyword} API 候选 ${index + 1}`,
15
- summary: "TikHub fallback placeholder. Replace with a real provider call only when browser-first mode is unavailable.",
16
- platform: query.platform,
17
- source: "api_provider",
18
- }));
12
+ // TikHub adapter is not implemented — return empty to trigger proper fallback.
13
+ // Previously returned fake placeholder items ("API 候选 1/2/3") which polluted
14
+ // the topic pool with useless entries. Empty array forces research.ts to fall
15
+ // through to free engine or return an actionable error with suggestion to use
16
+ // autocrew_intel pull instead.
17
+ return [];
19
18
  }
@@ -2,17 +2,34 @@ import type { CommandDef } from "./index.js";
2
2
 
3
3
  export const cmd: CommandDef = {
4
4
  name: "contents",
5
- description: "List content items",
5
+ description: "List content items (legacy + pipeline projects)",
6
6
  usage: "autocrew contents",
7
7
  action: async (_args, runner) => {
8
8
  const result = await runner.execute("autocrew_content", { action: "list" });
9
- const items = (result.items || []) as any[];
10
- if (items.length === 0) {
11
- console.log("No content yet. Use 'autocrew_content' tool to save drafts.");
9
+
10
+ // Legacy contents (local-store)
11
+ const contents = (result.contents || result.items || []) as any[];
12
+ // Pipeline projects (drafting/production/published)
13
+ const pipelineProjects = (result.pipelineProjects || []) as any[];
14
+
15
+ if (contents.length === 0 && pipelineProjects.length === 0) {
16
+ console.log("No content yet. Use autocrew_content action='save' to create drafts.");
12
17
  return;
13
18
  }
14
- for (const c of items) {
15
- console.log(`[${c.id}] ${c.title} ${c.status} (${c.platform || "general"})`);
19
+
20
+ if (contents.length > 0) {
21
+ console.log("Legacy contents:");
22
+ for (const c of contents) {
23
+ console.log(` [${c.id}] ${c.title} — ${c.status} (${c.platform || "general"})`);
24
+ }
25
+ }
26
+
27
+ if (pipelineProjects.length > 0) {
28
+ if (contents.length > 0) console.log("");
29
+ console.log("Pipeline projects:");
30
+ for (const p of pipelineProjects) {
31
+ console.log(` [${p.slug}] ${p.title} — ${p.stage} (${p.current})`);
32
+ }
16
33
  }
17
34
  },
18
35
  };
@@ -2,21 +2,49 @@ import type { CommandDef } from "./index.js";
2
2
 
3
3
  export const cmd: CommandDef = {
4
4
  name: "versions",
5
- description: "List version history for a content project",
6
- usage: "autocrew versions <content-id>",
5
+ description: "List version history for a content project (draft versions + asset versions)",
6
+ usage: "autocrew versions <project-slug-or-content-id>",
7
7
  action: async (args, runner) => {
8
- const contentId = args[0];
9
- if (!contentId) {
10
- console.error("Usage: autocrew versions <content-id>");
8
+ const id = args[0];
9
+ if (!id) {
10
+ console.error("Usage: autocrew versions <project-slug-or-content-id>");
11
11
  process.exitCode = 1;
12
12
  return;
13
13
  }
14
- const result = await runner.execute("autocrew_asset", { action: "versions", content_id: contentId });
14
+
15
+ // Try pipeline project first (draft versions from meta.yaml)
16
+ try {
17
+ const pipelineResult = await runner.execute("autocrew_pipeline_ops", {
18
+ action: "status",
19
+ });
20
+ // Search for project in pipeline stages
21
+ const { getProjectMeta } = await import("../../storage/pipeline-store.js");
22
+ const meta = await getProjectMeta(id);
23
+ if (meta) {
24
+ console.log(`Draft versions for "${meta.title}":`);
25
+ console.log(` Current: ${meta.current}`);
26
+ if (meta.versions.length === 0) {
27
+ console.log(" No revision history yet (initial draft only).");
28
+ } else {
29
+ for (const v of meta.versions) {
30
+ console.log(` ${v.file} — ${v.note} (${v.createdAt})`);
31
+ }
32
+ }
33
+ console.log(` Stage: ${meta.history.at(-1)?.stage ?? "unknown"}`);
34
+ return;
35
+ }
36
+ } catch {
37
+ // Pipeline store may not be available
38
+ }
39
+
40
+ // Fallback to asset versions (legacy)
41
+ const result = await runner.execute("autocrew_asset", { action: "versions", content_id: id });
15
42
  const versions = (result.versions || []) as any[];
16
43
  if (versions.length === 0) {
17
- console.log(`No versions for ${contentId}.`);
44
+ console.log(`No versions found for "${id}". Check the project slug or content ID.`);
18
45
  return;
19
46
  }
47
+ console.log(`Asset versions for ${id}:`);
20
48
  for (const v of versions) {
21
49
  console.log(` v${v.version} — ${v.note || "no note"} (${v.savedAt})`);
22
50
  }
package/src/e2e.test.ts CHANGED
@@ -181,6 +181,66 @@ describe("E2E: Topic Management", () => {
181
181
  });
182
182
  });
183
183
 
184
+ describe("E2E: Topic → Start → Content (cross-system)", () => {
185
+ it("topic created via autocrew_topic can be started via pipeline_ops", async () => {
186
+ // Create topic via local-store (autocrew_topic)
187
+ const topicResult = await runner.execute("autocrew_topic", {
188
+ action: "create",
189
+ title: "端到端测试选题",
190
+ description: "测试从 topic 到 project 的完整流程",
191
+ tags: ["test"],
192
+ });
193
+ expect(topicResult.ok).toBe(true);
194
+ const topicId = (topicResult.topic as any).id;
195
+ expect(topicId).toBeDefined();
196
+
197
+ // Start project via pipeline-store (should find the legacy topic)
198
+ const startResult = await runner.execute("autocrew_pipeline_ops", {
199
+ action: "start",
200
+ project: topicId,
201
+ });
202
+ expect(startResult.ok).toBe(true);
203
+ expect(startResult.projectDir).toBeDefined();
204
+ // Should return next step guidance
205
+ expect(startResult.nextStep).toBeDefined();
206
+ expect((startResult.nextStep as string)).toContain("autocrew_content");
207
+ });
208
+
209
+ it("content list returns both legacy contents and pipeline projects", async () => {
210
+ const result = await runner.execute("autocrew_content", { action: "list" });
211
+ expect(result.ok).toBe(true);
212
+ // Should have legacy contents from earlier tests
213
+ expect(result.contents).toBeDefined();
214
+ // Should have pipeline projects from start test above
215
+ expect(result.pipelineProjects).toBeDefined();
216
+ const projects = result.pipelineProjects as any[];
217
+ expect(projects.length).toBeGreaterThanOrEqual(1);
218
+ });
219
+
220
+ it("research auto mode does not return placeholder topics", async () => {
221
+ const result = await runner.execute("autocrew_research", {
222
+ action: "discover",
223
+ keyword: "测试关键词",
224
+ mode: "auto",
225
+ topic_count: 3,
226
+ save_topics: false,
227
+ });
228
+ // Should either return real results or a proper error — never fake placeholders
229
+ if (result.ok) {
230
+ const topics = (result.topics || []) as any[];
231
+ for (const t of topics) {
232
+ // No topic should contain "API 候选" placeholder text
233
+ expect(t.title).not.toContain("API 候选");
234
+ expect(t.title).not.toContain("候选");
235
+ }
236
+ } else {
237
+ // Error is acceptable — it means no source worked, but at least it's honest
238
+ expect(result.error).toBeDefined();
239
+ expect(result.suggestion).toBeDefined();
240
+ }
241
+ });
242
+ });
243
+
184
244
  describe("E2E: Pipeline & Workflow", () => {
185
245
  let workflowId: string;
186
246