Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 97 additions & 1 deletion .github/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,100 @@ To update this file, ensure that all sections are kept current with the latest a
- Add new sections as needed for significant changes in the project structure or processes.
- Ensure all technical terms are explained or linked to relevant documentation.
- Periodically review for outdated information and remove or update as necessary.
- If told to not do something, ensure this is also added to the "Dont Do that" section.
- If told to not do something, ensure this is also added to the "Dont Do that" section.

# Karpathy Guidelines

Behavioral guidelines to reduce common LLM coding mistakes.

**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.

## 1. Clarify Before Executing

**Resolve ambiguity up front — before spawning agents or starting any autonomous work.**

Do this in order, then stop and ask before proceeding:

1. **Understand the request.** Read what was asked closely and state your assumptions explicitly.
2. **Inspect the codebase.** Check the relevant code, conventions, and existing patterns. Note anything that's unclear or that could be interpreted in more than one way.
3. **Ask.** Raise every consequential question now, batched together — don't pick an interpretation silently, and don't defer the question into the work itself.

Throughout:

- If multiple interpretations exist, present them — don't choose one silently.
- If a simpler approach exists, say so. Push back when warranted.
- Don't invent APIs. Confirm a function, flag, or endpoint exists — and check its signature — before calling it. If you can't verify it, say so rather than guessing.

Only once these questions are resolved do you move to execution (§4).

## 2. Simplicity First

**Minimum code that solves the problem. Nothing speculative.**

- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.

Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.

## 3. Surgical Changes

**Touch only what you must. Clean up only your own mess.**

When editing existing code:

- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you spot problems outside your task — dead code, bugs — mention them; don't fix or delete them unless asked.

When your changes create orphans:

- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.

The test: every changed line should trace directly to the user's request.

## 4. Goal-Driven Execution

**Define success criteria. Loop until verified.**

Begin only once §1's questions are resolved. Then turn the task into a verifiable goal with an observable check. A passing test is the strongest check; a clean typecheck, running it and inspecting output, or diffing against expected output also count.

- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
- "Update the config" → "Apply it, start the service, and confirm the setting takes effect"

For multi-step tasks, state a brief plan:

```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```

When no obvious check exists (docs, exploration, some one-off scripts), ask how to verify success before starting. Don't report success you haven't actually observed.

Strong, agreed-upon criteria — settled in §1 — let you loop independently. Weak criteria ("make it work") force constant clarification, which is why §1 comes first.

## 5. Delegate Grunt Work Down a Tier

**Spend the capable model on judgment; push mechanical, low-context work to a cheaper model.**

Act as an orchestrator. Fan out grunt work — reading files, gathering context, simple searches, minor mechanical edits, other simple tool calls — to cheaper agents, then review their findings and any code changes yourself before acting on them. Keep the important and dangerous work — architecture, ambiguous decisions, risky or hard-to-reverse edits, final review — on the more capable model.

Which tier to spawn depends on who you are:

- **If you are Fable 5**, spawn Opus 4.8 agents for grunt work that still needs some capability; for genuinely simple tasks, hand off directly to Sonnet 5 instead. Review what they return.
- **If you are Opus 4.8**, and a task needs little knowledge or context, fan out to Sonnet 5 agents and review what they return.

Match the model to the task: pick the cheapest tier that can do the job well, and skip an intermediate tier when the work is simple enough for a lower one.

Guidelines:

- Only delegate work that is genuinely low-context and low-risk. If doing it well requires the capable model's judgment, keep it.
- Give each agent a self-contained task with clear success criteria (per §4) so you can verify its output without redoing the work.
- Never merge a delegated finding or edit unblocked — review it first. The cheaper model did the legwork; you own the decision.
9 changes: 9 additions & 0 deletions src/LogExpert.Resources/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@
<data name="Folder_open" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>images\png\48\Folder_open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="LogExpert_Common_UI_Message_ClipboardInUse" xml:space="preserve">
<value>Kopieren in die Zwischenablage nicht möglich. Sie wird von einer anderen Anwendung verwendet. Bitte versuchen Sie es erneut.</value>
</data>
<data name="LogExpert_Common_UI_Title_LogExpert" xml:space="preserve">
<value>LogExpert</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@
<data name="Folder_open" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>images\png\48\Folder_open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="LogExpert_Common_UI_Message_ClipboardInUse" xml:space="preserve">
<value>Could not copy to the clipboard. It is in use by another application. Please try again.</value>
</data>
<data name="LogExpert_Common_UI_Title_LogExpert" xml:space="preserve">
<value>LogExpert</value>
<comment>LogExpert</comment>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="LogExpert_Common_UI_Message_ClipboardInUse" xml:space="preserve">
<value>无法复制到剪贴板。剪贴板正被其他应用程序使用,请重试。</value>
</data>
<data name="LogExpert_Common_UI_Title_LogExpert" xml:space="preserve">
<value>LogExpert</value>
<comment>LogExpert</comment>
Expand Down
117 changes: 117 additions & 0 deletions src/LogExpert.Tests/Extensions/ClipboardHelperTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using LogExpert.UI.Extensions;

using NUnit.Framework;

using Vanara.PInvoke;

namespace LogExpert.Tests.Extensions;

/// <summary>
/// Tests for the hardened clipboard writes (see issue with "Requested Clipboard operation
/// did not succeed" / ExternalException, previously reported in #195). When another
/// application holds the clipboard open, LogExpert must report failure instead of crashing.
/// </summary>
[TestFixture]
[Apartment(ApartmentState.STA)]
[NonParallelizable]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Unit Tests")]
public class ClipboardHelperTests
{
[Test]
public void TrySetText_ClipboardAvailable_PlacesTextAndReturnsTrue ()
{
var ok = ClipboardHelper.TrySetText("LogExpert clipboard test");

Assert.That(ok, Is.True);
Assert.That(GetClipboardTextWithRetry(), Is.EqualTo("LogExpert clipboard test"));
}

/// <summary>
/// Reads the clipboard text, retrying briefly. Clipboard managers and similar tools
/// open the clipboard to inspect new content right after it changes, which can make an
/// immediate read-back fail or come up empty even though the write succeeded.
/// </summary>
private static string GetClipboardTextWithRetry ()
{
for (var i = 0; i < 20; i++)
{
var text = Clipboard.GetText();
if (!string.IsNullOrEmpty(text))
{
return text;
}

Thread.Sleep(100);
}

return string.Empty;
}

[Test]
public void TrySetText_ClipboardHeldByAnotherWindow_ReturnsFalseInsteadOfThrowing ()
{
using ClipboardLock clipboardLock = new();

var ok = ClipboardHelper.TrySetText("some text");

Assert.That(ok, Is.False);
}

[Test]
public void TrySetDataObject_ClipboardAvailable_PlacesDataAndReturnsTrue ()
{
var ok = ClipboardHelper.TrySetDataObject("LogExpert data object test");

Assert.That(ok, Is.True);
Assert.That(GetClipboardTextWithRetry(), Is.EqualTo("LogExpert data object test"));
}

[Test]
public void TrySetDataObject_ClipboardHeldByAnotherWindow_ReturnsFalseInsteadOfThrowing ()
{
using ClipboardLock clipboardLock = new();

var ok = ClipboardHelper.TrySetDataObject("some data");

Assert.That(ok, Is.False);
}

/// <summary>
/// Holds the Win32 clipboard open from a background thread (without closing it),
/// which makes every clipboard access in other threads/processes fail — the same
/// situation an external clipboard-monitoring tool causes.
/// </summary>
private sealed class ClipboardLock : IDisposable
{
private readonly Thread _thread;
private readonly ManualResetEventSlim _acquired = new(false);
private readonly ManualResetEventSlim _release = new(false);

public ClipboardLock ()
{
_thread = new Thread(() =>
{
if (!User32.OpenClipboard(HWND.NULL))
Comment thread
Hirogen marked this conversation as resolved.
Dismissed
{
throw new InvalidOperationException("Test setup failed: could not open the clipboard");
}

_acquired.Set();
_release.Wait();
_ = User32.CloseClipboard();
Comment thread
Hirogen marked this conversation as resolved.
Dismissed
})
{
IsBackground = true
};

_thread.Start();
_ = _acquired.Wait(TimeSpan.FromSeconds(5));
}

public void Dispose ()
{
_release.Set();
_ = _thread.Join(TimeSpan.FromSeconds(5));
}
}
}
14 changes: 10 additions & 4 deletions src/LogExpert.UI/Controls/LogWindow/LogWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1967,9 +1967,9 @@ private void OnEditModeCopyToolStripMenuItemClick (object sender, EventArgs e)
{
if (dataGridView.EditingControl is DataGridViewTextBoxEditingControl ctl)
{
if (!string.IsNullOrEmpty(ctl.SelectedText))
if (!string.IsNullOrEmpty(ctl.SelectedText) && !ClipboardHelper.TrySetText(ctl.SelectedText))
{
Clipboard.SetText(ctl.SelectedText);
StatusLineError(Resources.LogExpert_Common_UI_Message_ClipboardInUse);
}
}
}
Expand Down Expand Up @@ -5370,7 +5370,10 @@ private void CopyMarkedLinesToClipboard ()
data = new DataObject(DataFormats.UnicodeText, transformed);
}

Clipboard.SetDataObject(data);
if (!ClipboardHelper.TrySetDataObject(data))
{
StatusLineError(Resources.LogExpert_Common_UI_Message_ClipboardInUse);
}
}
else
{
Expand Down Expand Up @@ -5412,7 +5415,10 @@ private void CopyMarkedLinesToClipboard ()
}
}

Clipboard.SetDataObject(clipText.ToString());
if (!ClipboardHelper.TrySetDataObject(clipText.ToString()))
{
StatusLineError(Resources.LogExpert_Common_UI_Message_ClipboardInUse);
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/LogExpert.UI/Dialogs/ExceptionWindow.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Runtime.Versioning;

using LogExpert.UI.Extensions;

namespace LogExpert.UI.Dialogs;

[SupportedOSPlatform("windows")]
Expand Down Expand Up @@ -28,7 +30,7 @@ public ExceptionWindow (string errorText, string stackTrace)
_errorText = errorText;
_stackTrace = stackTrace;

stackTraceTextBox.Text = _errorText + @"\n\n" + _stackTrace;
stackTraceTextBox.Text = _errorText + Environment.NewLine + Environment.NewLine + _stackTrace;
stackTraceTextBox.Select(0, 0);

ResumeLayout();
Expand All @@ -51,7 +53,8 @@ private void ApplyResources ()

private void CopyToClipboard ()
{
Clipboard.SetText(_errorText + @"\n\n" + _stackTrace);
// Never let a failed clipboard access escalate out of the error dialog itself
_ = ClipboardHelper.TrySetText(_errorText + Environment.NewLine + Environment.NewLine + _stackTrace);
}

#endregion
Expand Down
11 changes: 9 additions & 2 deletions src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2417,8 +2417,15 @@ private void OnToolStripButtonBubblesClick (object sender, EventArgs e)
[SupportedOSPlatform("windows")]
private void OnCopyPathToClipboardToolStripMenuItemClick (object sender, EventArgs e)
{
var logWindow = dockPanel.ActiveContent as LogWindow.LogWindow;
Clipboard.SetText(logWindow.Title);
if (dockPanel.ActiveContent is not LogWindow.LogWindow logWindow)
{
return;
}

if (!ClipboardHelper.TrySetText(logWindow.Title))
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
{
_ = MessageBox.Show(this, Resources.LogExpert_Common_UI_Message_ClipboardInUse, Resources.LogExpert_Common_UI_Title_LogExpert, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}

private void OnFindInExplorerToolStripMenuItemClick (object sender, EventArgs e)
Expand Down
21 changes: 7 additions & 14 deletions src/LogExpert.UI/Dialogs/PluginHashDialog.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

using LogExpert.UI.Extensions;

namespace LogExpert.UI.Dialogs;

[SupportedOSPlatform("windows")]
Expand Down Expand Up @@ -55,25 +56,17 @@ private void ApplyResources (string pluginName)

private void OnButtonCopyClick (object sender, EventArgs e)
{
try
{
Clipboard.SetText(_hash);
_ = MessageBox.Show(
_ = ClipboardHelper.TrySetText(_hash)
? MessageBox.Show(
Resources.PluginHashDialog_UI_Message_CopySuccess,
Resources.PluginHashDialog_UI_Message_SuccessTitle,
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex) when (ex is ExternalException or
ThreadStateException or
ThreadStateException)
{
_ = MessageBox.Show(
string.Format(CultureInfo.InvariantCulture, Resources.PluginHashDialog_UI_Message_CopyError, ex.Message),
MessageBoxIcon.Information)
: MessageBox.Show(
string.Format(CultureInfo.InvariantCulture, Resources.PluginHashDialog_UI_Message_CopyError, Resources.LogExpert_Common_UI_Message_ClipboardInUse),
Resources.PluginHashDialog_UI_Message_ErrorTitle,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}

private void OnButtonCloseClick (object sender, EventArgs e)
Expand Down
Loading