yt-briefing 1.2.1 → 1.2.3
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/README.md +4 -3
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/register.tsx +25 -55
package/README.md
CHANGED
|
@@ -137,9 +137,10 @@ The **Comment** field takes anything else. Type what you think in your own words
|
|
|
137
137
|
shows, skip those") and press Enter: Claude turns it into a standing rule for that channel and
|
|
138
138
|
infers the rating. `? your question` starts research with that question, `stop` closes the pane.
|
|
139
139
|
Claude Code on a phone has no text fields, so there the pane shows the four keys only.
|
|
140
|
-
Where no pane can be seen (`/yt` sent from claude.ai/code over Remote Control, or
|
|
141
|
-
places no panes), the same loop
|
|
142
|
-
OK / Weak / Research / Stop
|
|
140
|
+
Where no pane can be seen (`/yt` sent from claude.ai/code or the phone app over Remote Control, or
|
|
141
|
+
a surface that places no panes), `/yt` hands the same loop to Claude in the chat: each summary is
|
|
142
|
+
Claude's message, a short dialog asks for the rating (OK / Weak / Research / Stop), and a typed
|
|
143
|
+
**Other** answer works as the Comment field. This path uses the model, so it counts toward plan usage.
|
|
143
144
|
|
|
144
145
|
Each step is the engine, not a chat turn: rating a video takes no turn and no context of your
|
|
145
146
|
session, and the next summary is usually ready before you have finished reading the current one.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yt-briefing",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
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": {
|
|
@@ -153,56 +153,34 @@ async function comment($: EngineInterface, raw: string) {
|
|
|
153
153
|
if (saved?.rule) $.ui.toast(`Rule saved: ${saved.rule}`)
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
/** The dialog's choices; anything else is text typed under "Other". */
|
|
157
|
-
const CHOICES = ['OK', 'Weak', 'Research', 'Stop'] as const
|
|
158
|
-
|
|
159
|
-
/** The dialog draws plain text: drop the briefing's Markdown marks so no `**` or `###` shows. */
|
|
160
|
-
function plain(md: string): string {
|
|
161
|
-
return md
|
|
162
|
-
.replace(/^#{1,6}\s+/gm, '')
|
|
163
|
-
.replace(/\*\*(.+?)\*\*/g, '$1')
|
|
164
|
-
.replace(/^_(.+)_$/gm, '$1')
|
|
165
|
-
.replace(/^(\d+\.)\s+/gm, '$1 ')
|
|
166
|
-
.replace(/(\d{4}-\d{2}-\d{2})T[\d:.]+Z/g, '$1')
|
|
167
|
-
}
|
|
168
|
-
|
|
169
156
|
/** True when the person typed /yt through Remote Control and nothing attached can show a pane. */
|
|
170
157
|
async function paneUnseen($: EngineInterface, origin: { kind: string } | undefined): Promise<boolean> {
|
|
171
158
|
if (origin?.kind !== 'bridge') return false
|
|
172
159
|
return !(await $.session.surfaces()).some(s => s !== 'terminal')
|
|
173
160
|
}
|
|
174
161
|
|
|
175
|
-
/**
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const saved = await record($, args)
|
|
198
|
-
if (!saved) return
|
|
199
|
-
$.ui.log(saved.rule ? `Rule saved: ${saved.rule}. Looking for the next video…` : 'Saved. Looking for the next video…')
|
|
200
|
-
try {
|
|
201
|
-
out = await runSweep($, false)
|
|
202
|
-
} catch (err) {
|
|
203
|
-
return void $.ui.log(`The engine did not answer: ${String(err)}`)
|
|
204
|
-
}
|
|
205
|
-
}
|
|
162
|
+
/**
|
|
163
|
+
* The loop for a session where no pane can be seen, handed to the session as a prompt. A plugin
|
|
164
|
+
* cannot show a second transcript row a remote client draws (it cannot run its own command, and
|
|
165
|
+
* its log lines reach only the terminal), so the briefing is the model's own message text and the
|
|
166
|
+
* dialog asks for the rating alone: its question is drawn in a heading face, too large for a briefing.
|
|
167
|
+
*/
|
|
168
|
+
function remoteLoop(): string {
|
|
169
|
+
const sweep = engine('yt-sweep').join(' ')
|
|
170
|
+
const rating = engine('yt-rating').join(' ')
|
|
171
|
+
const transcript = [...engine('yt-transcript'), '<videoId>', '--lang', 'auto'].join(' ')
|
|
172
|
+
return [
|
|
173
|
+
'Run my YouTube briefing here in the chat, one video at a time, until I stop or nothing is left.',
|
|
174
|
+
'',
|
|
175
|
+
`1. Run \`${sweep} --reset\` the first time, \`${sweep}\` after that. It prints one JSON line.`,
|
|
176
|
+
'2. When `status` is `rating_needed`: post `summary` as your whole message, verbatim, Markdown kept. Add nothing before or after it.',
|
|
177
|
+
'3. Then call AskUserQuestion with one question, exactly `«<pending.title>» — rating?` (nothing else in it), header `yt-briefing`, options OK, Weak, Research, Stop, no descriptions or previews.',
|
|
178
|
+
`4. OK: \`${rating} --rating 1\`. Weak: \`${rating} --rating 0\`. Text typed under Other: \`${rating} --raw-comment "<the text>"\`. Then back to step 1, without a word in between.`,
|
|
179
|
+
`5. Research: \`${rating} --rating 1\`, then research the video with me: ask what I want to dig into, pull the transcript with \`${transcript}\` when needed (never paste it), keep what the video claims apart from what you verify.`,
|
|
180
|
+
'6. Stop, or any other `status`: say so in one short line and end.',
|
|
181
|
+
'',
|
|
182
|
+
'Write in the language of the summaries.',
|
|
183
|
+
].join('\n')
|
|
206
184
|
}
|
|
207
185
|
|
|
208
186
|
export const register: Register = on => {
|
|
@@ -224,18 +202,10 @@ export const register: Register = on => {
|
|
|
224
202
|
}
|
|
225
203
|
await $.ui.close({ id: PANE })
|
|
226
204
|
}
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
let first: SweepOut
|
|
230
|
-
try {
|
|
231
|
-
first = await runSweep($, true)
|
|
232
|
-
} catch (err) {
|
|
233
|
-
return { text: `The engine did not answer: ${String(err)}` }
|
|
234
|
-
}
|
|
235
|
-
if (first.status !== 'rating_needed') return { text: endText(first) }
|
|
236
|
-
void dialogLoop($, first)
|
|
205
|
+
// A prompt submitted while this hook holds the command would wait on it: submit once it has answered.
|
|
206
|
+
$.clock.after(0, () => void $.prompt.submit({ text: remoteLoop() }).catch(err => $.ui.toast(`Could not start the briefing: ${String(err)}`)))
|
|
237
207
|
|
|
238
|
-
return { text: '
|
|
208
|
+
return { text: 'No pane here: the briefing runs in the chat.' }
|
|
239
209
|
})
|
|
240
210
|
|
|
241
211
|
on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
|