yt-briefing 1.2.0 → 1.2.2

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": "yt-briefing",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "A self-learning YouTube briefing for Claude Code: it sweeps the channels you follow, filters noise in two stages (title, then transcript), gives you a short briefing of the rest in your language in a pane, and adapts to your ratings — one video at a time.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yt-briefing",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "The /yt briefing loop as a Claude Code pane: summary, one-key rating, research hand-off.",
5
5
  "author": {
6
6
  "name": "Michal Ryzio"
@@ -163,6 +163,7 @@ function plain(md: string): string {
163
163
  .replace(/\*\*(.+?)\*\*/g, '$1')
164
164
  .replace(/^_(.+)_$/gm, '$1')
165
165
  .replace(/^(\d+\.)\s+/gm, '$1 ')
166
+ .replace(/(\d{4}-\d{2}-\d{2})T[\d:.]+Z/g, '$1')
166
167
  }
167
168
 
168
169
  /** True when the person typed /yt through Remote Control and nothing attached can show a pane. */
@@ -171,22 +172,22 @@ async function paneUnseen($: EngineInterface, origin: { kind: string } | undefin
171
172
  return !(await $.session.surfaces()).some(s => s !== 'terminal')
172
173
  }
173
174
 
174
- /** The rating loop in the engine's question dialog, for a session where no pane can be seen. */
175
- async function dialogLoop($: EngineInterface) {
176
- let reset = true
177
- for (;;) {
178
- let out: SweepOut
179
- try {
180
- out = await runSweep($, reset)
181
- } catch (err) {
182
- return void $.ui.toast(`The engine did not answer: ${String(err)}`)
183
- }
184
- reset = false
185
- if (out.status !== 'rating_needed' || !out.summary || !out.pending) return void $.ui.toast(endText(out))
175
+ /** The briefing as transcript text: plain, with the filtered-out count at the end. */
176
+ function briefing(out: SweepOut): string {
177
+ return [plain(out.summary ?? ''), out.skipped ? `(${out.skipped} filtered out)` : undefined]
178
+ .filter(line => line !== undefined).join('\n\n')
179
+ }
180
+
181
+ /** The rating loop in the engine's question dialog, from a sweep already run, for a session where no pane can be seen. */
182
+ async function dialogLoop($: EngineInterface, first: SweepOut) {
183
+ let out = first
184
+ for (let shown = true; ; shown = false) {
185
+ if (out.status !== 'rating_needed' || !out.summary || !out.pending) return void $.ui.log(endText(out))
186
186
 
187
- const skipped = skipLine(out)
188
- const question = [skipped, plain(out.summary), '', 'OK = neutral · Weak = skip titles like this · Research = dig in with Claude · Other = a rule for this channel (?question = research). Rating?']
189
- .filter(line => line !== undefined).join('\n')
187
+ // The dialog draws its question in a large heading face, so the briefing goes to the
188
+ // transcript as text and the dialog asks only for the rating. The first is the command's output.
189
+ if (!shown) $.ui.log(briefing(out))
190
+ const question = `${out.pending.channel}: rating?`
190
191
  let answer: string
191
192
  try {
192
193
  answer = (await $.ui.ask(question, { header: 'yt-briefing', options: CHOICES })).trim()
@@ -203,7 +204,12 @@ async function dialogLoop($: EngineInterface) {
203
204
  const args = answer === 'OK' ? ['--rating', '1'] : answer === 'Weak' ? ['--rating', '0'] : ['--raw-comment', answer]
204
205
  const saved = await record($, args)
205
206
  if (!saved) return
206
- if (saved.rule) $.ui.toast(`Rule saved: ${saved.rule}`)
207
+ $.ui.log(saved.rule ? `Rule saved: ${saved.rule}. Looking for the next video…` : 'Saved. Looking for the next video…')
208
+ try {
209
+ out = await runSweep($, false)
210
+ } catch (err) {
211
+ return void $.ui.log(`The engine did not answer: ${String(err)}`)
212
+ }
207
213
  }
208
214
  }
209
215
 
@@ -226,9 +232,18 @@ export const register: Register = on => {
226
232
  }
227
233
  await $.ui.close({ id: PANE })
228
234
  }
229
- void dialogLoop($)
235
+ // No pane to show progress in: the command itself runs while the first sweep does, so the
236
+ // session shows it working, and only then hands over to the dialogs.
237
+ let first: SweepOut
238
+ try {
239
+ first = await runSweep($, true)
240
+ } catch (err) {
241
+ return { text: `The engine did not answer: ${String(err)}` }
242
+ }
243
+ if (first.status !== 'rating_needed') return { text: endText(first) }
244
+ void dialogLoop($, first)
230
245
 
231
- return { text: 'No pane here: the briefing runs in question dialogs.' }
246
+ return { text: briefing(first) }
232
247
  })
233
248
 
234
249
  on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {