Files
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

291 lines
7.0 KiB
Go

package server
import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"
)
// resolveCursorProjectDirFromSessionFile derives the real workspace
// directory for a Cursor session from the stored transcript path.
// The bool reports whether multiple matching paths exist on disk.
func resolveCursorProjectDirFromSessionFile(
filePath string,
) (string, bool) {
projectDir := cursorProjectDirNameFromTranscriptPath(filePath)
if projectDir == "" {
return "", false
}
return resolveCursorProjectDirName(projectDir)
}
// resolveCursorProjectDirFromSessionFileHint derives the real workspace
// directory for a Cursor session from the stored transcript path,
// preferring candidates that contain the provided hint.
func resolveCursorProjectDirFromSessionFileHint(
filePath, hint string,
) string {
projectDir := cursorProjectDirNameFromTranscriptPath(filePath)
if projectDir == "" {
return ""
}
return resolveCursorProjectDirNameHint(projectDir, hint)
}
// cursorProjectDirNameFromTranscriptPath extracts the encoded Cursor
// project directory name from either flat or nested transcript paths.
func cursorProjectDirNameFromTranscriptPath(path string) string {
path = filepath.Clean(path)
dir := filepath.Dir(path)
for {
base := filepath.Base(dir)
if base == "." || base == string(filepath.Separator) {
return ""
}
if base == "agent-transcripts" {
parent := filepath.Dir(dir)
name := filepath.Base(parent)
if name == "." || name == string(filepath.Separator) {
return ""
}
return name
}
next := filepath.Dir(dir)
if next == dir {
return ""
}
dir = next
}
}
// resolveCursorProjectDirName derives a real workspace path from a
// Cursor-encoded directory name. The bool reports whether more than
// one matching path exists on disk.
func resolveCursorProjectDirName(dirName string) (string, bool) {
matches := resolveCursorProjectDirNameMatches(dirName, "", 2)
switch len(matches) {
case 0:
return "", false
case 1:
return matches[0], false
default:
return matches[0], true
}
}
// resolveCursorProjectDirNameHint derives a real workspace path from a
// Cursor-encoded directory name, preferring candidates that contain the
// provided hint.
func resolveCursorProjectDirNameHint(dirName, hint string) string {
matches := resolveCursorProjectDirNameMatches(dirName, hint, 1)
if len(matches) == 0 {
return ""
}
nh := normalizeCursorDir(hint)
if nh != "" && !cursorPathContainsHint(
normalizeCursorDir(matches[0]), nh,
) {
return ""
}
return matches[0]
}
func resolveCursorProjectDirNameMatches(
dirName, hint string, limit int,
) []string {
dirName = strings.TrimSpace(dirName)
if dirName == "" {
return nil
}
hint = normalizeCursorDir(hint)
if runtime.GOOS == "windows" {
return resolveCursorProjectDirNameFromRootMatches(
"", dirName, hint, limit,
)
}
return resolveCursorProjectDirNameFromRootMatches(
string(filepath.Separator), dirName, hint, limit,
)
}
// resolveCursorProjectDirNameFromRoot reconstructs a real path from a
// Cursor-encoded project directory name by walking an existing
// filesystem tree and matching each component against the encoded token
// stream. The root parameter is mainly for tests; empty means use the
// OS default root.
func resolveCursorProjectDirNameFromRoot(
root, dirName string,
) string {
return resolveCursorProjectDirNameFromRootHint(root, dirName, "")
}
// resolveCursorProjectDirNameFromRootHint reconstructs a real path from
// a Cursor-encoded project directory name. It backtracks across matching
// path components instead of committing to the first greedy match, and
// prefers candidates that contain the latest transcript cwd when one is
// available.
func resolveCursorProjectDirNameFromRootHint(
root, dirName, hint string,
) string {
matches := resolveCursorProjectDirNameFromRootMatches(
root, dirName, hint, 1,
)
if len(matches) == 0 {
return ""
}
nh := normalizeCursorDir(hint)
if nh != "" && !cursorPathContainsHint(
normalizeCursorDir(matches[0]), nh,
) {
return ""
}
return matches[0]
}
func resolveCursorProjectDirNameFromRootMatches(
root, dirName, hint string, limit int,
) []string {
tokens := cursorEncodedTokens(dirName)
if len(tokens) == 0 {
return nil
}
current := root
if runtime.GOOS == "windows" {
if len(tokens[0]) == 1 {
drive := tokens[0][0]
if (drive >= 'A' && drive <= 'Z') ||
(drive >= 'a' && drive <= 'z') {
current = strings.ToUpper(tokens[0]) + ":" +
string(filepath.Separator)
tokens = tokens[1:]
}
}
}
if current == "" {
current = string(filepath.Separator)
}
if !isDir(current) {
return nil
}
if len(tokens) == 0 {
return []string{current}
}
var matches []string
collectCursorPathMatches(
current, tokens, hint, limit, &matches,
)
return matches
}
type cursorPathMatch struct {
name string
path string
consumed int
hinted bool
}
func collectCursorPathMatches(
dir string, tokens []string, hint string, limit int,
matches *[]string,
) {
if limit > 0 && len(*matches) >= limit {
return
}
if len(tokens) == 0 {
if isDir(dir) {
*matches = append(*matches, dir)
}
return
}
for _, match := range matchCursorPathComponents(dir, tokens, hint) {
collectCursorPathMatches(
match.path, tokens[match.consumed:], hint,
limit, matches,
)
if limit > 0 && len(*matches) >= limit {
return
}
}
}
func matchCursorPathComponents(
dir string, tokens []string, hint string,
) []cursorPathMatch {
entries, err := os.ReadDir(dir)
if err != nil {
return nil
}
matches := make([]cursorPathMatch, 0, len(entries))
for _, entry := range entries {
fullPath := filepath.Join(dir, entry.Name())
if !isDir(fullPath) {
continue
}
candidate := cursorComponentTokens(entry.Name())
if len(candidate) == 0 || len(candidate) > len(tokens) {
continue
}
if !cursorTokenPrefixMatch(tokens, candidate) {
continue
}
matches = append(matches, cursorPathMatch{
name: entry.Name(),
path: fullPath,
consumed: len(candidate),
hinted: cursorPathContainsHint(fullPath, hint),
})
}
sort.Slice(matches, func(i, j int) bool {
if matches[i].hinted != matches[j].hinted {
return matches[i].hinted
}
if matches[i].consumed != matches[j].consumed {
return matches[i].consumed > matches[j].consumed
}
return matches[i].name < matches[j].name
})
return matches
}
func cursorPathContainsHint(path, hint string) bool {
if path == "" || hint == "" {
return false
}
rel, err := filepath.Rel(path, hint)
if err != nil {
return false
}
return rel == "." ||
(rel != ".." &&
!strings.HasPrefix(rel, ".."+string(filepath.Separator)))
}
func cursorTokenPrefixMatch(tokens, candidate []string) bool {
for i := range candidate {
if tokens[i] != candidate[i] {
return false
}
}
return true
}
func cursorEncodedTokens(s string) []string {
return strings.FieldsFunc(s, func(r rune) bool {
return r == '-'
})
}
func cursorComponentTokens(s string) []string {
return strings.FieldsFunc(s, func(r rune) bool {
return r == '-' || r == '.' || r == '_'
})
}