autocrew 0.3.0 → 0.3.1

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.0",
3
+ "version": "0.3.1",
4
4
  "description": "One-person content studio powered by AI — from trending topics to published posts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -665,21 +665,87 @@ export async function startProject(
665
665
  dataDir?: string,
666
666
  ): Promise<string> {
667
667
  await initPipeline(dataDir);
668
- const topicsDir = stagePath("topics", dataDir);
668
+ const pipelineTopicsDir = stagePath("topics", dataDir);
669
669
 
670
- // Find the topic file
671
- const files = await fs.readdir(topicsDir);
672
- const topicFile = files.find(
673
- (f) => f.endsWith(".md") && f.includes(topicSlug),
674
- );
675
- if (!topicFile) throw new Error(`Topic not found: ${topicSlug}`);
670
+ // Strategy 1: Find in pipeline topics (markdown files in pipeline/topics/)
671
+ let topicTitle = "";
672
+ let topicDomain = "";
673
+ let topicFormats: string[] = [];
674
+ let topicIntelRefs: string[] = [];
675
+ let topicFile = "";
676
676
 
677
- const content = await fs.readFile(
678
- path.join(topicsDir, topicFile),
679
- "utf-8",
680
- );
681
- const topic = parseTopicFile(content);
682
- const projectName = slugify(topic.title);
677
+ try {
678
+ const files = await fs.readdir(pipelineTopicsDir);
679
+ const found = files.find(
680
+ (f) => f.endsWith(".md") && f.includes(topicSlug),
681
+ );
682
+ if (found) {
683
+ topicFile = found;
684
+ const content = await fs.readFile(
685
+ path.join(pipelineTopicsDir, found),
686
+ "utf-8",
687
+ );
688
+ const topic = parseTopicFile(content);
689
+ topicTitle = topic.title;
690
+ topicDomain = topic.domain;
691
+ topicFormats = topic.formats;
692
+ topicIntelRefs = topic.intelRefs;
693
+ }
694
+ } catch {
695
+ // Pipeline topics dir may not exist
696
+ }
697
+
698
+ // Strategy 2: Fallback to legacy local-store topics (JSON files in topics/)
699
+ if (!topicTitle) {
700
+ const effectiveDataDir = dataDir || path.join(process.env.HOME ?? ".", ".autocrew");
701
+ const legacyTopicsDir = path.join(effectiveDataDir, "topics");
702
+ try {
703
+ const files = await fs.readdir(legacyTopicsDir);
704
+ for (const f of files) {
705
+ if (!f.endsWith(".json")) continue;
706
+ // Match by ID (topic-xxx) or by slug in filename
707
+ if (f.includes(topicSlug) || f === `${topicSlug}.json`) {
708
+ const raw = JSON.parse(
709
+ await fs.readFile(path.join(legacyTopicsDir, f), "utf-8"),
710
+ );
711
+ topicTitle = raw.title || topicSlug;
712
+ topicDomain = raw.domain || "";
713
+ topicFormats = raw.formats || [];
714
+ topicIntelRefs = [];
715
+ topicFile = f;
716
+ break;
717
+ }
718
+ }
719
+ // If still not found, try matching topic title against the slug
720
+ if (!topicTitle) {
721
+ for (const f of files) {
722
+ if (!f.endsWith(".json")) continue;
723
+ const raw = JSON.parse(
724
+ await fs.readFile(path.join(legacyTopicsDir, f), "utf-8"),
725
+ );
726
+ if (raw.title && slugify(raw.title).includes(topicSlug)) {
727
+ topicTitle = raw.title;
728
+ topicDomain = raw.domain || "";
729
+ topicFormats = raw.formats || [];
730
+ topicIntelRefs = [];
731
+ topicFile = f;
732
+ break;
733
+ }
734
+ }
735
+ }
736
+ } catch {
737
+ // Legacy topics dir may not exist
738
+ }
739
+ }
740
+
741
+ if (!topicTitle) {
742
+ throw new Error(
743
+ `Topic not found: "${topicSlug}". Searched in pipeline/topics/ (markdown) ` +
744
+ `and topics/ (JSON). Use autocrew_topic action="list" to see available topics.`,
745
+ );
746
+ }
747
+
748
+ const projectName = slugify(topicTitle);
683
749
 
684
750
  const projectDir = path.join(stagePath("drafting", dataDir), projectName);
685
751
  await fs.mkdir(projectDir, { recursive: true });
@@ -691,12 +757,12 @@ export async function startProject(
691
757
  // draft-v{N}.md = immutable snapshots of content that has been REPLACED by a revision.
692
758
  // On initial create there are no snapshots yet — only draft.md exists.
693
759
  const meta: ProjectMeta = {
694
- title: topic.title,
695
- domain: topic.domain,
696
- format: topic.formats[0] ?? "article",
760
+ title: topicTitle,
761
+ domain: topicDomain,
762
+ format: topicFormats[0] ?? "article",
697
763
  createdAt: now,
698
764
  sourceTopic: topicFile,
699
- intelRefs: topic.intelRefs,
765
+ intelRefs: topicIntelRefs,
700
766
  versions: [],
701
767
  current: "draft.md",
702
768
  history: [{ stage: "drafting", entered: now }],
@@ -706,12 +772,18 @@ export async function startProject(
706
772
  await writeMeta(projectDir, meta);
707
773
  await fs.writeFile(
708
774
  path.join(projectDir, "draft.md"),
709
- `# ${topic.title}\n\n`,
775
+ `# ${topicTitle}\n\n`,
710
776
  "utf-8",
711
777
  );
712
778
 
713
- // Remove topic file
714
- await fs.unlink(path.join(topicsDir, topicFile));
779
+ // Remove topic file from pipeline topics (if it came from there)
780
+ if (topicFile.endsWith(".md")) {
781
+ try {
782
+ await fs.unlink(path.join(pipelineTopicsDir, topicFile));
783
+ } catch {
784
+ // May not exist in pipeline topics dir
785
+ }
786
+ }
715
787
 
716
788
  return projectDir;
717
789
  }