@@ -22,11 +22,17 @@ interface ObsidianConfig {
2222 plan : string ;
2323}
2424
25+ // --- Bear Integration ---
26+
27+ interface BearConfig {
28+ plan : string ;
29+ }
30+
2531/**
2632 * Extract tags from markdown content using simple heuristics
2733 */
2834function extractTags ( markdown : string ) : string [ ] {
29- const tags = new Set < string > ( [ "plan " ] ) ;
35+ const tags = new Set < string > ( [ "plannotator " ] ) ;
3036
3137 const stopWords = new Set ( [
3238 "the" , "and" , "for" , "with" , "this" , "that" , "from" , "into" ,
@@ -213,6 +219,37 @@ async function saveToObsidian(
213219 }
214220}
215221
222+ /**
223+ * Save plan to Bear using x-callback-url
224+ * Returns { success: boolean, error?: string }
225+ */
226+ async function saveToBear (
227+ config : BearConfig
228+ ) : Promise < { success : boolean ; error ?: string } > {
229+ try {
230+ const { plan } = config ;
231+
232+ // Extract title and tags
233+ const title = extractTitle ( plan ) ;
234+ const tags = extractTags ( plan ) ;
235+ const hashtags = tags . map ( t => `#${ t } ` ) . join ( ' ' ) ;
236+
237+ // Append hashtags to content
238+ const content = `${ plan } \n\n${ hashtags } ` ;
239+
240+ // Build Bear URL
241+ const url = `bear://x-callback-url/create?title=${ encodeURIComponent ( title ) } &text=${ encodeURIComponent ( content ) } &open_note=no` ;
242+
243+ // Open Bear via URL scheme
244+ await $ `open ${ url } ` . quiet ( ) ;
245+
246+ return { success : true } ;
247+ } catch ( err ) {
248+ const message = err instanceof Error ? err . message : "Unknown error" ;
249+ return { success : false , error : message } ;
250+ }
251+ }
252+
216253// Embed the built HTML at compile time
217254import indexHtml from "../dist/index.html" with { type : "text" } ;
218255
@@ -294,12 +331,14 @@ async function startServer(): Promise<ReturnType<typeof Bun.serve>> {
294331
295332 // API: Approve plan
296333 if ( url . pathname === "/api/approve" && req . method === "POST" ) {
297- // Check for Obsidian integration
334+ // Check for note integrations
298335 try {
299336 const body = ( await req . json ( ) . catch ( ( ) => ( { } ) ) ) as {
300337 obsidian ?: ObsidianConfig ;
338+ bear ?: BearConfig ;
301339 } ;
302340
341+ // Obsidian integration
303342 if ( body . obsidian ?. vaultPath && body . obsidian ?. plan ) {
304343 const result = await saveToObsidian ( body . obsidian ) ;
305344 if ( result . success ) {
@@ -308,9 +347,19 @@ async function startServer(): Promise<ReturnType<typeof Bun.serve>> {
308347 console . error ( `[Obsidian] Save failed: ${ result . error } ` ) ;
309348 }
310349 }
350+
351+ // Bear integration
352+ if ( body . bear ?. plan ) {
353+ const result = await saveToBear ( body . bear ) ;
354+ if ( result . success ) {
355+ console . error ( `[Bear] Saved plan to Bear` ) ;
356+ } else {
357+ console . error ( `[Bear] Save failed: ${ result . error } ` ) ;
358+ }
359+ }
311360 } catch ( err ) {
312- // Don't block approval on Obsidian errors
313- console . error ( `[Obsidian ] Error:` , err ) ;
361+ // Don't block approval on integration errors
362+ console . error ( `[Integration ] Error:` , err ) ;
314363 }
315364
316365 resolveDecision ( { approved : true } ) ;
0 commit comments