autocrew 0.3.2 → 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
|
@@ -7,13 +7,12 @@ export interface TikHubResearchQuery {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export async function researchWithTikHub(
|
|
10
|
-
|
|
10
|
+
_query: TikHubResearchQuery,
|
|
11
11
|
): Promise<ResearchItem[]> {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
|
9
|
-
if (!
|
|
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
|
-
|
|
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 ${
|
|
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
|
|
|
@@ -62,7 +62,16 @@ export async function executePipelineOps(params: Record<string, unknown>) {
|
|
|
62
62
|
return { ok: false, error: "Missing 'project' — provide a topic slug to start from." };
|
|
63
63
|
}
|
|
64
64
|
const dir = await startProject(project, dataDir);
|
|
65
|
-
return {
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
action: "start",
|
|
68
|
+
projectDir: dir,
|
|
69
|
+
nextStep:
|
|
70
|
+
"Project structure created with empty draft.md. " +
|
|
71
|
+
"NEXT: Write the content draft and save it using autocrew_content action='save' " +
|
|
72
|
+
"with title and body. The save action handles pipeline integration, version tracking, " +
|
|
73
|
+
"and auto-humanization. Do NOT use the Write tool to edit draft.md directly.",
|
|
74
|
+
};
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
case "advance": {
|
package/src/tools/registry.ts
CHANGED
|
@@ -47,8 +47,12 @@ export function registerAllTools(runner: ToolRunner): void {
|
|
|
47
47
|
name: "autocrew_content",
|
|
48
48
|
label: "AutoCrew Content",
|
|
49
49
|
description:
|
|
50
|
-
"
|
|
51
|
-
"
|
|
50
|
+
"Content creation and lifecycle management. THIS IS THE PRIMARY CONTENT CREATION TOOL. " +
|
|
51
|
+
"To create content: use action='save' with title and body (the full draft text). " +
|
|
52
|
+
"The tool handles pipeline project creation, version tracking, and auto-humanization. " +
|
|
53
|
+
"Workflow: 1) Research with autocrew_intel, 2) Write the full draft body, " +
|
|
54
|
+
"3) Save with autocrew_content action='save' (title, body, platform, hypothesis, tags). " +
|
|
55
|
+
"Other actions: list, get, update, transition, create_variant, siblings, allowed_transitions.",
|
|
52
56
|
parameters: contentSaveSchema,
|
|
53
57
|
execute: executeContentSave,
|
|
54
58
|
});
|
|
@@ -145,8 +149,10 @@ export function registerAllTools(runner: ToolRunner): void {
|
|
|
145
149
|
name: "autocrew_intel",
|
|
146
150
|
label: "AutoCrew 灵感源",
|
|
147
151
|
description:
|
|
148
|
-
"
|
|
149
|
-
"
|
|
152
|
+
"Content research and inspiration pipeline. Use this BEFORE writing content to gather real data, " +
|
|
153
|
+
"case studies, and trends. Actions: pull (collect from web search/RSS/trends — primary research tool), " +
|
|
154
|
+
"list (show saved intel), clean (archive expired), ingest (manually add url/text/memory sources). " +
|
|
155
|
+
"Research results feed into the knowledge wiki and are referenced during content creation.",
|
|
150
156
|
parameters: intelSchema,
|
|
151
157
|
execute: executeIntel,
|
|
152
158
|
});
|
|
@@ -155,7 +161,9 @@ export function registerAllTools(runner: ToolRunner): void {
|
|
|
155
161
|
name: "autocrew_pipeline_ops",
|
|
156
162
|
label: "AutoCrew Pipeline Ops",
|
|
157
163
|
description:
|
|
158
|
-
"Content pipeline lifecycle management. Actions: status (stage counts), start (topic→project
|
|
164
|
+
"Content pipeline lifecycle management. Actions: status (stage counts), start (topic→project — creates project structure, NOT content), " +
|
|
165
|
+
"advance (next stage — requires draft.md with ≥100 chars), version (add draft revision), trash, restore. " +
|
|
166
|
+
"NOTE: 'start' only creates the project folder. To create actual content, use autocrew_content action='save' with the full draft body.",
|
|
159
167
|
parameters: pipelineOpsSchema,
|
|
160
168
|
execute: executePipelineOps,
|
|
161
169
|
});
|