autoblogger 0.2.20 → 0.2.22

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/dist/cli/index.js CHANGED
@@ -446,6 +446,17 @@ async function promptInit(options) {
446
446
  message: "Run database migration after setup?",
447
447
  initial: true
448
448
  });
449
+ questions.push({
450
+ type: "select",
451
+ name: "deploymentPlatform",
452
+ message: "Where will you deploy? (for RSS auto-draft scheduling)",
453
+ choices: [
454
+ { title: "Vercel (serverless)", value: "vercel", description: "Creates API route + vercel.json cron" },
455
+ { title: "Server (VPS/Docker)", value: "server", description: "Creates cron script for crontab" },
456
+ { title: "Skip for now", value: "skip", description: "Set up auto-draft later" }
457
+ ],
458
+ initial: 0
459
+ });
449
460
  if (options.contentPaths.length > 0) {
450
461
  const contentSummary = options.contentPaths.map((p) => `${p} (${options.contentCounts[p]} files)`).join(", ");
451
462
  console.log(import_picocolors.default.cyan(`
@@ -478,7 +489,8 @@ Found existing content: ${contentSummary}`));
478
489
  dbProvider: answers.dbProvider || "postgresql",
479
490
  runMigration: answers.runMigration ?? true,
480
491
  importContent: answers.importContent ?? false,
481
- importPath: answers.importPath || options.contentPaths[0]
492
+ importPath: answers.importPath || options.contentPaths[0],
493
+ deploymentPlatform: answers.deploymentPlatform || "vercel"
482
494
  };
483
495
  }
484
496
  async function confirm(message, initial = true) {
@@ -632,6 +644,121 @@ var WRITER_LAYOUT_TEMPLATE = `export default function WriterLayout({
632
644
  }
633
645
  `;
634
646
 
647
+ // src/cli/templates/auto-draft-script.ts
648
+ var AUTO_DRAFT_SCRIPT_TEMPLATE = `/**
649
+ * Auto-Draft Cron Script
650
+ *
651
+ * Fetches RSS feeds for active topic subscriptions and generates essay drafts.
652
+ * Run with: npx tsx --env-file=.env.local scripts/auto-draft.ts
653
+ *
654
+ * Schedule via cron:
655
+ * 0 6 * * * cd /path/to/project && npx tsx scripts/auto-draft.ts >> /var/log/auto-draft.log 2>&1
656
+ */
657
+
658
+ import 'dotenv/config'
659
+ import { cms } from '../lib/cms'
660
+
661
+ async function main() {
662
+ const startTime = new Date()
663
+ console.log(\`[\${startTime.toISOString()}] Starting auto-draft...\`)
664
+
665
+ try {
666
+ const results = await cms.autoDraft.run()
667
+
668
+ if (results.length === 0) {
669
+ console.log(' No active topics to process.')
670
+ } else {
671
+ let totalGenerated = 0
672
+ let totalSkipped = 0
673
+
674
+ for (const r of results) {
675
+ console.log(\` \${r.topicName}: generated \${r.generated}, skipped \${r.skipped}\`)
676
+ totalGenerated += r.generated
677
+ totalSkipped += r.skipped
678
+ }
679
+
680
+ console.log(' ---')
681
+ console.log(\` Total: \${totalGenerated} essays generated, \${totalSkipped} articles skipped\`)
682
+ }
683
+
684
+ const endTime = new Date()
685
+ const duration = (endTime.getTime() - startTime.getTime()) / 1000
686
+ console.log(\`[\${endTime.toISOString()}] Done in \${duration.toFixed(1)}s\`)
687
+
688
+ process.exit(0)
689
+ } catch (error) {
690
+ console.error('Auto-draft failed:', error)
691
+ process.exit(1)
692
+ }
693
+ }
694
+
695
+ main()
696
+ `;
697
+
698
+ // src/cli/templates/vercel-cron-route.ts
699
+ var VERCEL_CRON_ROUTE_TEMPLATE = `import { NextResponse } from 'next/server'
700
+ import { cms } from '@/lib/cms'
701
+
702
+ /**
703
+ * Vercel Cron endpoint for RSS auto-draft generation.
704
+ *
705
+ * Triggered by Vercel Cron on schedule defined in vercel.json.
706
+ * Fetches RSS feeds for active topic subscriptions and generates essay drafts.
707
+ *
708
+ * To test manually:
709
+ * curl -H "Authorization: Bearer YOUR_CRON_SECRET" http://localhost:3000/api/cron/auto-draft
710
+ */
711
+ export async function GET(request: Request) {
712
+ // Verify the request is from Vercel Cron (security)
713
+ const authHeader = request.headers.get('authorization')
714
+ if (authHeader !== \`Bearer \${process.env.CRON_SECRET}\`) {
715
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
716
+ }
717
+
718
+ const startTime = new Date()
719
+ console.log(\`[\${startTime.toISOString()}] Starting auto-draft via Vercel Cron...\`)
720
+
721
+ try {
722
+ const results = await cms.autoDraft.run()
723
+
724
+ if (results.length === 0) {
725
+ console.log(' No active topics to process.')
726
+ } else {
727
+ let totalGenerated = 0
728
+ let totalSkipped = 0
729
+
730
+ for (const r of results) {
731
+ console.log(\` \${r.topicName}: generated \${r.generated}, skipped \${r.skipped}\`)
732
+ totalGenerated += r.generated
733
+ totalSkipped += r.skipped
734
+ }
735
+
736
+ console.log(' ---')
737
+ console.log(\` Total: \${totalGenerated} essays generated, \${totalSkipped} articles skipped\`)
738
+ }
739
+
740
+ const endTime = new Date()
741
+ const duration = (endTime.getTime() - startTime.getTime()) / 1000
742
+ console.log(\`[\${endTime.toISOString()}] Done in \${duration.toFixed(1)}s\`)
743
+
744
+ return NextResponse.json({
745
+ success: true,
746
+ results,
747
+ duration: \`\${duration.toFixed(1)}s\`,
748
+ })
749
+ } catch (error) {
750
+ console.error('Auto-draft failed:', error)
751
+ return NextResponse.json(
752
+ { error: error instanceof Error ? error.message : 'Auto-draft failed' },
753
+ { status: 500 }
754
+ )
755
+ }
756
+ }
757
+
758
+ // Vercel Cron requires a longer timeout for AI generation
759
+ export const maxDuration = 300 // 5 minutes
760
+ `;
761
+
635
762
  // src/cli/import.ts
636
763
  var fs5 = __toESM(require("fs"));
637
764
  var path5 = __toESM(require("path"));
@@ -895,11 +1022,15 @@ async function init(options = {}) {
895
1022
  for (const contentPath of project.contentPaths) {
896
1023
  contentCounts[contentPath] = countMarkdownFiles(path6.join(cwd, contentPath));
897
1024
  }
1025
+ const hasVercelJson = fs6.existsSync(path6.join(cwd, "vercel.json"));
1026
+ const hasVercelDir = fs6.existsSync(path6.join(cwd, ".vercel"));
1027
+ const isLikelyVercel = hasVercelJson || hasVercelDir;
898
1028
  let answers = {
899
1029
  dbProvider: "postgresql",
900
1030
  runMigration: !options.skipMigrate,
901
1031
  importContent: !!options.importPath,
902
- importPath: options.importPath || project.contentPaths[0]
1032
+ importPath: options.importPath || project.contentPaths[0],
1033
+ deploymentPlatform: isLikelyVercel ? "vercel" : "server"
903
1034
  };
904
1035
  if (!options.yes) {
905
1036
  answers = await promptInit({
@@ -929,6 +1060,12 @@ Error: Found conflicting model names in your Prisma schema:`));
929
1060
  console.log(` - ${project.appRouterPath}/api/cms/[...path]/route.ts`);
930
1061
  console.log(` - ${project.appRouterPath}/(writer)/writer/[[...path]]/page.tsx`);
931
1062
  console.log(` - ${project.appRouterPath}/(writer)/layout.tsx`);
1063
+ if (answers.deploymentPlatform === "vercel") {
1064
+ console.log(` - ${project.appRouterPath}/api/cron/auto-draft/route.ts (Vercel Cron endpoint)`);
1065
+ console.log(` - vercel.json (add cron schedule)`);
1066
+ } else if (answers.deploymentPlatform === "server") {
1067
+ console.log(` - scripts/auto-draft.ts (cron script for crontab)`);
1068
+ }
932
1069
  console.log(` - globals.css (add CSS import)`);
933
1070
  console.log(` - ${project.appRouterPath}/layout.tsx (add suppressHydrationWarning, GlobalShortcuts)`);
934
1071
  if (answers.runMigration) {
@@ -999,6 +1136,53 @@ Would import ${count} posts from ${answers.importPath}`);
999
1136
  fs6.writeFileSync(writerLayoutPath, WRITER_LAYOUT_TEMPLATE);
1000
1137
  log("write", `Created ${project.appRouterPath}/(writer)/layout.tsx`);
1001
1138
  }
1139
+ if (answers.deploymentPlatform === "vercel") {
1140
+ const cronRoutePath = path6.join(cwd, project.appRouterPath, "api", "cron", "auto-draft", "route.ts");
1141
+ if (fs6.existsSync(cronRoutePath)) {
1142
+ log("skip", `${project.appRouterPath}/api/cron/auto-draft/route.ts already exists`);
1143
+ } else {
1144
+ const cronRouteDir = path6.dirname(cronRoutePath);
1145
+ if (!fs6.existsSync(cronRouteDir)) {
1146
+ fs6.mkdirSync(cronRouteDir, { recursive: true });
1147
+ }
1148
+ fs6.writeFileSync(cronRoutePath, VERCEL_CRON_ROUTE_TEMPLATE);
1149
+ log("write", `Created ${project.appRouterPath}/api/cron/auto-draft/route.ts`);
1150
+ }
1151
+ const vercelJsonPath = path6.join(cwd, "vercel.json");
1152
+ let vercelConfig = {};
1153
+ if (fs6.existsSync(vercelJsonPath)) {
1154
+ try {
1155
+ vercelConfig = JSON.parse(fs6.readFileSync(vercelJsonPath, "utf-8"));
1156
+ } catch {
1157
+ }
1158
+ }
1159
+ const cronPath = "/api/cron/auto-draft";
1160
+ const existingCron = vercelConfig.crons?.find((c) => c.path === cronPath);
1161
+ if (existingCron) {
1162
+ log("skip", "vercel.json already has auto-draft cron");
1163
+ } else {
1164
+ vercelConfig.crons = vercelConfig.crons || [];
1165
+ vercelConfig.crons.push({
1166
+ path: cronPath,
1167
+ schedule: "0 6 * * *"
1168
+ // Daily at 6am UTC
1169
+ });
1170
+ fs6.writeFileSync(vercelJsonPath, JSON.stringify(vercelConfig, null, 2) + "\n");
1171
+ log("write", "Updated vercel.json with auto-draft cron (daily at 6am UTC)");
1172
+ }
1173
+ } else if (answers.deploymentPlatform === "server") {
1174
+ const scriptsDir = path6.join(cwd, "scripts");
1175
+ const autoDraftScriptPath = path6.join(scriptsDir, "auto-draft.ts");
1176
+ if (fs6.existsSync(autoDraftScriptPath)) {
1177
+ log("skip", "scripts/auto-draft.ts already exists");
1178
+ } else {
1179
+ if (!fs6.existsSync(scriptsDir)) {
1180
+ fs6.mkdirSync(scriptsDir, { recursive: true });
1181
+ }
1182
+ fs6.writeFileSync(autoDraftScriptPath, AUTO_DRAFT_SCRIPT_TEMPLATE);
1183
+ log("write", "Created scripts/auto-draft.ts (schedule via crontab)");
1184
+ }
1185
+ }
1002
1186
  const globalsCssPath = findGlobalsCss(cwd);
1003
1187
  if (globalsCssPath) {
1004
1188
  const cssResult = patchGlobalsCss(globalsCssPath);
@@ -1095,6 +1279,12 @@ Would import ${count} posts from ${answers.importPath}`);
1095
1279
  console.log(import_picocolors3.default.gray(" 1. Update lib/cms.ts with your auth configuration"));
1096
1280
  console.log(import_picocolors3.default.gray(" 2. Add your auth check to app/(writer)/writer/[[...path]]/page.tsx"));
1097
1281
  console.log(import_picocolors3.default.gray(" 3. Set ANTHROPIC_API_KEY and/or OPENAI_API_KEY for AI features"));
1282
+ if (answers.deploymentPlatform === "vercel") {
1283
+ console.log(import_picocolors3.default.gray(" 4. Set CRON_SECRET env var in Vercel for auto-draft security"));
1284
+ } else if (answers.deploymentPlatform === "server") {
1285
+ console.log(import_picocolors3.default.gray(" 4. Schedule scripts/auto-draft.ts via crontab:"));
1286
+ console.log(import_picocolors3.default.gray(" 0 6 * * * cd /path/to/project && npx tsx scripts/auto-draft.ts"));
1287
+ }
1098
1288
  console.log("");
1099
1289
  }
1100
1290
 
package/dist/index.d.mts CHANGED
@@ -689,30 +689,35 @@ declare function buildAutoDraftPrompt(options: {
689
689
  * Placeholders: {{RULES}}, {{STYLE_EXAMPLES}}, {{WORD_COUNT}}
690
690
  */
691
691
  declare const DEFAULT_GENERATE_TEMPLATE = "<system>\n<role>Expert essay writer creating engaging, thoughtful content</role>\n\n<critical>\nALWAYS output a complete essay. NEVER respond conversationally.\n- Do NOT ask questions or request clarification\n- Do NOT say \"Here is your essay\" or similar preamble\n- Do NOT explain what you're going to write\n- If the prompt is vague, make creative choices and proceed\n- Output ONLY the essay in markdown format\n</critical>\n\n<rules>\n{{RULES}}\n</rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<constraints>\n<word_count>{{WORD_COUNT}}</word_count>\n</constraints>\n\n<output_format>\nCRITICAL: Your response MUST start with exactly this format:\n\nLine 1: # [Your Title Here]\nLine 2: *[Your subtitle here]*\nLine 3: (blank line)\nLine 4+: Essay body in markdown\n\n<title_guidelines>\n- Be SPECIFIC, not generic (avoid \"The Power of\", \"Why X Matters\", \"A Guide to\")\n- Include a concrete detail, angle, or unexpected element\n- Create curiosity or make a bold claim\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that hooks the reader\n- Tease the main argument or reveal a key insight\n- Create tension, curiosity, or promise value\n- Make readers want to continue reading\n</subtitle_guidelines>\n</output_format>\n</system>";
692
+
692
693
  /**
693
694
  * Default template for chat interactions.
694
695
  * Placeholders: {{CHAT_RULES}}, {{RULES}}, {{STYLE_EXAMPLES}}, {{ESSAY_CONTEXT}}
695
696
  */
696
697
  declare const DEFAULT_CHAT_TEMPLATE = "<system>\n<role>Helpful writing assistant for essay creation and editing</role>\n\n<chat_rules>\n{{CHAT_RULES}}\n</chat_rules>\n\n<writing_rules>\n{{RULES}}\n</writing_rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<context>\n{{ESSAY_CONTEXT}}\n</context>\n\n<behavior>\n- Be concise and actionable\n- When suggesting edits, be specific about what to change\n- Match the author's voice and style when writing\n- Ask clarifying questions if the request is ambiguous\n</behavior>\n</system>";
698
+
697
699
  /**
698
700
  * Default template for text rewriting.
699
701
  * Placeholders: {{REWRITE_RULES}}, {{RULES}}, {{STYLE_EXAMPLES}}
700
702
  */
701
703
  declare const DEFAULT_REWRITE_TEMPLATE = "<system>\n<role>Writing assistant that improves text quality</role>\n\n<rewrite_rules>\n{{REWRITE_RULES}}\n</rewrite_rules>\n\n<writing_rules>\n{{RULES}}\n</writing_rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<behavior>\n- Preserve the original meaning exactly\n- Improve clarity, flow, and readability\n- Fix grammar and punctuation issues\n- Maintain the author's voice and tone\n- Output only the improved text, no explanations\n</behavior>\n</system>";
704
+
702
705
  /**
703
706
  * Default template for auto-drafting from news articles.
704
707
  * Placeholders: {{AUTO_DRAFT_RULES}}, {{RULES}}, {{STYLE_EXAMPLES}}, {{TOPIC_NAME}}, {{ARTICLE_TITLE}}, {{ARTICLE_SUMMARY}}, {{ARTICLE_URL}}, {{AUTO_DRAFT_WORD_COUNT}}
705
708
  */
706
709
  declare const DEFAULT_AUTO_DRAFT_TEMPLATE = "<system>\n<role>Expert essay writer creating engaging content from news articles</role>\n\n<auto_draft_rules>\n{{AUTO_DRAFT_RULES}}\n</auto_draft_rules>\n\n<writing_rules>\n{{RULES}}\n</writing_rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<source_article>\n<topic>{{TOPIC_NAME}}</topic>\n<title>{{ARTICLE_TITLE}}</title>\n<summary>{{ARTICLE_SUMMARY}}</summary>\n<url>{{ARTICLE_URL}}</url>\n</source_article>\n\n<constraints>\n<word_count>{{AUTO_DRAFT_WORD_COUNT}}</word_count>\n</constraints>\n\n<output_format>\nCRITICAL: Your response MUST start with exactly this format:\n\nLine 1: # [Your Title Here]\nLine 2: *[Your subtitle here]*\nLine 3: (blank line)\nLine 4+: Essay body in markdown\n\n<title_guidelines>\n- Be SPECIFIC about the news angle, not generic\n- Include a concrete detail or unexpected element\n- Create curiosity or make a bold claim\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that hooks the reader\n- Tease the main argument or unique perspective\n- Create tension, curiosity, or promise value\n</subtitle_guidelines>\n</output_format>\n</system>";
710
+
707
711
  /**
708
712
  * Default template for essay outline generation.
709
713
  * Placeholders: {{PLAN_RULES}}, {{STYLE_EXAMPLES}}
710
714
  */
711
- declare const DEFAULT_PLAN_TEMPLATE = "<system>\n<role>Writing assistant that creates essay outlines</role>\n\n<critical>\nWrap your ENTIRE response in <plan> tags. Output NOTHING outside the tags.\n</critical>\n\n<rules>\n{{PLAN_RULES}}\n</rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n</system>";
715
+ declare const DEFAULT_PLAN_TEMPLATE = "<system>\n<role>Essay outline generator - you ONLY output plans, never conversation</role>\n\n<critical>\nYOU ARE A PLAN GENERATOR. Every response must be a complete essay outline.\n\nABSOLUTE RULES:\n1. ALWAYS output a plan wrapped in <plan> tags\n2. NEVER have conversational responses outside the plan\n3. If user asks a question \u2192 answer by generating/revising a plan\n4. If user gives feedback \u2192 output the revised plan\n5. If user gives a topic \u2192 output a new plan for that topic\n6. Your ENTIRE response is ONLY the <plan>...</plan> block\n\nNO EXCEPTIONS. Every message you send is a plan.\n</critical>\n\n<rules>\n{{PLAN_RULES}}\n</rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n</system>";
712
716
  /**
713
717
  * Default rules for plan generation format.
714
718
  */
715
- declare const DEFAULT_PLAN_RULES = "<format>\nSTRICT LIMIT: Maximum 3 bullets per section. Most sections should have 1-2 bullets.\n\n<plan>\n# Essay Title\n*One-line subtitle*\n\n## Section Name\n- Key point\n\n## Section Name\n- Key point\n- Another point\n\n## Section Name\n- Key point\n</plan>\n</format>\n\n<constraints>\n- 4-6 section headings (## lines)\n- 1-3 bullets per section \u2014 NEVER 4 or more\n- Bullets are short phrases, not sentences\n- No prose, no paragraphs, no explanations\n- When revising, output the complete updated plan\n</constraints>\n\n<title_guidelines>\n- Be SPECIFIC about the essay's angle\n- Include a concrete detail or unexpected element\n- Avoid generic patterns like \"The Power of\", \"Why X Matters\"\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that previews the main argument\n- Create curiosity or make a bold claim\n</subtitle_guidelines>";
719
+ declare const DEFAULT_PLAN_RULES = "<format>\nEXACT OUTPUT FORMAT - copy this structure precisely:\n\n<plan>\n# Essay Title\n*One-line subtitle*\n\n## Section Name\n- Key point\n\n## Section Name\n- Key point\n- Another point\n\n## Section Name\n- Key point\n</plan>\n</format>\n\n<syntax_requirements>\n- Title: \"# \" (hash + space) then title text\n- Subtitle: \"*\" + text + \"*\" (asterisks for italics)\n- Sections: \"## \" (double hash + space) then section name \n- Points: \"- \" (dash + space) then point text\n</syntax_requirements>\n\n<constraints>\n- 4-6 section headings (## lines)\n- 1-3 bullets per section \u2014 NEVER 4 or more\n- Bullets are short phrases, not full sentences\n- NO prose, NO paragraphs, NO explanations outside the plan\n- When revising, output the COMPLETE updated plan\n- NEVER output anything outside the <plan> tags\n</constraints>\n\n<title_guidelines>\n- Be SPECIFIC about the essay's angle\n- Include a concrete detail or unexpected element\n- Avoid generic patterns like \"The Power of\", \"Why X Matters\"\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that previews the main argument\n- Create curiosity or make a bold claim\n</subtitle_guidelines>";
720
+
716
721
  /**
717
722
  * Default template for expanding outlines into full essays.
718
723
  * Placeholders: {{RULES}}, {{STYLE_EXAMPLES}}, {{PLAN}}
package/dist/index.d.ts CHANGED
@@ -689,30 +689,35 @@ declare function buildAutoDraftPrompt(options: {
689
689
  * Placeholders: {{RULES}}, {{STYLE_EXAMPLES}}, {{WORD_COUNT}}
690
690
  */
691
691
  declare const DEFAULT_GENERATE_TEMPLATE = "<system>\n<role>Expert essay writer creating engaging, thoughtful content</role>\n\n<critical>\nALWAYS output a complete essay. NEVER respond conversationally.\n- Do NOT ask questions or request clarification\n- Do NOT say \"Here is your essay\" or similar preamble\n- Do NOT explain what you're going to write\n- If the prompt is vague, make creative choices and proceed\n- Output ONLY the essay in markdown format\n</critical>\n\n<rules>\n{{RULES}}\n</rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<constraints>\n<word_count>{{WORD_COUNT}}</word_count>\n</constraints>\n\n<output_format>\nCRITICAL: Your response MUST start with exactly this format:\n\nLine 1: # [Your Title Here]\nLine 2: *[Your subtitle here]*\nLine 3: (blank line)\nLine 4+: Essay body in markdown\n\n<title_guidelines>\n- Be SPECIFIC, not generic (avoid \"The Power of\", \"Why X Matters\", \"A Guide to\")\n- Include a concrete detail, angle, or unexpected element\n- Create curiosity or make a bold claim\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that hooks the reader\n- Tease the main argument or reveal a key insight\n- Create tension, curiosity, or promise value\n- Make readers want to continue reading\n</subtitle_guidelines>\n</output_format>\n</system>";
692
+
692
693
  /**
693
694
  * Default template for chat interactions.
694
695
  * Placeholders: {{CHAT_RULES}}, {{RULES}}, {{STYLE_EXAMPLES}}, {{ESSAY_CONTEXT}}
695
696
  */
696
697
  declare const DEFAULT_CHAT_TEMPLATE = "<system>\n<role>Helpful writing assistant for essay creation and editing</role>\n\n<chat_rules>\n{{CHAT_RULES}}\n</chat_rules>\n\n<writing_rules>\n{{RULES}}\n</writing_rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<context>\n{{ESSAY_CONTEXT}}\n</context>\n\n<behavior>\n- Be concise and actionable\n- When suggesting edits, be specific about what to change\n- Match the author's voice and style when writing\n- Ask clarifying questions if the request is ambiguous\n</behavior>\n</system>";
698
+
697
699
  /**
698
700
  * Default template for text rewriting.
699
701
  * Placeholders: {{REWRITE_RULES}}, {{RULES}}, {{STYLE_EXAMPLES}}
700
702
  */
701
703
  declare const DEFAULT_REWRITE_TEMPLATE = "<system>\n<role>Writing assistant that improves text quality</role>\n\n<rewrite_rules>\n{{REWRITE_RULES}}\n</rewrite_rules>\n\n<writing_rules>\n{{RULES}}\n</writing_rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<behavior>\n- Preserve the original meaning exactly\n- Improve clarity, flow, and readability\n- Fix grammar and punctuation issues\n- Maintain the author's voice and tone\n- Output only the improved text, no explanations\n</behavior>\n</system>";
704
+
702
705
  /**
703
706
  * Default template for auto-drafting from news articles.
704
707
  * Placeholders: {{AUTO_DRAFT_RULES}}, {{RULES}}, {{STYLE_EXAMPLES}}, {{TOPIC_NAME}}, {{ARTICLE_TITLE}}, {{ARTICLE_SUMMARY}}, {{ARTICLE_URL}}, {{AUTO_DRAFT_WORD_COUNT}}
705
708
  */
706
709
  declare const DEFAULT_AUTO_DRAFT_TEMPLATE = "<system>\n<role>Expert essay writer creating engaging content from news articles</role>\n\n<auto_draft_rules>\n{{AUTO_DRAFT_RULES}}\n</auto_draft_rules>\n\n<writing_rules>\n{{RULES}}\n</writing_rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n\n<source_article>\n<topic>{{TOPIC_NAME}}</topic>\n<title>{{ARTICLE_TITLE}}</title>\n<summary>{{ARTICLE_SUMMARY}}</summary>\n<url>{{ARTICLE_URL}}</url>\n</source_article>\n\n<constraints>\n<word_count>{{AUTO_DRAFT_WORD_COUNT}}</word_count>\n</constraints>\n\n<output_format>\nCRITICAL: Your response MUST start with exactly this format:\n\nLine 1: # [Your Title Here]\nLine 2: *[Your subtitle here]*\nLine 3: (blank line)\nLine 4+: Essay body in markdown\n\n<title_guidelines>\n- Be SPECIFIC about the news angle, not generic\n- Include a concrete detail or unexpected element\n- Create curiosity or make a bold claim\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that hooks the reader\n- Tease the main argument or unique perspective\n- Create tension, curiosity, or promise value\n</subtitle_guidelines>\n</output_format>\n</system>";
710
+
707
711
  /**
708
712
  * Default template for essay outline generation.
709
713
  * Placeholders: {{PLAN_RULES}}, {{STYLE_EXAMPLES}}
710
714
  */
711
- declare const DEFAULT_PLAN_TEMPLATE = "<system>\n<role>Writing assistant that creates essay outlines</role>\n\n<critical>\nWrap your ENTIRE response in <plan> tags. Output NOTHING outside the tags.\n</critical>\n\n<rules>\n{{PLAN_RULES}}\n</rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n</system>";
715
+ declare const DEFAULT_PLAN_TEMPLATE = "<system>\n<role>Essay outline generator - you ONLY output plans, never conversation</role>\n\n<critical>\nYOU ARE A PLAN GENERATOR. Every response must be a complete essay outline.\n\nABSOLUTE RULES:\n1. ALWAYS output a plan wrapped in <plan> tags\n2. NEVER have conversational responses outside the plan\n3. If user asks a question \u2192 answer by generating/revising a plan\n4. If user gives feedback \u2192 output the revised plan\n5. If user gives a topic \u2192 output a new plan for that topic\n6. Your ENTIRE response is ONLY the <plan>...</plan> block\n\nNO EXCEPTIONS. Every message you send is a plan.\n</critical>\n\n<rules>\n{{PLAN_RULES}}\n</rules>\n\n<style_reference>\n{{STYLE_EXAMPLES}}\n</style_reference>\n</system>";
712
716
  /**
713
717
  * Default rules for plan generation format.
714
718
  */
715
- declare const DEFAULT_PLAN_RULES = "<format>\nSTRICT LIMIT: Maximum 3 bullets per section. Most sections should have 1-2 bullets.\n\n<plan>\n# Essay Title\n*One-line subtitle*\n\n## Section Name\n- Key point\n\n## Section Name\n- Key point\n- Another point\n\n## Section Name\n- Key point\n</plan>\n</format>\n\n<constraints>\n- 4-6 section headings (## lines)\n- 1-3 bullets per section \u2014 NEVER 4 or more\n- Bullets are short phrases, not sentences\n- No prose, no paragraphs, no explanations\n- When revising, output the complete updated plan\n</constraints>\n\n<title_guidelines>\n- Be SPECIFIC about the essay's angle\n- Include a concrete detail or unexpected element\n- Avoid generic patterns like \"The Power of\", \"Why X Matters\"\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that previews the main argument\n- Create curiosity or make a bold claim\n</subtitle_guidelines>";
719
+ declare const DEFAULT_PLAN_RULES = "<format>\nEXACT OUTPUT FORMAT - copy this structure precisely:\n\n<plan>\n# Essay Title\n*One-line subtitle*\n\n## Section Name\n- Key point\n\n## Section Name\n- Key point\n- Another point\n\n## Section Name\n- Key point\n</plan>\n</format>\n\n<syntax_requirements>\n- Title: \"# \" (hash + space) then title text\n- Subtitle: \"*\" + text + \"*\" (asterisks for italics)\n- Sections: \"## \" (double hash + space) then section name \n- Points: \"- \" (dash + space) then point text\n</syntax_requirements>\n\n<constraints>\n- 4-6 section headings (## lines)\n- 1-3 bullets per section \u2014 NEVER 4 or more\n- Bullets are short phrases, not full sentences\n- NO prose, NO paragraphs, NO explanations outside the plan\n- When revising, output the COMPLETE updated plan\n- NEVER output anything outside the <plan> tags\n</constraints>\n\n<title_guidelines>\n- Be SPECIFIC about the essay's angle\n- Include a concrete detail or unexpected element\n- Avoid generic patterns like \"The Power of\", \"Why X Matters\"\n- 5-12 words ideal\n</title_guidelines>\n\n<subtitle_guidelines>\n- One sentence that previews the main argument\n- Create curiosity or make a bold claim\n</subtitle_guidelines>";
720
+
716
721
  /**
717
722
  * Default template for expanding outlines into full essays.
718
723
  * Placeholders: {{RULES}}, {{STYLE_EXAMPLES}}, {{PLAN}}