@link-assistant/hive-mind 1.52.1 → 1.53.1
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/CHANGELOG.md +16 -0
- package/package.json +1 -1
- package/src/agent.lib.mjs +18 -11
- package/src/agent.prompts.lib.mjs +18 -0
- package/src/claude.lib.mjs +16 -5
- package/src/claude.prompts.lib.mjs +1 -0
- package/src/codex.lib.mjs +5 -0
- package/src/codex.prompts.lib.mjs +1 -0
- package/src/github.lib.mjs +59 -43
- package/src/interactive-mode.lib.mjs +12 -2
- package/src/opencode.lib.mjs +15 -0
- package/src/opencode.prompts.lib.mjs +18 -0
- package/src/playwright-mcp.lib.mjs +298 -0
- package/src/solve.auto-merge-helpers.lib.mjs +9 -6
- package/src/solve.auto-merge.lib.mjs +28 -20
- package/src/solve.config.lib.mjs +6 -1
- package/src/solve.mjs +36 -35
- package/src/solve.progress-monitoring.lib.mjs +16 -11
- package/src/solve.repo-setup.lib.mjs +8 -4
- package/src/solve.repository.lib.mjs +6 -4
- package/src/solve.restart-shared.lib.mjs +29 -2
- package/src/solve.results.lib.mjs +64 -3
- package/src/solve.session.lib.mjs +25 -13
- package/src/solve.watch.lib.mjs +7 -2
- package/src/tool-comments.lib.mjs +271 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.53.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c0e8c6d: Fix `--auto-attach-solution-summary` falsely detecting solve.mjs's own session bookkeeping comments ("AI Work Session Started", "Solution Draft Log", "Auto-restart", "Ready to merge", etc.) as AI-authored comments, which caused the solution summary to always be suppressed even when the AI session produced no comments of its own.
|
|
8
|
+
|
|
9
|
+
The fix introduces a new `src/tool-comments.lib.mjs` module as the single source of truth for every marker string embedded in tool-posted comments, along with in-memory tracking of the GitHub comment IDs that solve.mjs itself creates during a session. `checkForAiCreatedComments` now uses the tracked ID set as the primary filter — any comment the tool posted in this session is excluded regardless of body text — and falls back to marker-based substring matching only when an ID was not captured.
|
|
10
|
+
|
|
11
|
+
Every tool-posting site (`solve.session.lib.mjs`, `solve.auto-merge.lib.mjs`, `solve.watch.lib.mjs`, `github.lib.mjs`'s `attachLogToGitHub`/`attachTruncatedLog`/`attachRegularComment`, `claude.lib.mjs`'s force-kill notice, `interactive-mode.lib.mjs`, `solve.progress-monitoring.lib.mjs`, `solve.repo-setup.lib.mjs`, `solve.repository.lib.mjs`, and `solve.mjs`'s usage-limit notifications) now routes through `postTrackedComment` / `postTrackedCommentFromFile`, so every solve-posted comment is registered and filtered correctly across all supported AI tools (claude, codex, agent, opencode). See issue #1625.
|
|
12
|
+
|
|
13
|
+
## 1.53.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- 906f61e: Add Playwright MCP browser automation fallback hints to all tools (opencode, agent), WebSearch fallback guidance to all tools (claude, codex, opencode, agent), and --no-playwright-mcp flag to physically disable Playwright MCP server connection per session without affecting global registration.
|
|
18
|
+
|
|
3
19
|
## 1.52.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/package.json
CHANGED
package/src/agent.lib.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { detectUsageLimit, formatUsageLimitMessage } from './usage-limit.lib.mjs
|
|
|
20
20
|
import { sanitizeObjectStrings } from './unicode-sanitization.lib.mjs';
|
|
21
21
|
import Decimal from 'decimal.js-light';
|
|
22
22
|
import { agentModels, defaultModels, freeToBaseModelMap } from './models/index.mjs';
|
|
23
|
+
import { checkPlaywrightMcpPackageAvailability, getAgentPlaywrightMcpDisableEnv } from './playwright-mcp.lib.mjs';
|
|
23
24
|
import { createAgentTokenUsage, accumulateAgentStepFinishUsage, parseAgentTokenUsage } from './agent-token-usage.lib.mjs';
|
|
24
25
|
|
|
25
26
|
export { createAgentTokenUsage, accumulateAgentStepFinishUsage, parseAgentTokenUsage };
|
|
@@ -320,6 +321,9 @@ export const handleAgentRuntimeSwitch = async () => {
|
|
|
320
321
|
await log('ℹ️ Agent runtime handling not required for this operation');
|
|
321
322
|
};
|
|
322
323
|
|
|
324
|
+
/** Check if Playwright MCP is available for Agent @returns {Promise<boolean>} */
|
|
325
|
+
export const checkPlaywrightMcpAvailability = checkPlaywrightMcpPackageAvailability;
|
|
326
|
+
|
|
323
327
|
// Main function to execute Agent with prompts and settings
|
|
324
328
|
export const executeAgent = async params => {
|
|
325
329
|
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, agentPath = 'agent', $ } = params;
|
|
@@ -439,6 +443,19 @@ export const executeAgentCommand = async params => {
|
|
|
439
443
|
await log(` Memory: ${resourcesBefore.memory.split('\n')[1]}`, { verbose: true });
|
|
440
444
|
await log(` Load: ${resourcesBefore.load}`, { verbose: true });
|
|
441
445
|
|
|
446
|
+
// Issue #1521: Build environment for agent process.
|
|
447
|
+
// Pass LINK_ASSISTANT_AGENT_VERBOSE env var when --verbose is enabled so verbose logging is initialized at module load time.
|
|
448
|
+
const agentEnv = { ...process.env };
|
|
449
|
+
if (argv.verbose) {
|
|
450
|
+
agentEnv.LINK_ASSISTANT_AGENT_VERBOSE = 'true';
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Apply Playwright MCP session state before launching Agent.
|
|
454
|
+
if (argv.playwrightMcp === false) {
|
|
455
|
+
Object.assign(agentEnv, await getAgentPlaywrightMcpDisableEnv({ env: agentEnv, cwd: tempDir, log }));
|
|
456
|
+
await log('🎭 Playwright MCP physically disabled for this Agent session via --no-playwright-mcp', { verbose: true });
|
|
457
|
+
}
|
|
458
|
+
|
|
442
459
|
// Build Agent command
|
|
443
460
|
let execCommand;
|
|
444
461
|
|
|
@@ -473,17 +490,6 @@ export const executeAgentCommand = async params => {
|
|
|
473
490
|
// Pipe the prompt file to agent via stdin
|
|
474
491
|
// Use agentArgs which includes --model and optionally --verbose
|
|
475
492
|
|
|
476
|
-
// Issue #1521: Build environment for agent process
|
|
477
|
-
// Pass LINK_ASSISTANT_AGENT_VERBOSE env var when --verbose is enabled
|
|
478
|
-
// This ensures Flag.LINK_ASSISTANT_AGENT_VERBOSE is true at module load time inside the agent,
|
|
479
|
-
// which is required for HTTP request/response logging to work.
|
|
480
|
-
// The --verbose CLI flag alone is not sufficient because the agent's Flag module
|
|
481
|
-
// reads the env var at initialization, before yargs middleware calls Flag.setVerbose().
|
|
482
|
-
const agentEnv = { ...process.env };
|
|
483
|
-
if (argv.verbose) {
|
|
484
|
-
agentEnv.LINK_ASSISTANT_AGENT_VERBOSE = 'true';
|
|
485
|
-
}
|
|
486
|
-
|
|
487
493
|
execCommand = $({
|
|
488
494
|
cwd: tempDir,
|
|
489
495
|
mirror: false,
|
|
@@ -1041,6 +1047,7 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
|
|
|
1041
1047
|
export default {
|
|
1042
1048
|
validateAgentConnection,
|
|
1043
1049
|
handleAgentRuntimeSwitch,
|
|
1050
|
+
checkPlaywrightMcpAvailability,
|
|
1044
1051
|
executeAgent,
|
|
1045
1052
|
executeAgentCommand,
|
|
1046
1053
|
checkForUncommittedChanges,
|
|
@@ -225,6 +225,24 @@ GitHub CLI command patterns.
|
|
|
225
225
|
- When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
|
|
226
226
|
- When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
|
|
227
227
|
- When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${
|
|
228
|
+
argv && argv.promptPlaywrightMcp
|
|
229
|
+
? `
|
|
230
|
+
|
|
231
|
+
Playwright MCP usage (browser automation via MCP tools).
|
|
232
|
+
- When you develop frontend web applications (HTML, CSS, JavaScript, React, Vue, Angular, etc.), use Playwright MCP tools to test the UI in a real browser.
|
|
233
|
+
- When WebFetch tool fails to retrieve expected content (e.g., returns empty content, JavaScript-rendered pages, or login-protected pages), use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for web browsing.
|
|
234
|
+
- When WebSearch tool fails or returns insufficient results, use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for internet search.
|
|
235
|
+
- When you need to interact with dynamic web pages that require JavaScript execution, use Playwright MCP tools.
|
|
236
|
+
- When you need to visually verify how a web page looks or take screenshots, use browser_take_screenshot from Playwright MCP.
|
|
237
|
+
- When you need to fill forms, click buttons, or perform user interactions on web pages, use Playwright MCP tools (browser_click, browser_type, browser_fill_form).
|
|
238
|
+
- When you need to test responsive design or different viewport sizes, use browser_resize from Playwright MCP.
|
|
239
|
+
- When you finish using the browser, close it with browser_close to free resources.
|
|
240
|
+
- When reproducing UI bugs, use browser_take_screenshot to capture the problem state before implementing any fix.
|
|
241
|
+
- When fixing UI bugs, take before/after screenshots to provide visual evidence of the fix for human verification.
|
|
242
|
+
- When creating UI tests, save baseline screenshots to the repository for visual regression testing.
|
|
243
|
+
- When verifying UI fixes, compare screenshots to ensure the fix does not introduce unintended visual changes.`
|
|
244
|
+
: ''
|
|
245
|
+
}${
|
|
228
246
|
modelSupportsVision
|
|
229
247
|
? `
|
|
230
248
|
|
package/src/claude.lib.mjs
CHANGED
|
@@ -16,8 +16,10 @@ import { sanitizeObjectStrings } from './unicode-sanitization.lib.mjs';
|
|
|
16
16
|
import Decimal from 'decimal.js-light';
|
|
17
17
|
import { displayBudgetStats, createEmptySubSessionUsage, accumulateModelUsage, displayModelUsage, displayCostComparison, mergeResultModelUsage, createSubAgentCallEntry, accumulateSubAgentUsage } from './claude.budget-stats.lib.mjs';
|
|
18
18
|
import { buildClaudeResumeCommand } from './claude.command-builder.lib.mjs';
|
|
19
|
+
import { SESSION_FORCE_KILLED_MARKER, postTrackedComment } from './tool-comments.lib.mjs'; // Issue #1625
|
|
19
20
|
import { handleClaudeRuntimeSwitch } from './claude.runtime-switch.lib.mjs'; // see issue #1141
|
|
20
21
|
import { CLAUDE_MODELS as availableModels } from './models/index.mjs'; // Issue #1221
|
|
22
|
+
import { buildMcpConfigWithoutPlaywright } from './playwright-mcp.lib.mjs';
|
|
21
23
|
export { availableModels }; // Re-export for backward compatibility
|
|
22
24
|
const showResumeCommand = async (sessionId, tempDir, claudePath, model, log) => {
|
|
23
25
|
if (!sessionId || !tempDir) return;
|
|
@@ -772,6 +774,14 @@ export const executeClaudeCommand = async params => {
|
|
|
772
774
|
await log(`🔄 Resuming from session: ${argv.resume}`);
|
|
773
775
|
claudeArgs = `--resume ${argv.resume} ${claudeArgs}`;
|
|
774
776
|
}
|
|
777
|
+
let mcpConfigPath = null;
|
|
778
|
+
if (argv.playwrightMcp === false) {
|
|
779
|
+
mcpConfigPath = await buildMcpConfigWithoutPlaywright(log);
|
|
780
|
+
if (mcpConfigPath) {
|
|
781
|
+
claudeArgs += ` --strict-mcp-config --mcp-config "${mcpConfigPath}"`;
|
|
782
|
+
await log('🎭 Playwright MCP physically disabled for this session via --strict-mcp-config', { verbose: true });
|
|
783
|
+
}
|
|
784
|
+
}
|
|
775
785
|
claudeArgs += ` -p "${escapedPrompt}" --append-system-prompt "${escapedSystemPrompt}"`;
|
|
776
786
|
const fullCommand = `(cd "${tempDir}" && ${claudePath} ${claudeArgs} | jq -c .)`;
|
|
777
787
|
await log(`\n${formatAligned('📝', 'Raw command:', '')}`);
|
|
@@ -795,11 +805,12 @@ export const executeClaudeCommand = async params => {
|
|
|
795
805
|
if (!isNewVersion && thinkLevel) await log(`📊 Thinking level (via keywords): ${thinkLevel}`, { verbose: true });
|
|
796
806
|
}
|
|
797
807
|
const simpleEscapedSystem = systemPrompt.replace(/"/g, '\\"');
|
|
808
|
+
const mcpDisableArgs = mcpConfigPath ? ['--strict-mcp-config', '--mcp-config', mcpConfigPath] : [];
|
|
798
809
|
if (argv.resume) {
|
|
799
810
|
const simpleEscapedPrompt = prompt.replace(/"/g, '\\"');
|
|
800
|
-
execCommand = $({ cwd: tempDir, mirror: false, env: claudeEnv })`${claudePath} --resume ${argv.resume} --output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel} -p "${simpleEscapedPrompt}" --append-system-prompt "${simpleEscapedSystem}"`;
|
|
811
|
+
execCommand = $({ cwd: tempDir, mirror: false, env: claudeEnv })`${claudePath} --resume ${argv.resume} --output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel} ${mcpDisableArgs} -p "${simpleEscapedPrompt}" --append-system-prompt "${simpleEscapedSystem}"`;
|
|
801
812
|
} else {
|
|
802
|
-
execCommand = $({ cwd: tempDir, stdin: prompt, mirror: false, env: claudeEnv })`${claudePath} --output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel} --append-system-prompt "${simpleEscapedSystem}"`;
|
|
813
|
+
execCommand = $({ cwd: tempDir, stdin: prompt, mirror: false, env: claudeEnv })`${claudePath} --output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel} ${mcpDisableArgs} --append-system-prompt "${simpleEscapedSystem}"`;
|
|
803
814
|
}
|
|
804
815
|
await log(`${formatAligned('📋', 'Command details:', '')}`);
|
|
805
816
|
await log(formatAligned('📂', 'Working directory:', tempDir, 2));
|
|
@@ -1197,9 +1208,9 @@ export const executeClaudeCommand = async params => {
|
|
|
1197
1208
|
const timeoutType = isActivityTimeout ? 'activity' : 'startup';
|
|
1198
1209
|
const sessionInfo = sessionId ? `\nSession ID: \`${sessionId}\`` : '';
|
|
1199
1210
|
const resumeInfo = isStartupTimeout ? 'Session will be restarted (fresh start).' : `Session will be resumed with \`--resume\` (context preserved).`;
|
|
1200
|
-
const commentBody = `## :warning:
|
|
1201
|
-
|
|
1202
|
-
await log(` Posted force-kill notification to PR #${prNumber}`, { verbose: true });
|
|
1211
|
+
const commentBody = `## :warning: ${SESSION_FORCE_KILLED_MARKER} (${timeoutType} timeout)\n\nThe working session was force-killed due to ${timeoutType} timeout (no stream output for ${isActivityTimeout ? timeouts.streamActivityMs / 1000 : timeouts.streamStartupMs / 1000}s).\n\n**Auto-resuming**: Retry ${retryCount + 1}/${maxRetries} in ${delayLabel}. ${resumeInfo}${sessionInfo}\n\n*This is an automated notification — the session will continue automatically.*`;
|
|
1212
|
+
const posted = await postTrackedComment({ $, owner, repo, targetNumber: prNumber, body: commentBody });
|
|
1213
|
+
await log(posted.ok ? ` Posted force-kill notification to PR #${prNumber}${posted.commentId ? ` (id=${posted.commentId})` : ''}` : ` Warning: Could not post force-kill comment to PR: ${posted.stderr || 'unknown error'}`, { verbose: true });
|
|
1203
1214
|
} catch (commentError) {
|
|
1204
1215
|
await log(` Warning: Could not post force-kill comment to PR: ${commentError.message}`, { verbose: true });
|
|
1205
1216
|
}
|
|
@@ -268,6 +268,7 @@ GitHub CLI command patterns.
|
|
|
268
268
|
Playwright MCP usage (browser automation via mcp__playwright__* tools).
|
|
269
269
|
- When you develop frontend web applications (HTML, CSS, JavaScript, React, Vue, Angular, etc.), use Playwright MCP tools to test the UI in a real browser.
|
|
270
270
|
- When WebFetch tool fails to retrieve expected content (e.g., returns empty content, JavaScript-rendered pages, or login-protected pages), use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for web browsing.
|
|
271
|
+
- When WebSearch tool fails or returns insufficient results, use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for internet search.
|
|
271
272
|
- When you need to interact with dynamic web pages that require JavaScript execution, use Playwright MCP tools.
|
|
272
273
|
- When you need to visually verify how a web page looks or take screenshots, use browser_take_screenshot from Playwright MCP.
|
|
273
274
|
- When you need to fill forms, click buttons, or perform user interactions on web pages, use Playwright MCP tools (browser_click, browser_type, browser_fill_form).
|
package/src/codex.lib.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import { sanitizeObjectStrings } from './unicode-sanitization.lib.mjs';
|
|
|
21
21
|
import { mapModelToId, resolveCodexReasoningEffort } from './codex.options.lib.mjs';
|
|
22
22
|
import { createInteractiveHandler } from './interactive-mode.lib.mjs';
|
|
23
23
|
import { initProgressMonitoring } from './solve.progress-monitoring.lib.mjs';
|
|
24
|
+
import { getCodexPlaywrightMcpDisableConfigArgs } from './playwright-mcp.lib.mjs';
|
|
24
25
|
|
|
25
26
|
const CODEX_USAGE_FIELD_NAMES = ['input_tokens', 'cached_input_tokens', 'output_tokens', 'cache_write_tokens', 'cache_creation_input_tokens', 'reasoning_tokens', 'input_tokens_details.cached_tokens', 'input_tokens_details.cache_read_tokens', 'input_tokens_details.cache_write_tokens', 'input_tokens_details.cache_creation_tokens', 'input_tokens_details.cache_creation_input_tokens', 'output_tokens_details.reasoning_tokens'];
|
|
26
27
|
const getCodexExecEnv = (verbose = false) => (verbose ? { ...process.env, RUST_LOG: 'debug' } : { ...process.env });
|
|
@@ -584,6 +585,10 @@ export const executeCodexCommand = async params => {
|
|
|
584
585
|
} else {
|
|
585
586
|
codexArgs += ` --model ${shellQuote(mappedModel)}`;
|
|
586
587
|
}
|
|
588
|
+
const codexPlaywrightMcpDisableConfigArgs = argv.playwrightMcp === false ? await getCodexPlaywrightMcpDisableConfigArgs(log) : [];
|
|
589
|
+
for (const arg of codexPlaywrightMcpDisableConfigArgs) {
|
|
590
|
+
codexArgs += ` ${shellQuote(arg)}`;
|
|
591
|
+
}
|
|
587
592
|
codexArgs += ` --json --skip-git-repo-check -o ${shellQuote(lastMessageFile)} -c ${shellQuote(`model_reasoning_effort=${reasoningEffort}`)} -c ${shellQuote('model_reasoning_summary=auto')} --dangerously-bypass-approvals-and-sandbox`;
|
|
588
593
|
|
|
589
594
|
const fullCommand = `(cd ${shellQuote(tempDir)} && cat ${shellQuote(promptFile)} | ${codexPath} ${codexArgs})`;
|
|
@@ -260,6 +260,7 @@ GitHub CLI command patterns.
|
|
|
260
260
|
Playwright MCP usage (browser automation via MCP tools).
|
|
261
261
|
- When you develop frontend web applications or debug UI issues, use Playwright MCP tools to test the UI in a real browser.
|
|
262
262
|
- When simple fetch-based browsing is insufficient for dynamic pages, use Playwright MCP browser automation as a fallback.
|
|
263
|
+
- When WebSearch tool fails or returns insufficient results, use Playwright MCP browser automation as a fallback for internet search.
|
|
263
264
|
- When reproducing or verifying UI bugs, take before/after screenshots and close the browser when finished.`
|
|
264
265
|
: ''
|
|
265
266
|
}${
|
package/src/github.lib.mjs
CHANGED
|
@@ -16,6 +16,9 @@ export { getToolDisplayName }; // Re-export for use by other modules
|
|
|
16
16
|
import { buildBudgetStatsString } from './claude.budget-stats.lib.mjs';
|
|
17
17
|
import { buildCostInfoString } from './github-cost-info.lib.mjs';
|
|
18
18
|
export { buildCostInfoString };
|
|
19
|
+
// Issue #1625: Named marker constants (single source of truth) + in-memory
|
|
20
|
+
// tracking for tool-posted comments. See tool-comments.lib.mjs for design.
|
|
21
|
+
import { SOLUTION_DRAFT_LOG_MARKER, SOLUTION_DRAFT_FAILED_MARKER, SOLUTION_DRAFT_FINISHED_WITH_ERRORS_MARKER, USAGE_LIMIT_REACHED_MARKER, NOW_WORKING_SESSION_IS_ENDED_MARKER, postTrackedComment, postTrackedCommentFromFile } from './tool-comments.lib.mjs';
|
|
19
22
|
export const maskGitHubToken = maskToken; // Alias for backward compatibility
|
|
20
23
|
export const escapeCodeBlocksInLog = logContent => logContent.replace(/```/g, '\\`\\`\\`'); // Escape ``` in logs
|
|
21
24
|
export const checkFileInBranch = async (owner, repo, fileName, branchName) => {
|
|
@@ -260,13 +263,16 @@ Could you please enable the **"Allow edits by maintainers"** checkbox? This will
|
|
|
260
263
|
3. Check the box ✅
|
|
261
264
|
Alternatively, you can enable it when creating/editing the PR. See: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork
|
|
262
265
|
Thank you! 🙏`;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
+
// Issue #1625: track this comment so it's not counted as AI-authored by
|
|
267
|
+
// --auto-attach-solution-summary. The "Allow edits by maintainers"
|
|
268
|
+
// phrase embedded above matches MAINTAINER_ACCESS_REQUEST_MARKER as a
|
|
269
|
+
// fallback if the ID capture fails.
|
|
270
|
+
const posted = await postTrackedComment({ $, owner, repo, targetNumber: prNumber, body: commentBody });
|
|
271
|
+
if (posted.ok) {
|
|
272
|
+
await log(`✅ Comment posted successfully${posted.commentId ? ` (id=${posted.commentId})` : ''}`, { verbose: true });
|
|
266
273
|
return true;
|
|
267
274
|
} else {
|
|
268
|
-
|
|
269
|
-
await log(`⚠️ Warning: Failed to post comment: ${cleanErrorMessage(errorOutput)}`, { level: 'warning' });
|
|
275
|
+
await log(`⚠️ Warning: Failed to post comment: ${cleanErrorMessage(posted.stderr || 'unknown error')}`, { level: 'warning' });
|
|
270
276
|
return false;
|
|
271
277
|
}
|
|
272
278
|
} catch (error) {
|
|
@@ -295,7 +301,7 @@ export async function attachLogToGitHub(options) {
|
|
|
295
301
|
sanitizeLogContent,
|
|
296
302
|
verbose = false,
|
|
297
303
|
errorMessage,
|
|
298
|
-
customTitle =
|
|
304
|
+
customTitle = `🤖 ${SOLUTION_DRAFT_LOG_MARKER}`,
|
|
299
305
|
sessionId = null,
|
|
300
306
|
tempDir = null,
|
|
301
307
|
anthropicTotalCostUSD = null,
|
|
@@ -316,7 +322,6 @@ export async function attachLogToGitHub(options) {
|
|
|
316
322
|
} = options;
|
|
317
323
|
const budgetStats = budgetStatsData ? buildBudgetStatsString(budgetStatsData.tokenUsage, budgetStatsData.subAgentCalls) : '';
|
|
318
324
|
const targetName = targetType === 'pr' ? 'Pull Request' : 'Issue';
|
|
319
|
-
const ghCommand = targetType === 'pr' ? 'pr' : 'issue';
|
|
320
325
|
try {
|
|
321
326
|
// Issue #1212: Check disk space before attempting log upload (100MB minimum)
|
|
322
327
|
try {
|
|
@@ -411,7 +416,7 @@ export async function attachLogToGitHub(options) {
|
|
|
411
416
|
// regardless of whether a generic errorMessage is provided.
|
|
412
417
|
if (isUsageLimit) {
|
|
413
418
|
// Usage limit error format - separate from general failures
|
|
414
|
-
logComment = `## ⏳
|
|
419
|
+
logComment = `## ⏳ ${USAGE_LIMIT_REACHED_MARKER}
|
|
415
420
|
|
|
416
421
|
The automated solution draft was interrupted because the ${toolName} usage limit was reached.
|
|
417
422
|
|
|
@@ -477,7 +482,7 @@ ${logContent}
|
|
|
477
482
|
${footerNote}`;
|
|
478
483
|
} else if (errorMessage) {
|
|
479
484
|
// Failure log format (non-usage-limit errors)
|
|
480
|
-
logComment = `## 🚨
|
|
485
|
+
logComment = `## 🚨 ${SOLUTION_DRAFT_FAILED_MARKER}
|
|
481
486
|
The automated solution draft encountered an error:
|
|
482
487
|
\`\`\`
|
|
483
488
|
${errorMessage}
|
|
@@ -493,11 +498,11 @@ ${logContent}
|
|
|
493
498
|
</details>
|
|
494
499
|
|
|
495
500
|
---
|
|
496
|
-
|
|
501
|
+
*${NOW_WORKING_SESSION_IS_ENDED_MARKER}, feel free to review and add any feedback on the solution draft.*`;
|
|
497
502
|
} else if (errorDuringExecution) {
|
|
498
503
|
// Issue #1088: "Finished with errors" format - work may have been completed but errors occurred
|
|
499
504
|
const costInfo = buildCostInfoString(totalCostUSD, anthropicTotalCostUSD, pricingInfo);
|
|
500
|
-
logComment = `## ⚠️
|
|
505
|
+
logComment = `## ⚠️ ${SOLUTION_DRAFT_FINISHED_WITH_ERRORS_MARKER}
|
|
501
506
|
This log file contains the complete execution trace of the AI ${targetType === 'pr' ? 'solution draft' : 'analysis'} process.${costInfo}${budgetStats}${modelInfoString}
|
|
502
507
|
|
|
503
508
|
> **Note**: The session encountered errors during execution, but some work may have been completed. Please review the changes carefully.
|
|
@@ -512,20 +517,23 @@ ${logContent}
|
|
|
512
517
|
</details>
|
|
513
518
|
|
|
514
519
|
---
|
|
515
|
-
|
|
520
|
+
*${NOW_WORKING_SESSION_IS_ENDED_MARKER}, feel free to review and add any feedback on the solution draft.*`;
|
|
516
521
|
} else {
|
|
517
522
|
const costInfo = buildCostInfoString(totalCostUSD, anthropicTotalCostUSD, pricingInfo);
|
|
518
523
|
// Determine title based on session type (Issue #1152)
|
|
524
|
+
// Issue #1625: Every title variant embeds SOLUTION_DRAFT_LOG_MARKER so
|
|
525
|
+
// the filter in checkForAiCreatedComments matches every variant with a
|
|
526
|
+
// single substring check against the centralized marker constant.
|
|
519
527
|
let title = customTitle;
|
|
520
528
|
let sessionNote = '';
|
|
521
529
|
if (sessionType === 'auto-resume') {
|
|
522
|
-
title =
|
|
530
|
+
title = `🔄 ${SOLUTION_DRAFT_LOG_MARKER} (auto resume on limit reset)`;
|
|
523
531
|
sessionNote = '\n\n**Note**: This session was automatically resumed after a usage limit reset, with the previous context preserved.';
|
|
524
532
|
} else if (sessionType === 'auto-restart') {
|
|
525
|
-
title =
|
|
533
|
+
title = `🔄 ${SOLUTION_DRAFT_LOG_MARKER} (auto restart on limit reset)`;
|
|
526
534
|
sessionNote = '\n\n**Note**: This session was automatically restarted after a usage limit reset (fresh start).';
|
|
527
535
|
} else if (sessionType === 'resume') {
|
|
528
|
-
title =
|
|
536
|
+
title = `🔄 ${SOLUTION_DRAFT_LOG_MARKER} (Resumed)`;
|
|
529
537
|
sessionNote = '\n\n**Note**: This session was manually resumed using the --resume flag.';
|
|
530
538
|
}
|
|
531
539
|
logComment = `## ${title}
|
|
@@ -541,11 +549,10 @@ ${logContent}
|
|
|
541
549
|
</details>
|
|
542
550
|
|
|
543
551
|
---
|
|
544
|
-
|
|
552
|
+
*${NOW_WORKING_SESSION_IS_ENDED_MARKER}, feel free to review and add any feedback on the solution draft.*`;
|
|
545
553
|
}
|
|
546
554
|
// Check GitHub comment size limit or large file mode
|
|
547
555
|
// Issue #1173: Also use gh-upload-log for large files, not just long comments
|
|
548
|
-
let commentResult;
|
|
549
556
|
if (useLargeFileMode || logComment.length > githubLimits.commentMaxSize) {
|
|
550
557
|
if (useLargeFileMode) {
|
|
551
558
|
await log(` 📁 Log file too large for inline comment (${Math.round(logStats.size / 1024 / 1024)}MB), using gh-upload-log`);
|
|
@@ -604,7 +611,7 @@ ${logContent}
|
|
|
604
611
|
// For usage limit cases, always use the dedicated format regardless of errorMessage
|
|
605
612
|
if (isUsageLimit) {
|
|
606
613
|
// Usage limit error format
|
|
607
|
-
logUploadComment = `## ⏳
|
|
614
|
+
logUploadComment = `## ⏳ ${USAGE_LIMIT_REACHED_MARKER}
|
|
608
615
|
|
|
609
616
|
The automated solution draft was interrupted because the ${toolName} usage limit was reached.
|
|
610
617
|
|
|
@@ -664,7 +671,7 @@ ${resumeCommand}
|
|
|
664
671
|
${uploadFooterNote}`;
|
|
665
672
|
} else if (errorMessage) {
|
|
666
673
|
// Failure log format (non-usage-limit errors)
|
|
667
|
-
logUploadComment = `## 🚨
|
|
674
|
+
logUploadComment = `## 🚨 ${SOLUTION_DRAFT_FAILED_MARKER}
|
|
668
675
|
The automated solution draft encountered an error:
|
|
669
676
|
\`\`\`
|
|
670
677
|
${errorMessage}
|
|
@@ -674,11 +681,11 @@ ${errorMessage}
|
|
|
674
681
|
- [View complete failure log](${logUrl})
|
|
675
682
|
|
|
676
683
|
---
|
|
677
|
-
|
|
684
|
+
*${NOW_WORKING_SESSION_IS_ENDED_MARKER}, feel free to review and add any feedback on the solution draft.*`;
|
|
678
685
|
} else if (errorDuringExecution) {
|
|
679
686
|
// Issue #1088: "Finished with errors" format - work may have been completed but errors occurred
|
|
680
687
|
const costInfo = buildCostInfoString(totalCostUSD, anthropicTotalCostUSD, pricingInfo);
|
|
681
|
-
logUploadComment = `## ⚠️
|
|
688
|
+
logUploadComment = `## ⚠️ ${SOLUTION_DRAFT_FINISHED_WITH_ERRORS_MARKER}
|
|
682
689
|
This log file contains the complete execution trace of the AI ${targetType === 'pr' ? 'solution draft' : 'analysis'} process.${costInfo}${budgetStats}${modelInfoString}
|
|
683
690
|
|
|
684
691
|
> **Note**: The session encountered errors during execution, but some work may have been completed. Please review the changes carefully.
|
|
@@ -687,22 +694,23 @@ This log file contains the complete execution trace of the AI ${targetType === '
|
|
|
687
694
|
- [View complete solution draft log](${logUrl})
|
|
688
695
|
|
|
689
696
|
---
|
|
690
|
-
|
|
697
|
+
*${NOW_WORKING_SESSION_IS_ENDED_MARKER}, feel free to review and add any feedback on the solution draft.*`;
|
|
691
698
|
} else {
|
|
692
699
|
// Success log format - use helper function for cost info
|
|
693
700
|
const costInfo = buildCostInfoString(totalCostUSD, anthropicTotalCostUSD, pricingInfo);
|
|
694
701
|
// Determine title based on session type
|
|
695
702
|
// See: https://github.com/link-assistant/hive-mind/issues/1152
|
|
703
|
+
// Issue #1625: titles embed SOLUTION_DRAFT_LOG_MARKER (single source).
|
|
696
704
|
let title = customTitle;
|
|
697
705
|
let sessionNote = '';
|
|
698
706
|
if (sessionType === 'auto-resume') {
|
|
699
|
-
title =
|
|
707
|
+
title = `🔄 ${SOLUTION_DRAFT_LOG_MARKER} (auto resume on limit reset)`;
|
|
700
708
|
sessionNote = '\n**Note**: This session was automatically resumed after a usage limit reset, with the previous context preserved.\n';
|
|
701
709
|
} else if (sessionType === 'auto-restart') {
|
|
702
|
-
title =
|
|
710
|
+
title = `🔄 ${SOLUTION_DRAFT_LOG_MARKER} (auto restart on limit reset)`;
|
|
703
711
|
sessionNote = '\n**Note**: This session was automatically restarted after a usage limit reset (fresh start).\n';
|
|
704
712
|
} else if (sessionType === 'resume') {
|
|
705
|
-
title =
|
|
713
|
+
title = `🔄 ${SOLUTION_DRAFT_LOG_MARKER} (Resumed)`;
|
|
706
714
|
sessionNote = '\n**Note**: This session was manually resumed using the --resume flag.\n';
|
|
707
715
|
}
|
|
708
716
|
logUploadComment = `## ${title}
|
|
@@ -712,19 +720,22 @@ ${sessionNote}
|
|
|
712
720
|
- [View complete solution draft log](${logUrl})
|
|
713
721
|
|
|
714
722
|
---
|
|
715
|
-
|
|
723
|
+
*${NOW_WORKING_SESSION_IS_ENDED_MARKER}, feel free to review and add any feedback on the solution draft.*`;
|
|
716
724
|
}
|
|
717
725
|
const tempCommentFile = `/tmp/log-upload-comment-${targetType}-${Date.now()}.md`;
|
|
718
726
|
await fs.writeFile(tempCommentFile, logUploadComment);
|
|
719
|
-
|
|
727
|
+
// Issue #1625: post via postTrackedCommentFromFile so the returned
|
|
728
|
+
// comment ID is registered in-memory and excluded from the
|
|
729
|
+
// "did the AI post anything?" check.
|
|
730
|
+
const posted = await postTrackedCommentFromFile({ $, owner, repo, targetNumber, bodyFile: tempCommentFile });
|
|
720
731
|
await fs.unlink(tempCommentFile).catch(() => {});
|
|
721
|
-
if (
|
|
722
|
-
await log(` ✅ Solution draft log uploaded to ${targetName} as ${isPublicRepo ? 'public' : 'private'} ${uploadTypeLabel}${chunkInfo}`);
|
|
732
|
+
if (posted.ok) {
|
|
733
|
+
await log(` ✅ Solution draft log uploaded to ${targetName} as ${isPublicRepo ? 'public' : 'private'} ${uploadTypeLabel}${chunkInfo}${posted.commentId ? ` (comment id=${posted.commentId})` : ''}`);
|
|
723
734
|
await log(` 🔗 Log URL: ${logUrl}`);
|
|
724
735
|
await log(` 📊 Log size: ${Math.round(logStats.size / 1024)}KB`);
|
|
725
736
|
return true;
|
|
726
737
|
} else {
|
|
727
|
-
await log(` ❌ Failed to post comment with log link: ${
|
|
738
|
+
await log(` ❌ Failed to post comment with log link: ${posted.stderr || 'unknown error'}`);
|
|
728
739
|
return false;
|
|
729
740
|
}
|
|
730
741
|
} else {
|
|
@@ -774,7 +785,7 @@ async function attachTruncatedLog(options) {
|
|
|
774
785
|
const maxContentLength = GITHUB_COMMENT_LIMIT - 500;
|
|
775
786
|
const truncatedContent = logContent.substring(0, maxContentLength) + '\n\n[... Log truncated due to length ...]';
|
|
776
787
|
|
|
777
|
-
const truncatedComment = `## 🤖
|
|
788
|
+
const truncatedComment = `## 🤖 ${SOLUTION_DRAFT_LOG_MARKER} (Truncated)
|
|
778
789
|
This log file contains the complete execution trace of the AI ${targetType === 'pr' ? 'solution draft' : 'analysis'} process.
|
|
779
790
|
⚠️ **Log was truncated** due to GitHub comment size limits.
|
|
780
791
|
|
|
@@ -788,20 +799,23 @@ ${truncatedContent}
|
|
|
788
799
|
</details>
|
|
789
800
|
|
|
790
801
|
---
|
|
791
|
-
|
|
802
|
+
*${NOW_WORKING_SESSION_IS_ENDED_MARKER}, feel free to review and add any feedback on the solution draft.*`;
|
|
792
803
|
const tempFile = `/tmp/log-truncated-comment-${targetType}-${Date.now()}.md`;
|
|
793
804
|
await fs.writeFile(tempFile, truncatedComment);
|
|
794
805
|
|
|
795
|
-
|
|
796
|
-
|
|
806
|
+
// Issue #1625: track the posted comment ID so it's excluded from the
|
|
807
|
+
// AI-authored-comment check in --auto-attach-solution-summary.
|
|
808
|
+
const posted = await postTrackedCommentFromFile({ $, owner, repo, targetNumber, bodyFile: tempFile });
|
|
797
809
|
await fs.unlink(tempFile).catch(() => {});
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
810
|
+
// ghCommand and targetName are retained in signature for symmetry with
|
|
811
|
+
// attachLogToGitHub's logging vocabulary.
|
|
812
|
+
void ghCommand;
|
|
813
|
+
if (posted.ok) {
|
|
814
|
+
await log(` ✅ Truncated solution draft log uploaded to ${targetName}${posted.commentId ? ` (comment id=${posted.commentId})` : ''}`);
|
|
801
815
|
await log(` 📊 Log size: ${Math.round(logStats.size / 1024)}KB (truncated)`);
|
|
802
816
|
return true;
|
|
803
817
|
} else {
|
|
804
|
-
await log(` ❌ Failed to upload truncated log: ${
|
|
818
|
+
await log(` ❌ Failed to upload truncated log: ${posted.stderr || 'unknown error'}`);
|
|
805
819
|
return false;
|
|
806
820
|
}
|
|
807
821
|
}
|
|
@@ -814,21 +828,23 @@ async function attachRegularComment(options, logComment) {
|
|
|
814
828
|
|
|
815
829
|
const targetName = targetType === 'pr' ? 'Pull Request' : 'Issue';
|
|
816
830
|
const ghCommand = targetType === 'pr' ? 'pr' : 'issue';
|
|
831
|
+
void ghCommand;
|
|
817
832
|
const logStats = await fs.stat(logFile);
|
|
818
833
|
|
|
819
834
|
const tempFile = `/tmp/log-comment-${targetType}-${Date.now()}.md`;
|
|
820
835
|
await fs.writeFile(tempFile, logComment);
|
|
821
836
|
|
|
822
|
-
|
|
823
|
-
|
|
837
|
+
// Issue #1625: track the posted comment ID so it's excluded from the
|
|
838
|
+
// AI-authored-comment check in --auto-attach-solution-summary.
|
|
839
|
+
const posted = await postTrackedCommentFromFile({ $, owner, repo, targetNumber, bodyFile: tempFile });
|
|
824
840
|
await fs.unlink(tempFile).catch(() => {});
|
|
825
841
|
|
|
826
|
-
if (
|
|
827
|
-
await log(` ✅ Solution draft log uploaded to ${targetName} as comment`);
|
|
842
|
+
if (posted.ok) {
|
|
843
|
+
await log(` ✅ Solution draft log uploaded to ${targetName} as comment${posted.commentId ? ` (id=${posted.commentId})` : ''}`);
|
|
828
844
|
await log(` 📊 Log size: ${Math.round(logStats.size / 1024)}KB`);
|
|
829
845
|
return true;
|
|
830
846
|
} else {
|
|
831
|
-
await log(` ❌ Failed to upload log to ${targetName}: ${
|
|
847
|
+
await log(` ❌ Failed to upload log to ${targetName}: ${posted.stderr || 'unknown error'}`);
|
|
832
848
|
return false;
|
|
833
849
|
}
|
|
834
850
|
}
|
|
@@ -33,6 +33,11 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { CONFIG, createCollapsible, createRawJsonSection, escapeMarkdown, execFileAsync, formatCost, formatDuration, getToolIcon, safeJsonStringify, sanitizeUnicode, truncateMiddle } from './interactive-mode.shared.lib.mjs';
|
|
36
|
+
// Issue #1625: track interactive-mode comment IDs so they're excluded from
|
|
37
|
+
// the "did the AI post anything?" check in checkForAiCreatedComments().
|
|
38
|
+
// Use the session-started marker as the single source of truth for the
|
|
39
|
+
// header string, keeping posting and filtering in lock-step.
|
|
40
|
+
import { INTERACTIVE_SESSION_STARTED_MARKER, trackToolCommentId } from './tool-comments.lib.mjs';
|
|
36
41
|
|
|
37
42
|
/**
|
|
38
43
|
* Creates an interactive mode handler for processing Claude/Codex CLI events
|
|
@@ -136,6 +141,11 @@ export const createInteractiveHandler = options => {
|
|
|
136
141
|
commentId = match ? match[1] || match[2] : null;
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
// Issue #1625: register this comment ID in the shared in-memory tracking
|
|
145
|
+
// set so --auto-attach-solution-summary correctly excludes it from the
|
|
146
|
+
// AI-authored-comment check. Tracking is a no-op when commentId is null.
|
|
147
|
+
trackToolCommentId(commentId);
|
|
148
|
+
|
|
139
149
|
if (verbose) {
|
|
140
150
|
await log(`✅ Interactive mode: Comment posted${commentId ? ` (ID: ${commentId})` : ''} (body: ${body.length} chars)`, { verbose: true });
|
|
141
151
|
}
|
|
@@ -288,7 +298,7 @@ export const createInteractiveHandler = options => {
|
|
|
288
298
|
const agents = data.agents || [];
|
|
289
299
|
const agentsList = agents.length > 0 ? agents.map(a => `\`${a}\``).join(', ') : '_None_';
|
|
290
300
|
|
|
291
|
-
const comment = `## 🚀
|
|
301
|
+
const comment = `## 🚀 ${INTERACTIVE_SESSION_STARTED_MARKER}
|
|
292
302
|
|
|
293
303
|
| Property | Value |
|
|
294
304
|
|----------|-------|
|
|
@@ -989,7 +999,7 @@ ${createRawJsonSection(data)}`;
|
|
|
989
999
|
state.sessionId = data.thread_id || data.session_id || null;
|
|
990
1000
|
state.startTime = Date.now();
|
|
991
1001
|
|
|
992
|
-
const comment = `## 🚀
|
|
1002
|
+
const comment = `## 🚀 ${INTERACTIVE_SESSION_STARTED_MARKER}
|
|
993
1003
|
|
|
994
1004
|
| Property | Value |
|
|
995
1005
|
|----------|-------|
|
package/src/opencode.lib.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import { timeouts } from './config.lib.mjs';
|
|
|
19
19
|
import { detectUsageLimit, formatUsageLimitMessage } from './usage-limit.lib.mjs';
|
|
20
20
|
import { sanitizeObjectStrings } from './unicode-sanitization.lib.mjs';
|
|
21
21
|
import { opencodeModels, defaultModels } from './models/index.mjs';
|
|
22
|
+
import { checkPlaywrightMcpPackageAvailability, getOpenCodePlaywrightMcpDisableEnv } from './playwright-mcp.lib.mjs';
|
|
22
23
|
import { createAgentTokenUsage, accumulateAgentStepFinishUsage, parseAgentTokenUsage as parseOpenCodeTokenUsage } from './agent-token-usage.lib.mjs';
|
|
23
24
|
import { calculateAgentPricing } from './agent.lib.mjs';
|
|
24
25
|
|
|
@@ -101,6 +102,9 @@ export const handleOpenCodeRuntimeSwitch = async () => {
|
|
|
101
102
|
await log('ℹ️ OpenCode runtime handling not required for this operation');
|
|
102
103
|
};
|
|
103
104
|
|
|
105
|
+
/** Check if Playwright MCP is available for OpenCode @returns {Promise<boolean>} */
|
|
106
|
+
export const checkPlaywrightMcpAvailability = checkPlaywrightMcpPackageAvailability;
|
|
107
|
+
|
|
104
108
|
// Main function to execute OpenCode with prompts and settings
|
|
105
109
|
export const executeOpenCode = async params => {
|
|
106
110
|
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, opencodePath = 'opencode', $ } = params;
|
|
@@ -241,6 +245,14 @@ export const executeOpenCodeCommand = async params => {
|
|
|
241
245
|
await log(` Memory: ${resourcesBefore.memory.split('\n')[1]}`, { verbose: true });
|
|
242
246
|
await log(` Load: ${resourcesBefore.load}`, { verbose: true });
|
|
243
247
|
|
|
248
|
+
const opencodeEnv = { ...process.env };
|
|
249
|
+
|
|
250
|
+
// Apply Playwright MCP session state before launching OpenCode.
|
|
251
|
+
if (argv.playwrightMcp === false) {
|
|
252
|
+
Object.assign(opencodeEnv, await getOpenCodePlaywrightMcpDisableEnv({ env: opencodeEnv, cwd: tempDir, log }));
|
|
253
|
+
await log('🎭 Playwright MCP physically disabled for this OpenCode session via --no-playwright-mcp', { verbose: true });
|
|
254
|
+
}
|
|
255
|
+
|
|
244
256
|
// Build OpenCode command
|
|
245
257
|
let execCommand;
|
|
246
258
|
|
|
@@ -288,11 +300,13 @@ export const executeOpenCodeCommand = async params => {
|
|
|
288
300
|
execCommand = $({
|
|
289
301
|
cwd: tempDir,
|
|
290
302
|
mirror: false,
|
|
303
|
+
env: opencodeEnv,
|
|
291
304
|
})`cat ${promptFile} | ${opencodePath} run --format json --resume ${argv.resume} --model ${mappedModel}`;
|
|
292
305
|
} else {
|
|
293
306
|
execCommand = $({
|
|
294
307
|
cwd: tempDir,
|
|
295
308
|
mirror: false,
|
|
309
|
+
env: opencodeEnv,
|
|
296
310
|
})`cat ${promptFile} | ${opencodePath} run --format json --model ${mappedModel}`;
|
|
297
311
|
}
|
|
298
312
|
|
|
@@ -653,6 +667,7 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
|
|
|
653
667
|
export default {
|
|
654
668
|
validateOpenCodeConnection,
|
|
655
669
|
handleOpenCodeRuntimeSwitch,
|
|
670
|
+
checkPlaywrightMcpAvailability,
|
|
656
671
|
executeOpenCode,
|
|
657
672
|
executeOpenCodeCommand,
|
|
658
673
|
checkForUncommittedChanges,
|
|
@@ -226,6 +226,24 @@ GitHub CLI command patterns.
|
|
|
226
226
|
- When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
|
|
227
227
|
- When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
|
|
228
228
|
- When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${
|
|
229
|
+
argv && argv.promptPlaywrightMcp
|
|
230
|
+
? `
|
|
231
|
+
|
|
232
|
+
Playwright MCP usage (browser automation via MCP tools).
|
|
233
|
+
- When you develop frontend web applications (HTML, CSS, JavaScript, React, Vue, Angular, etc.), use Playwright MCP tools to test the UI in a real browser.
|
|
234
|
+
- When WebFetch tool fails to retrieve expected content (e.g., returns empty content, JavaScript-rendered pages, or login-protected pages), use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for web browsing.
|
|
235
|
+
- When WebSearch tool fails or returns insufficient results, use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for internet search.
|
|
236
|
+
- When you need to interact with dynamic web pages that require JavaScript execution, use Playwright MCP tools.
|
|
237
|
+
- When you need to visually verify how a web page looks or take screenshots, use browser_take_screenshot from Playwright MCP.
|
|
238
|
+
- When you need to fill forms, click buttons, or perform user interactions on web pages, use Playwright MCP tools (browser_click, browser_type, browser_fill_form).
|
|
239
|
+
- When you need to test responsive design or different viewport sizes, use browser_resize from Playwright MCP.
|
|
240
|
+
- When you finish using the browser, close it with browser_close to free resources.
|
|
241
|
+
- When reproducing UI bugs, use browser_take_screenshot to capture the problem state before implementing any fix.
|
|
242
|
+
- When fixing UI bugs, take before/after screenshots to provide visual evidence of the fix for human verification.
|
|
243
|
+
- When creating UI tests, save baseline screenshots to the repository for visual regression testing.
|
|
244
|
+
- When verifying UI fixes, compare screenshots to ensure the fix does not introduce unintended visual changes.`
|
|
245
|
+
: ''
|
|
246
|
+
}${
|
|
229
247
|
modelSupportsVision
|
|
230
248
|
? `
|
|
231
249
|
|