kimaki 0.4.35 → 0.4.36

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.
Files changed (75) hide show
  1. package/dist/ai-tool-to-genai.js +1 -3
  2. package/dist/channel-management.js +1 -1
  3. package/dist/cli.js +135 -39
  4. package/dist/commands/abort.js +1 -1
  5. package/dist/commands/add-project.js +1 -1
  6. package/dist/commands/agent.js +6 -2
  7. package/dist/commands/ask-question.js +2 -1
  8. package/dist/commands/fork.js +7 -7
  9. package/dist/commands/queue.js +2 -2
  10. package/dist/commands/remove-project.js +109 -0
  11. package/dist/commands/resume.js +3 -5
  12. package/dist/commands/session.js +2 -2
  13. package/dist/commands/share.js +1 -1
  14. package/dist/commands/undo-redo.js +2 -2
  15. package/dist/commands/user-command.js +3 -6
  16. package/dist/config.js +1 -1
  17. package/dist/discord-bot.js +4 -10
  18. package/dist/discord-utils.js +33 -9
  19. package/dist/genai.js +4 -6
  20. package/dist/interaction-handler.js +8 -1
  21. package/dist/markdown.js +1 -3
  22. package/dist/message-formatting.js +7 -3
  23. package/dist/openai-realtime.js +3 -5
  24. package/dist/opencode.js +1 -1
  25. package/dist/session-handler.js +25 -15
  26. package/dist/system-message.js +5 -3
  27. package/dist/tools.js +9 -22
  28. package/dist/voice-handler.js +9 -12
  29. package/dist/voice.js +5 -3
  30. package/dist/xml.js +2 -4
  31. package/package.json +3 -2
  32. package/src/__snapshots__/compact-session-context-no-system.md +24 -24
  33. package/src/__snapshots__/compact-session-context.md +31 -31
  34. package/src/ai-tool-to-genai.ts +3 -11
  35. package/src/channel-management.ts +14 -25
  36. package/src/cli.ts +282 -195
  37. package/src/commands/abort.ts +1 -3
  38. package/src/commands/add-project.ts +8 -14
  39. package/src/commands/agent.ts +16 -9
  40. package/src/commands/ask-question.ts +8 -7
  41. package/src/commands/create-new-project.ts +8 -14
  42. package/src/commands/fork.ts +23 -27
  43. package/src/commands/model.ts +14 -11
  44. package/src/commands/permissions.ts +1 -1
  45. package/src/commands/queue.ts +6 -19
  46. package/src/commands/remove-project.ts +136 -0
  47. package/src/commands/resume.ts +11 -30
  48. package/src/commands/session.ts +4 -13
  49. package/src/commands/share.ts +1 -3
  50. package/src/commands/types.ts +1 -3
  51. package/src/commands/undo-redo.ts +6 -18
  52. package/src/commands/user-command.ts +8 -10
  53. package/src/config.ts +5 -5
  54. package/src/database.ts +10 -8
  55. package/src/discord-bot.ts +22 -46
  56. package/src/discord-utils.ts +35 -18
  57. package/src/escape-backticks.test.ts +0 -2
  58. package/src/format-tables.ts +1 -4
  59. package/src/genai-worker-wrapper.ts +3 -9
  60. package/src/genai-worker.ts +4 -19
  61. package/src/genai.ts +10 -42
  62. package/src/interaction-handler.ts +133 -121
  63. package/src/markdown.test.ts +10 -32
  64. package/src/markdown.ts +6 -14
  65. package/src/message-formatting.ts +13 -14
  66. package/src/openai-realtime.ts +25 -47
  67. package/src/opencode.ts +24 -34
  68. package/src/session-handler.ts +91 -61
  69. package/src/system-message.ts +13 -3
  70. package/src/tools.ts +13 -39
  71. package/src/utils.ts +1 -4
  72. package/src/voice-handler.ts +34 -78
  73. package/src/voice.ts +11 -19
  74. package/src/xml.test.ts +1 -1
  75. package/src/xml.ts +3 -12
package/src/tools.ts CHANGED
@@ -19,10 +19,7 @@ const toolsLogger = createLogger('TOOLS')
19
19
  import { ShareMarkdown } from './markdown.js'
20
20
  import { formatDistanceToNow } from './utils.js'
21
21
  import pc from 'picocolors'
22
- import {
23
- initializeOpencodeForDirectory,
24
- getOpencodeSystemMessage,
25
- } from './discord-bot.js'
22
+ import { initializeOpencodeForDirectory, getOpencodeSystemMessage } from './discord-bot.js'
26
23
 
27
24
  export async function getTools({
28
25
  onMessageCompleted,
@@ -116,25 +113,17 @@ export async function getTools({
116
113
  description:
117
114
  'Start a new chat session with an initial message. Does not wait for the message to complete',
118
115
  inputSchema: z.object({
119
- message: z
120
- .string()
121
- .describe('The initial message to start the chat with'),
116
+ message: z.string().describe('The initial message to start the chat with'),
122
117
  title: z.string().optional().describe('Optional title for the session'),
123
118
  model: z
124
119
  .object({
125
- providerId: z
126
- .string()
127
- .describe('The provider ID (e.g., "anthropic", "openai")'),
128
- modelId: z
129
- .string()
130
- .describe(
131
- 'The model ID (e.g., "claude-opus-4-20250514", "gpt-5")',
132
- ),
120
+ providerId: z.string().describe('The provider ID (e.g., "anthropic", "openai")'),
121
+ modelId: z.string().describe('The model ID (e.g., "claude-opus-4-20250514", "gpt-5")'),
133
122
  })
134
123
  .optional()
135
124
  .describe('Optional model to use for this session'),
136
125
  }),
137
- execute: async ({ message, title, }) => {
126
+ execute: async ({ message, title }) => {
138
127
  if (!message.trim()) {
139
128
  throw new Error(`message must be a non empty string`)
140
129
  }
@@ -187,18 +176,14 @@ export async function getTools({
187
176
  } catch (error) {
188
177
  return {
189
178
  success: false,
190
- error:
191
- error instanceof Error
192
- ? error.message
193
- : 'Failed to create chat session',
179
+ error: error instanceof Error ? error.message : 'Failed to create chat session',
194
180
  }
195
181
  }
196
182
  },
197
183
  }),
198
184
 
199
185
  listChats: tool({
200
- description:
201
- 'Get a list of available chat sessions sorted by most recent',
186
+ description: 'Get a list of available chat sessions sorted by most recent',
202
187
  inputSchema: z.object({}),
203
188
  execute: async () => {
204
189
  toolsLogger.log(`Listing opencode sessions`)
@@ -223,10 +208,7 @@ export async function getTools({
223
208
  })
224
209
  const messages = messagesResponse.data || []
225
210
  const lastMessage = messages[messages.length - 1]
226
- if (
227
- lastMessage?.info.role === 'assistant' &&
228
- !lastMessage.info.time.completed
229
- ) {
211
+ if (lastMessage?.info.role === 'assistant' && !lastMessage.info.time.completed) {
230
212
  return 'in_progress'
231
213
  }
232
214
  return 'finished'
@@ -281,10 +263,7 @@ export async function getTools({
281
263
  description: 'Read messages from a chat session',
282
264
  inputSchema: z.object({
283
265
  sessionId: z.string().describe('The session ID to read messages from'),
284
- lastAssistantOnly: z
285
- .boolean()
286
- .optional()
287
- .describe('Only read the last assistant message'),
266
+ lastAssistantOnly: z.boolean().optional().describe('Only read the last assistant message'),
288
267
  }),
289
268
  execute: async ({ sessionId, lastAssistantOnly = false }) => {
290
269
  if (lastAssistantOnly) {
@@ -296,9 +275,7 @@ export async function getTools({
296
275
  return { success: false, error: 'No messages found' }
297
276
  }
298
277
 
299
- const assistantMessages = messages.data.filter(
300
- (m) => m.info.role === 'assistant',
301
- )
278
+ const assistantMessages = messages.data.filter((m) => m.info.role === 'assistant')
302
279
 
303
280
  if (assistantMessages.length === 0) {
304
281
  return {
@@ -309,8 +286,7 @@ export async function getTools({
309
286
 
310
287
  const lastMessage = assistantMessages[assistantMessages.length - 1]
311
288
  const status =
312
- 'completed' in lastMessage!.info.time &&
313
- lastMessage!.info.time.completed
289
+ 'completed' in lastMessage!.info.time && lastMessage!.info.time.completed
314
290
  ? 'completed'
315
291
  : 'in_progress'
316
292
 
@@ -376,8 +352,7 @@ export async function getTools({
376
352
  } catch (error) {
377
353
  return {
378
354
  success: false,
379
- error:
380
- error instanceof Error ? error.message : 'Unknown error occurred',
355
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
381
356
  }
382
357
  }
383
358
  },
@@ -412,8 +387,7 @@ export async function getTools({
412
387
  } catch (error) {
413
388
  return {
414
389
  success: false,
415
- error:
416
- error instanceof Error ? error.message : 'Failed to fetch models',
390
+ error: error instanceof Error ? error.message : 'Failed to fetch models',
417
391
  models: [],
418
392
  }
419
393
  }
package/src/utils.ts CHANGED
@@ -66,10 +66,7 @@ export function deduplicateByKey<T, K>(arr: T[], keyFn: (item: T) => K): T[] {
66
66
  })
67
67
  }
68
68
 
69
- export function isAbortError(
70
- error: unknown,
71
- signal?: AbortSignal,
72
- ): error is Error {
69
+ export function isAbortError(error: unknown, signal?: AbortSignal): error is Error {
73
70
  return (
74
71
  (error instanceof Error &&
75
72
  (error.name === 'AbortError' ||
@@ -28,7 +28,11 @@ import {
28
28
  } from 'discord.js'
29
29
  import { createGenAIWorker, type GenAIWorker } from './genai-worker-wrapper.js'
30
30
  import { getDatabase } from './database.js'
31
- import { sendThreadMessage, escapeDiscordFormatting, SILENT_MESSAGE_FLAGS } from './discord-utils.js'
31
+ import {
32
+ sendThreadMessage,
33
+ escapeDiscordFormatting,
34
+ SILENT_MESSAGE_FLAGS,
35
+ } from './discord-utils.js'
32
36
  import { transcribeAudio } from './voice.js'
33
37
  import { createLogger } from './logger.js'
34
38
 
@@ -75,12 +79,7 @@ export async function createUserAudioLogStream(
75
79
  if (!process.env.DEBUG) return undefined
76
80
 
77
81
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
78
- const audioDir = path.join(
79
- process.cwd(),
80
- 'discord-audio-logs',
81
- guildId,
82
- channelId,
83
- )
82
+ const audioDir = path.join(process.cwd(), 'discord-audio-logs', guildId, channelId)
84
83
 
85
84
  try {
86
85
  await mkdir(audioDir, { recursive: true })
@@ -98,8 +97,7 @@ export async function createUserAudioLogStream(
98
97
  }
99
98
 
100
99
  export function frameMono16khz(): Transform {
101
- const FRAME_BYTES =
102
- (100 * 16_000 * 1 * 2) / 1000
100
+ const FRAME_BYTES = (100 * 16_000 * 1 * 2) / 1000
103
101
  let stash: Buffer = Buffer.alloc(0)
104
102
  let offset = 0
105
103
 
@@ -149,20 +147,14 @@ export async function setupVoiceHandling({
149
147
  appId: string
150
148
  discordClient: Client
151
149
  }) {
152
- voiceLogger.log(
153
- `Setting up voice handling for guild ${guildId}, channel ${channelId}`,
154
- )
150
+ voiceLogger.log(`Setting up voice handling for guild ${guildId}, channel ${channelId}`)
155
151
 
156
152
  const channelDirRow = getDatabase()
157
- .prepare(
158
- 'SELECT directory FROM channel_directories WHERE channel_id = ? AND channel_type = ?',
159
- )
153
+ .prepare('SELECT directory FROM channel_directories WHERE channel_id = ? AND channel_type = ?')
160
154
  .get(channelId, 'voice') as { directory: string } | undefined
161
155
 
162
156
  if (!channelDirRow) {
163
- voiceLogger.log(
164
- `Voice channel ${channelId} has no associated directory, skipping setup`,
165
- )
157
+ voiceLogger.log(`Voice channel ${channelId} has no associated directory, skipping setup`)
166
158
  return
167
159
  }
168
160
 
@@ -266,11 +258,12 @@ export async function setupVoiceHandling({
266
258
 
267
259
  if (textChannelRow) {
268
260
  try {
269
- const textChannel = await discordClient.channels.fetch(
270
- textChannelRow.channel_id,
271
- )
261
+ const textChannel = await discordClient.channels.fetch(textChannelRow.channel_id)
272
262
  if (textChannel?.isTextBased() && 'send' in textChannel) {
273
- await textChannel.send({ content: `⚠️ Voice session error: ${error}`, flags: SILENT_MESSAGE_FLAGS })
263
+ await textChannel.send({
264
+ content: `⚠️ Voice session error: ${error}`,
265
+ flags: SILENT_MESSAGE_FLAGS,
266
+ })
274
267
  }
275
268
  } catch (e) {
276
269
  voiceLogger.error('Failed to send error to text channel:', e)
@@ -330,10 +323,7 @@ export async function setupVoiceHandling({
330
323
 
331
324
  const framer = frameMono16khz()
332
325
 
333
- const pipeline = audioStream
334
- .pipe(decoder)
335
- .pipe(downsampleTransform)
336
- .pipe(framer)
326
+ const pipeline = audioStream.pipe(decoder).pipe(downsampleTransform).pipe(framer)
337
327
 
338
328
  pipeline
339
329
  .on('data', (frame: Buffer) => {
@@ -359,9 +349,7 @@ export async function setupVoiceHandling({
359
349
  })
360
350
  .on('end', () => {
361
351
  if (currentSessionCount === speakingSessionCount) {
362
- voiceLogger.log(
363
- `User ${userId} stopped speaking (session ${currentSessionCount})`,
364
- )
352
+ voiceLogger.log(`User ${userId} stopped speaking (session ${currentSessionCount})`)
365
353
  voiceData.genAiWorker?.sendRealtimeInput({
366
354
  audioStreamEnd: true,
367
355
  })
@@ -413,9 +401,7 @@ export async function cleanupVoiceConnection(guildId: string) {
413
401
  })
414
402
  }
415
403
 
416
- if (
417
- voiceData.connection.state.status !== VoiceConnectionStatus.Destroyed
418
- ) {
404
+ if (voiceData.connection.state.status !== VoiceConnectionStatus.Destroyed) {
419
405
  voiceLogger.log(`Destroying voice connection...`)
420
406
  voiceData.connection.destroy()
421
407
  }
@@ -445,8 +431,8 @@ export async function processVoiceAttachment({
445
431
  currentSessionContext?: string
446
432
  lastSessionContext?: string
447
433
  }): Promise<string | null> {
448
- const audioAttachment = Array.from(message.attachments.values()).find(
449
- (attachment) => attachment.contentType?.startsWith('audio/'),
434
+ const audioAttachment = Array.from(message.attachments.values()).find((attachment) =>
435
+ attachment.contentType?.startsWith('audio/'),
450
436
  )
451
437
 
452
438
  if (!audioAttachment) return null
@@ -550,15 +536,9 @@ export function registerVoiceStateHandler({
550
536
 
551
537
  const guild = newState.guild || oldState.guild
552
538
  const isOwner = member.id === guild.ownerId
553
- const isAdmin = member.permissions.has(
554
- PermissionsBitField.Flags.Administrator,
555
- )
556
- const canManageServer = member.permissions.has(
557
- PermissionsBitField.Flags.ManageGuild,
558
- )
559
- const hasKimakiRole = member.roles.cache.some(
560
- (role) => role.name.toLowerCase() === 'kimaki',
561
- )
539
+ const isAdmin = member.permissions.has(PermissionsBitField.Flags.Administrator)
540
+ const canManageServer = member.permissions.has(PermissionsBitField.Flags.ManageGuild)
541
+ const hasKimakiRole = member.roles.cache.some((role) => role.name.toLowerCase() === 'kimaki')
562
542
 
563
543
  if (!isOwner && !isAdmin && !canManageServer && !hasKimakiRole) {
564
544
  return
@@ -572,10 +552,7 @@ export function registerVoiceStateHandler({
572
552
  const guildId = guild.id
573
553
  const voiceData = voiceConnections.get(guildId)
574
554
 
575
- if (
576
- voiceData &&
577
- voiceData.connection.joinConfig.channelId === oldState.channelId
578
- ) {
555
+ if (voiceData && voiceData.connection.joinConfig.channelId === oldState.channelId) {
579
556
  const voiceChannel = oldState.channel as VoiceChannel
580
557
  if (!voiceChannel) return
581
558
 
@@ -596,9 +573,7 @@ export function registerVoiceStateHandler({
596
573
 
597
574
  await cleanupVoiceConnection(guildId)
598
575
  } else {
599
- voiceLogger.log(
600
- `Other admins still in channel, bot staying in voice channel`,
601
- )
576
+ voiceLogger.log(`Other admins still in channel, bot staying in voice channel`)
602
577
  }
603
578
  }
604
579
  return
@@ -616,10 +591,7 @@ export function registerVoiceStateHandler({
616
591
  const guildId = guild.id
617
592
  const voiceData = voiceConnections.get(guildId)
618
593
 
619
- if (
620
- voiceData &&
621
- voiceData.connection.joinConfig.channelId === oldState.channelId
622
- ) {
594
+ if (voiceData && voiceData.connection.joinConfig.channelId === oldState.channelId) {
623
595
  const oldVoiceChannel = oldState.channel as VoiceChannel
624
596
  if (oldVoiceChannel) {
625
597
  const hasOtherAdmins = oldVoiceChannel.members.some((m) => {
@@ -633,9 +605,7 @@ export function registerVoiceStateHandler({
633
605
  })
634
606
 
635
607
  if (!hasOtherAdmins) {
636
- voiceLogger.log(
637
- `Following admin to new channel: ${newState.channel?.name}`,
638
- )
608
+ voiceLogger.log(`Following admin to new channel: ${newState.channel?.name}`)
639
609
  const voiceChannel = newState.channel as VoiceChannel
640
610
  if (voiceChannel) {
641
611
  voiceData.connection.rejoin({
@@ -645,9 +615,7 @@ export function registerVoiceStateHandler({
645
615
  })
646
616
  }
647
617
  } else {
648
- voiceLogger.log(
649
- `Other admins still in old channel, bot staying put`,
650
- )
618
+ voiceLogger.log(`Other admins still in old channel, bot staying put`)
651
619
  }
652
620
  }
653
621
  }
@@ -667,16 +635,11 @@ export function registerVoiceStateHandler({
667
635
  const existingVoiceData = voiceConnections.get(newState.guild.id)
668
636
  if (
669
637
  existingVoiceData &&
670
- existingVoiceData.connection.state.status !==
671
- VoiceConnectionStatus.Destroyed
638
+ existingVoiceData.connection.state.status !== VoiceConnectionStatus.Destroyed
672
639
  ) {
673
- voiceLogger.log(
674
- `Bot already connected to a voice channel in guild ${newState.guild.name}`,
675
- )
640
+ voiceLogger.log(`Bot already connected to a voice channel in guild ${newState.guild.name}`)
676
641
 
677
- if (
678
- existingVoiceData.connection.joinConfig.channelId !== voiceChannel.id
679
- ) {
642
+ if (existingVoiceData.connection.joinConfig.channelId !== voiceChannel.id) {
680
643
  voiceLogger.log(
681
644
  `Moving bot from channel ${existingVoiceData.connection.joinConfig.channelId} to ${voiceChannel.id}`,
682
645
  )
@@ -720,9 +683,7 @@ export function registerVoiceStateHandler({
720
683
  })
721
684
 
722
685
  connection.on(VoiceConnectionStatus.Disconnected, async () => {
723
- voiceLogger.log(
724
- `Disconnected from voice channel in guild: ${newState.guild.name}`,
725
- )
686
+ voiceLogger.log(`Disconnected from voice channel in guild: ${newState.guild.name}`)
726
687
  try {
727
688
  await Promise.race([
728
689
  entersState(connection, VoiceConnectionStatus.Signalling, 5_000),
@@ -737,17 +698,12 @@ export function registerVoiceStateHandler({
737
698
  })
738
699
 
739
700
  connection.on(VoiceConnectionStatus.Destroyed, async () => {
740
- voiceLogger.log(
741
- `Connection destroyed for guild: ${newState.guild.name}`,
742
- )
701
+ voiceLogger.log(`Connection destroyed for guild: ${newState.guild.name}`)
743
702
  await cleanupVoiceConnection(newState.guild.id)
744
703
  })
745
704
 
746
705
  connection.on('error', (error) => {
747
- voiceLogger.error(
748
- `Connection error in guild ${newState.guild.name}:`,
749
- error,
750
- )
706
+ voiceLogger.error(`Connection error in guild ${newState.guild.name}:`, error)
751
707
  })
752
708
  } catch (error) {
753
709
  voiceLogger.error(`Failed to join voice channel:`, error)
package/src/voice.ts CHANGED
@@ -2,13 +2,7 @@
2
2
  // Transcribes voice messages with code-aware context, using grep/glob tools
3
3
  // to verify technical terms, filenames, and function names in the codebase.
4
4
 
5
- import {
6
- GoogleGenAI,
7
- Type,
8
- type Content,
9
- type Part,
10
- type Tool,
11
- } from '@google/genai'
5
+ import { GoogleGenAI, Type, type Content, type Part, type Tool } from '@google/genai'
12
6
  import { createLogger } from './logger.js'
13
7
  import { glob } from 'glob'
14
8
  import { ripGrep } from 'ripgrep-js'
@@ -134,11 +128,7 @@ const transcriptionResultToolDeclaration = {
134
128
  },
135
129
  }
136
130
 
137
- function createToolRunner({
138
- directory,
139
- }: {
140
- directory?: string
141
- }): TranscriptionToolRunner {
131
+ function createToolRunner({ directory }: { directory?: string }): TranscriptionToolRunner {
142
132
  const hasDirectory = directory && directory.trim().length > 0
143
133
 
144
134
  return async ({ name, args }) => {
@@ -242,9 +232,7 @@ export async function runTranscriptionLoop({
242
232
 
243
233
  if (result.type === 'result') {
244
234
  const transcription = result.transcription?.trim() || ''
245
- voiceLogger.log(
246
- `Transcription result received: "${transcription.slice(0, 100)}..."`,
247
- )
235
+ voiceLogger.log(`Transcription result received: "${transcription.slice(0, 100)}..."`)
248
236
  if (!transcription) {
249
237
  throw new Error('Transcription failed: Model returned empty transcription')
250
238
  }
@@ -292,7 +280,10 @@ export async function runTranscriptionLoop({
292
280
  thinkingConfig: {
293
281
  thinkingBudget: 512,
294
282
  },
295
- tools: stepsRemaining <= 0 ? [{ functionDeclarations: [transcriptionResultToolDeclaration] }] : tools,
283
+ tools:
284
+ stepsRemaining <= 0
285
+ ? [{ functionDeclarations: [transcriptionResultToolDeclaration] }]
286
+ : tools,
296
287
  },
297
288
  })
298
289
  }
@@ -353,9 +344,10 @@ ${lastSessionContext}
353
344
  ${currentSessionContext}
354
345
  </current_session>`)
355
346
  }
356
- const sessionContextSection = sessionContextParts.length > 0
357
- ? `\nSession context (use to understand references to files, functions, tools used):\n${sessionContextParts.join('\n\n')}`
358
- : ''
347
+ const sessionContextSection =
348
+ sessionContextParts.length > 0
349
+ ? `\nSession context (use to understand references to files, functions, tools used):\n${sessionContextParts.join('\n\n')}`
350
+ : ''
359
351
 
360
352
  const transcriptionPrompt = `${languageHint}Transcribe this audio for a coding agent (like Claude Code or OpenCode).
361
353
 
package/src/xml.test.ts CHANGED
@@ -34,4 +34,4 @@ describe('extractNonXmlContent', () => {
34
34
  const xml = ''
35
35
  expect(extractNonXmlContent(xml)).toMatchInlineSnapshot(`""`)
36
36
  })
37
- })
37
+ })
package/src/xml.ts CHANGED
@@ -40,15 +40,9 @@ export function extractTagsArrays<T extends string>({
40
40
  // Extract content using original string positions
41
41
  const extractContent = (): string => {
42
42
  // Use element's own indices but exclude the tags
43
- if (
44
- element.startIndex !== null &&
45
- element.endIndex !== null
46
- ) {
43
+ if (element.startIndex !== null && element.endIndex !== null) {
47
44
  // Extract the full element including tags
48
- const fullElement = xml.substring(
49
- element.startIndex,
50
- element.endIndex + 1,
51
- )
45
+ const fullElement = xml.substring(element.startIndex, element.endIndex + 1)
52
46
  // Find where content starts (after opening tag)
53
47
  const contentStart = fullElement.indexOf('>') + 1
54
48
  // Find where content ends (before this element's closing tag)
@@ -79,10 +73,7 @@ export function extractTagsArrays<T extends string>({
79
73
  if (element.children) {
80
74
  findTags(element.children, currentPath)
81
75
  }
82
- } else if (
83
- node.type === ElementType.Text &&
84
- node.parent?.type === ElementType.Root
85
- ) {
76
+ } else if (node.type === ElementType.Text && node.parent?.type === ElementType.Root) {
86
77
  const textNode = node as Text
87
78
  if (textNode.data.trim()) {
88
79
  // console.log('node.parent',node.parent)