chrome-devtools-mcp-for-extension 0.10.3 → 0.11.0

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.
@@ -266,44 +266,73 @@ async function detectDeepResearchMode(page) {
266
266
  });
267
267
  }
268
268
  /**
269
- * Enable DeepResearch mode by clicking + button and selecting option
269
+ * Enable DeepResearch mode using new UI structure (2025-10)
270
+ * - Click "ファイルの追加など" button to open menu
271
+ * - Select "Deep Research" menuitemradio
272
+ * - Verify by checking placeholder and delete button
270
273
  */
271
274
  async function enableDeepResearchMode(page, response) {
272
275
  try {
273
276
  response.appendResponseLine('DeepResearchモードを有効化中...');
274
- // Step 1: +ボタンをクリック(Puppeteer click
275
- const plusButton = await page.$('button[aria-label*="ファイルの追加"]');
276
- if (!plusButton) {
277
- return { success: false, error: '+ボタンが見つかりません' };
278
- }
279
- await plusButton.click();
280
- response.appendResponseLine('✅ +ボタンをクリック');
281
- await page.waitForTimeout(500);
282
- // Step 2: Deep Research menuitemradio をクリック(Puppeteer click
283
- const menuItems = await page.$$('[role="menuitemradio"]');
284
- let deepResearchItem = null;
285
- for (const item of menuItems) {
286
- const text = await item.evaluate((el) => el.textContent);
287
- if (text?.includes('Deep Research') || text?.includes('リサーチ')) {
288
- deepResearchItem = item;
289
- break;
277
+ // Step 1: Find and click "ファイルの追加など" button (with haspopup="menu")
278
+ const menuButton = await page.evaluate(() => {
279
+ const buttons = Array.from(document.querySelectorAll('button[haspopup="menu"]'));
280
+ const targetButton = buttons.find(btn => {
281
+ const text = btn.textContent || '';
282
+ return text.includes('ファイルの追加') || text.includes('追加');
283
+ });
284
+ if (targetButton) {
285
+ targetButton.click();
286
+ return true;
290
287
  }
288
+ return false;
289
+ });
290
+ if (!menuButton) {
291
+ return { success: false, error: '「ファイルの追加など」ボタンが見つかりません' };
291
292
  }
292
- if (!deepResearchItem) {
293
+ response.appendResponseLine('✅ メニューボタンをクリック');
294
+ await new Promise((resolve) => setTimeout(resolve, 500));
295
+ // Step 2: Click "Deep Research" menuitemradio from opened menu
296
+ const deepResearchClicked = await page.evaluate(() => {
297
+ const menuItems = Array.from(document.querySelectorAll('[role="menuitemradio"]'));
298
+ const deepResearchItem = menuItems.find(item => {
299
+ const text = item.textContent || '';
300
+ return text.includes('Deep Research') || text.includes('リサーチ');
301
+ });
302
+ if (deepResearchItem) {
303
+ deepResearchItem.click();
304
+ return true;
305
+ }
306
+ return false;
307
+ });
308
+ if (!deepResearchClicked) {
293
309
  return { success: false, error: 'Deep Research menuitemradio が見つかりません' };
294
310
  }
295
- await deepResearchItem.click();
296
- response.appendResponseLine('✅ Deep Research menuitemradio をクリック');
297
- await page.waitForTimeout(1000);
298
- // Step 3: 検証(composer-pill確認)
299
- const verification = await detectDeepResearchMode(page);
300
- if (!verification.isEnabled) {
311
+ response.appendResponseLine('✅ Deep Research を選択');
312
+ await new Promise((resolve) => setTimeout(resolve, 1000));
313
+ // Step 3: Verify DeepResearch mode is enabled
314
+ const verification = await page.evaluate(() => {
315
+ // Check placeholder text
316
+ const textarea = document.querySelector('textarea');
317
+ const placeholder = textarea?.getAttribute('placeholder') || '';
318
+ // Check for delete button
319
+ const deleteButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.includes('リサーチ:クリックして削除'));
320
+ // Check for sources button
321
+ const sourcesButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.includes('情報源'));
322
+ return {
323
+ hasCorrectPlaceholder: placeholder.includes('詳細なレポート') || placeholder.includes('リサーチ'),
324
+ hasDeleteButton: !!deleteButton,
325
+ hasSourcesButton: !!sourcesButton,
326
+ placeholder: placeholder
327
+ };
328
+ });
329
+ if (!verification.hasCorrectPlaceholder && !verification.hasDeleteButton) {
301
330
  return {
302
331
  success: false,
303
- error: 'DeepResearchモードの有効化に失敗しました(リサーチpillが検出されませんでした)',
332
+ error: `DeepResearchモードの有効化に失敗しました(プレースホルダー: ${verification.placeholder})`,
304
333
  };
305
334
  }
306
- response.appendResponseLine(`✅ モード確認完了: DeepResearch有効 (${verification.indicator})`);
335
+ response.appendResponseLine(`✅ モード確認完了: DeepResearch有効 (placeholder + delete button)`);
307
336
  return { success: true };
308
337
  }
309
338
  catch (error) {
@@ -414,13 +443,28 @@ async function handleConversationLoop(page, response, maxTurns = 5) {
414
443
  while (conversationTurns < maxTurns) {
415
444
  await new Promise((resolve) => setTimeout(resolve, 3000));
416
445
  const status = await page.evaluate(() => {
417
- // Check for research progress indicator
446
+ // Check for research progress indicator (NEW UI: loading sources button)
447
+ const allButtons = Array.from(document.querySelectorAll('button'));
448
+ // NEW: Look for "〜を読み込んでいます X 情報源" button
449
+ const loadingSourcesButton = allButtons.find(btn => {
450
+ const text = btn.textContent || '';
451
+ return text.includes('読み込んでいます') && text.includes('情報源');
452
+ });
453
+ // Also check for "リサーチを開始しています" button
454
+ const researchStartButton = allButtons.find(btn => btn.textContent?.includes('リサーチを開始しています'));
455
+ if (loadingSourcesButton || researchStartButton) {
456
+ return {
457
+ phase: 'researching',
458
+ indicator: loadingSourcesButton ? 'loading sources' : 'starting research'
459
+ };
460
+ }
461
+ // Legacy indicators (fallback)
418
462
  const progressIndicators = Array.from(document.querySelectorAll('div, span'));
419
463
  const isResearching = progressIndicators.some((el) => el.textContent?.includes('リサーチ中') ||
420
464
  el.textContent?.includes('Researching') ||
421
465
  el.textContent?.includes('情報を収集中'));
422
466
  if (isResearching) {
423
- return { phase: 'researching' };
467
+ return { phase: 'researching', indicator: 'text match' };
424
468
  }
425
469
  // Check if ChatGPT is asking a clarifying question
426
470
  const assistantMessages = document.querySelectorAll('[data-message-author-role="assistant"]');
@@ -430,8 +474,8 @@ async function handleConversationLoop(page, response, maxTurns = 5) {
430
474
  const latestMessage = assistantMessages[assistantMessages.length - 1];
431
475
  const messageText = latestMessage.textContent || '';
432
476
  // Check if it's still streaming
433
- const buttons = Array.from(document.querySelectorAll('button'));
434
- const isStreaming = buttons.some((btn) => {
477
+ const streamButtons = Array.from(document.querySelectorAll('button'));
478
+ const isStreaming = streamButtons.some((btn) => {
435
479
  const text = btn.textContent || '';
436
480
  const aria = btn.getAttribute('aria-label') || '';
437
481
  return (text.includes('ストリーミングの停止') ||
@@ -501,22 +545,39 @@ async function monitorResearch(page, response, startTime) {
501
545
  while (Date.now() - startTime < MAX_WAIT_TIME) {
502
546
  await new Promise((resolve) => setTimeout(resolve, 5000));
503
547
  const researchStatus = await page.evaluate(() => {
504
- // Check if research completed
505
- const assistantMessages = document.querySelectorAll('[data-message-author-role="assistant"]');
506
- if (assistantMessages.length === 0) {
507
- return { completed: false, stillResearching: true };
548
+ const buttons = Array.from(document.querySelectorAll('button'));
549
+ // Check if still loading sources (NEW UI indicator)
550
+ const loadingSourcesButton = buttons.find(btn => {
551
+ const text = btn.textContent || '';
552
+ return text.includes('読み込んでいます') && text.includes('情報源');
553
+ });
554
+ // Check for "リサーチを開始しています" button
555
+ const researchStartButton = buttons.find(btn => btn.textContent?.includes('リサーチを開始しています'));
556
+ if (loadingSourcesButton || researchStartButton) {
557
+ const sourcesText = loadingSourcesButton?.textContent || '';
558
+ const match = sourcesText.match(/(\d+)\s*情報源/);
559
+ const sourcesCount = match ? match[1] : '?';
560
+ return {
561
+ completed: false,
562
+ stillResearching: true,
563
+ progress: `情報源読み込み中 (${sourcesCount}件)`
564
+ };
508
565
  }
509
- const latestMessage = assistantMessages[assistantMessages.length - 1];
510
- // Check if still researching
566
+ // Legacy: Check if still researching
511
567
  const progressIndicators = Array.from(document.querySelectorAll('div, span'));
512
568
  const isResearching = progressIndicators.some((el) => el.textContent?.includes('リサーチ中') ||
513
569
  el.textContent?.includes('Researching') ||
514
570
  el.textContent?.includes('情報を収集中'));
515
571
  if (isResearching) {
516
- return { completed: false, stillResearching: true };
572
+ return { completed: false, stillResearching: true, progress: 'リサーチ中' };
517
573
  }
574
+ // Check if research completed
575
+ const assistantMessages = document.querySelectorAll('[data-message-author-role="assistant"]');
576
+ if (assistantMessages.length === 0) {
577
+ return { completed: false, stillResearching: true, progress: '待機中' };
578
+ }
579
+ const latestMessage = assistantMessages[assistantMessages.length - 1];
518
580
  // Check if streaming
519
- const buttons = Array.from(document.querySelectorAll('button'));
520
581
  const isStreaming = buttons.some((btn) => {
521
582
  const text = btn.textContent || '';
522
583
  const aria = btn.getAttribute('aria-label') || '';
@@ -544,7 +605,8 @@ async function monitorResearch(page, response, startTime) {
544
605
  progressCounter++;
545
606
  if (progressCounter % 6 === 0) {
546
607
  const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
547
- response.appendResponseLine(`⏱️ ${elapsedSeconds}秒経過 - リサーチ継続中...`);
608
+ const progressText = researchStatus.progress || 'リサーチ継続中';
609
+ response.appendResponseLine(`⏱️ ${elapsedSeconds}秒経過 - ${progressText}...`);
548
610
  }
549
611
  }
550
612
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-devtools-mcp-for-extension",
3
- "version": "0.10.3",
3
+ "version": "0.11.0",
4
4
  "description": "MCP server for Chrome extension development with Web Store automation. Fork of chrome-devtools-mcp with extension-specific tools.",
5
5
  "type": "module",
6
6
  "bin": "./build/src/index.js",