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 +1 -1
- package/src/storage/pipeline-store.ts +92 -20
package/package.json
CHANGED
|
@@ -665,21 +665,87 @@ export async function startProject(
|
|
|
665
665
|
dataDir?: string,
|
|
666
666
|
): Promise<string> {
|
|
667
667
|
await initPipeline(dataDir);
|
|
668
|
-
const
|
|
668
|
+
const pipelineTopicsDir = stagePath("topics", dataDir);
|
|
669
669
|
|
|
670
|
-
// Find
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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:
|
|
695
|
-
domain:
|
|
696
|
-
format:
|
|
760
|
+
title: topicTitle,
|
|
761
|
+
domain: topicDomain,
|
|
762
|
+
format: topicFormats[0] ?? "article",
|
|
697
763
|
createdAt: now,
|
|
698
764
|
sourceTopic: topicFile,
|
|
699
|
-
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
|
-
`# ${
|
|
775
|
+
`# ${topicTitle}\n\n`,
|
|
710
776
|
"utf-8",
|
|
711
777
|
);
|
|
712
778
|
|
|
713
|
-
// Remove topic file
|
|
714
|
-
|
|
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
|
}
|