Skip to content

v0.18.9#122

Merged
runkids merged 14 commits intomainfrom
v0.18.9
Apr 7, 2026
Merged

v0.18.9#122
runkids merged 14 commits intomainfrom
v0.18.9

Conversation

@runkids
Copy link
Copy Markdown
Owner

@runkids runkids commented Apr 7, 2026

[0.18.9] - 2026-04-07

New Features

  • Relative symlinks in project mode Relative Symlink Support #120skillshare sync -p now creates relative symlinks (e.g., ../../.skillshare/skills/my-skill) instead of absolute paths. This makes the project directory portable — rename it, move it, or clone it on another machine and all skill symlinks continue to work. Global mode continues to use absolute paths. Existing absolute symlinks are automatically upgraded to relative on the next sync

Bug Fixes

runkids added 10 commits April 7, 2026 14:40
…mlinks

Determines whether both source and target paths are under the project
root, in which case relative symlinks are portable and can be committed
to git.

Ref: #120
Unix: computes filepath.Rel from link dir to source, falls back to
absolute on error.
Windows: tries os.Symlink with relative path first (Developer Mode),
falls back to mklink /J with absolute paths.

All existing call sites pass false (no behavior change yet).

Ref: #120
SyncTargetMerge, SyncTargetMergeWithSkills, CreateSymlink, and SyncTarget
now accept projectRoot. When non-empty, createLink uses relative symlinks
for paths under the project root.

All existing callers pass empty string (global mode, no behavior change).

Ref: #120
Project-mode sync now passes the project root directory through to
createLink, enabling relative symlinks for all paths under the project.
Server handlers use s.projectRoot for project-mode dashboards.

Ref: #120
Replace direct os.Symlink calls in extras.go with createLink, gaining:
- Relative symlink support in project mode
- Windows junction fallback for extras (previously missing)

Thread projectRoot through SyncExtra, CollectExtraFiles, and all callers.

Ref: #120
- Use utils.PathHasPrefix/PathsEqual for Windows case-insensitivity
- Eliminate redundant filepath.Clean call
- Hoist shouldUseRelative in syncExtraPerFile (compute once, pass bool)
- Add invariant comment in SyncTargetMergeWithSkills
status command used a legacy local parser that only looked for a
top-level version: key in SKILL.md frontmatter, always returning empty
since version now lives under metadata.version.

Replace local readSkillVersion() and fetchRemoteSkillVersion() with
the shared versioncheck.ReadLocalSkillVersion() and
FetchRemoteSkillVersion() from internal/version, matching what doctor
and upgrade already use.

Remove now-unused skillshareSkillURL constant from init.go.

Closes #121
…mode

sync previously skipped symlinks that pointed to the correct source,
even if their format (absolute vs relative) didn't match the expected
mode. This meant existing absolute symlinks in project mode were never
converted to relative on subsequent syncs.

Add linkNeedsReformat(dest, wantRelative) helper that checks whether
the raw readlink result has the wrong format. All four 'already
correct' skip points now check format and recreate when mismatched:

- SyncTargetMergeWithSkills (skills merge mode)
- SyncTarget (skills symlink mode)
- syncExtraSymlinkMode (extras directory symlink)
- syncOneExtraFile (extras per-file merge)

Global mode is unaffected: shouldUseRelative returns false, so
linkNeedsReformat never triggers on absolute links.

Also fix macOS /var → /private/var path mismatch in
TestSyncProject_RelativeSymlinks by EvalSymlinks on expected path.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements relative symlinks for project-mode synchronization to improve directory portability and fixes a bug in the status command's version detection. The changes involve updating the sync stack to propagate a project root and modifying symlink creation logic for both Unix and Windows. Feedback identifies a path resolution bug in the extras sync logic and recommends more robust error handling by checking the results of file removals across several sync functions.

Comment thread internal/sync/extras.go Outdated
Comment thread internal/sync/sync.go Outdated
Comment thread internal/sync/sync.go Outdated
Comment thread internal/sync/extras.go Outdated
…move errors

filepath.Abs(dest) on a relative readlink result resolves against CWD,
not the symlink's parent directory. This broke the 'already correct'
check when comparing relative symlinks to absolute source paths.

Add resolveReadlink() helper that joins relative targets with
filepath.Dir(linkPath) before calling filepath.Abs.

Also check os.Remove errors in all reformat paths instead of ignoring
them silently.

Addresses PR #122 review feedback.
@runkids
Copy link
Copy Markdown
Owner Author

runkids commented Apr 7, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements relative symlinks for project mode to ensure directory portability and fixes a bug in the status command's version detection logic. The implementation involves a new utility for path relativity and the propagation of project root context across the sync, extras, and server packages. Feedback highlights the need for more robust path prefix handling at the filesystem root and consistent use of absolute paths when calculating relative symlink targets to avoid potential failures.

Comment thread internal/sync/relative.go Outdated
Comment thread internal/sync/symlink_unix.go Outdated
Comment thread internal/sync/symlink_windows.go Outdated
- shouldUseRelative: avoid double separator when projectRoot is /
- symlink_windows.go: use pre-resolved absTarget/absSource for Rel
- extras.go: check os.Remove error on wrong symlink replacement
@runkids
Copy link
Copy Markdown
Owner Author

runkids commented Apr 7, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces relative symlinks for project mode (skillshare sync -p), ensuring that project directories remain portable when moved or cloned. It also includes a bug fix for version detection in the status command by refactoring version parsing into a shared internal package. The changes involve updating function signatures across the CLI and server components to propagate the project root, as well as implementing logic to automatically upgrade existing absolute symlinks to relative ones during sync. Feedback was provided regarding a potential issue on Windows where the tool might enter a loop of removing and recreating directory junctions if the environment does not support relative symlinks.

Comment thread internal/sync/relative.go
runkids added 2 commits April 7, 2026 16:34
On Windows without Developer Mode, createLink falls back to an NTFS
junction (always absolute). linkNeedsReformat would then return true
on every sync, causing a remove→recreate loop.

Add build-tagged canCreateRelativeLink(): returns true on Unix, and
on Windows probes once with sync.Once by creating a test relative
symlink in a temp dir. linkNeedsReformat skips reformat when the
platform cannot create relative links.

Also use filepath.Clean instead of filepath.Abs in resolveReadlink
(input is already absolute, avoids unnecessary os.Getwd).

Addresses PR #122 review feedback.
Two correctness improvements from Codex review:

1. EvalSymlinks before relative-link decisions: shouldUseRelative and
   createLink now resolve symlinked ancestors via EvalSymlinks so that
   a project opened through a symlinked workspace, or a target dir that
   traverses a symlink, produces correct relative paths (or falls back
   to absolute when paths diverge from the project root).

2. Atomic reformat via reformatLink: instead of remove→create (which
   loses the link on createLink failure), reformat now creates a temp
   link and renames it over the original. On Unix this is atomic; on
   Windows it falls back to remove→create when rename fails.

Tests added for symlinked project root, divergent target symlink,
and reformatLink atomic swap.
@runkids
Copy link
Copy Markdown
Owner Author

runkids commented Apr 7, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces relative symlinks for project mode (skillshare sync -p), ensuring project portability by using paths relative to the project root. It also refactors the status command to use a shared version parser, fixing a detection bug. The review feedback highlights several opportunities to improve the robustness of the new symlink logic, specifically regarding error handling for os.Readlink and ensuring consistent absolute path resolution when calculating relative links.

Comment thread internal/sync/sync.go
Comment thread internal/sync/sync.go
Comment thread internal/sync/relative.go
Comment thread internal/sync/relative.go
@runkids runkids merged commit 9ff0069 into main Apr 7, 2026
8 checks passed
runkids added a commit that referenced this pull request Apr 7, 2026
…move errors

filepath.Abs(dest) on a relative readlink result resolves against CWD,
not the symlink's parent directory. This broke the 'already correct'
check when comparing relative symlinks to absolute source paths.

Add resolveReadlink() helper that joins relative targets with
filepath.Dir(linkPath) before calling filepath.Abs.

Also check os.Remove errors in all reformat paths instead of ignoring
them silently.

Addresses PR #122 review feedback.
runkids added a commit that referenced this pull request Apr 7, 2026
On Windows without Developer Mode, createLink falls back to an NTFS
junction (always absolute). linkNeedsReformat would then return true
on every sync, causing a remove→recreate loop.

Add build-tagged canCreateRelativeLink(): returns true on Unix, and
on Windows probes once with sync.Once by creating a test relative
symlink in a temp dir. linkNeedsReformat skips reformat when the
platform cannot create relative links.

Also use filepath.Clean instead of filepath.Abs in resolveReadlink
(input is already absolute, avoids unnecessary os.Getwd).

Addresses PR #122 review feedback.
@runkids runkids deleted the v0.18.9 branch April 7, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

status: Skill version check still uses legacy parser, always shows 'not found or missing version' Relative Symlink Support

1 participant