Using a Reference File strategy helps build a better LAN scanner with Cursor Composer

CoffeeAndCode Advanced 5/14/2026 228 views 13 likes 2 min read

Cursor's "Composer" mode (Cmd+I) revolutionizes writing boilerplate-heavy networking code. However, simply prompting it to "write a LAN scanner" usually results in a generic, slow loop lacking proper error handling. While building a local-first network discovery tool in Go this morning, I found that the real productivity breakthrough came from using a "Reference File" strategy to enforce specific concurrency patterns.

Using a Reference File strategy helps build a better LAN scanner with Cursor Composer

Managing timeouts and goroutines without leaking resources is the primary challenge in Go networking. If left to its own devices, the AI often neglects to close connections or relies on time.Sleep instead of implementing proper context cancellation. To solve this, I added a patterns.md file to my project root and included it in Cursor's context. I explicitly defined in that file that all network I/O must utilize context.WithTimeout. When prompting the AI to build the ARP/ICMP scanning logic, I merely referenced that specific file.

The following concurrent scanner logic is what I ultimately implemented. I utilized a worker pool pattern to prevent hitting OS file descriptor limits during a /24 subnet scan:

package main

import (
	"context"
	"fmt"
	"net"
	"sync"
	"time"
)

func scanHost(ctx context.Context, ip string, results chan<- string, wg *sync.WaitGroup) {
	defer wg.Done()
	
	d := net.Dialer{Timeout: 500 * time.Millisecond}
	conn, err := d.DialContext(ctx, "tcp", ip+":80") // Scanning port 80 as a proxy for "alive"
	if err == nil {
		conn.Close()
		results <- ip
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	ips := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"} // Simplified for example
	results := make(chan string, len(ips))
	var wg sync.WaitGroup

	for _, ip := range ips {
		wg.Add(1)
		go scanHost(ctx, ip, results, &wg)
	}

	go func() {
		wg.Wait()
		close(results)
	}()

	for res := range results {
		fmt.Printf("Host found: %s\n", res)
	}
}

Leverage .cursorrules for Go: I implemented a project rule instructing the AI to "Prefer slog over fmt for logging and always implement context propagation in function signatures." This effectively prevented the AI from suggesting outdated log.Printf calls.

Achieving the correct worker pool required multiple steps. I started with a basic loop, then highlighted the slow segments and used "Chat" (Cmd+L) to ask: "This is blocking. Rewrite this using a worker pool of 50 goroutines to speed up the /24 scan." The AI correctly identified the synchronous Dial call as the bottleneck.

Switching from a sequential scan to a controlled goroutine pool reduced my local subnet scan time from ~40 seconds to under 3 seconds.

One frustration with Claude 3.5 Sonnet in Cursor is its tendency to suggest third-party libraries like github.com/mdlayher/arp without prompting a go get. I have found that adding "Stick to the standard library unless performance requires otherwise" to my prompts keeps the dependency tree clean.

All Replies (0)

Want a live back-and-forth? Join the global AI chat room — login to talk.

No replies yet — be the first!

Write a Reply

Markdown supported