docks-kit 0.1.2 → 0.1.4
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.
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and the snapshot write.
|
|
6
6
|
*/
|
|
7
7
|
import { spawnSync } from "node:child_process"
|
|
8
|
-
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs"
|
|
8
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, rmdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"
|
|
9
9
|
import { tmpdir } from "node:os"
|
|
10
10
|
import { capture, commandExists, isExecutable, p, which } from "./exec"
|
|
11
11
|
import type { Ctx } from "./index"
|
|
@@ -136,11 +136,17 @@ function healClaudeSymlink(ctx: Ctx, skillsDir: string, base: string): boolean {
|
|
|
136
136
|
if (linkStat?.isSymbolicLink() === true) {
|
|
137
137
|
const current = safeReadlink(claudeLink)
|
|
138
138
|
if (current === relTarget) return false
|
|
139
|
+
// win32: `npx skills add` creates absolute symlinks/junctions — any link
|
|
140
|
+
// that RESOLVES to the canonical dir is healthy, not stale.
|
|
141
|
+
if (process.platform === "win32" && realpathEquals(claudeLink, canonical)) return false
|
|
139
142
|
if (ctx.dryRun) {
|
|
140
143
|
echo(`[dry-run] would replace stale Claude symlink: ~/.claude/skills/${base} -> ${current} (correct: ${relTarget})`)
|
|
141
144
|
return true
|
|
142
145
|
}
|
|
143
|
-
|
|
146
|
+
if (!removeLink(claudeLink)) {
|
|
147
|
+
warn(`could not remove stale link ~/.claude/skills/${base} — remove it manually, then re-run sync`)
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
144
150
|
} else if (linkStat !== undefined) {
|
|
145
151
|
warn(`~/.claude/skills/${base} exists as a real path (not a symlink) — leaving alone; remove manually if it's stale`)
|
|
146
152
|
return false
|
|
@@ -169,9 +175,34 @@ function safeReadlink(path: string): string {
|
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
177
|
|
|
178
|
+
function realpathEquals(a: string, b: string): boolean {
|
|
179
|
+
try {
|
|
180
|
+
return realpathSync(a) === realpathSync(b)
|
|
181
|
+
} catch {
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Remove a symlink/junction without crashing: Bun's rmSync throws EFAULT on
|
|
188
|
+
* win32 directory symlinks. unlink handles file symlinks, rmdir handles
|
|
189
|
+
* dir symlinks/junctions, rmSync is the POSIX catch-all.
|
|
190
|
+
*/
|
|
191
|
+
function removeLink(path: string): boolean {
|
|
192
|
+
for (const rm of [() => unlinkSync(path), () => rmdirSync(path), () => rmSync(path, { force: true })]) {
|
|
193
|
+
try {
|
|
194
|
+
rm()
|
|
195
|
+
return true
|
|
196
|
+
} catch {
|
|
197
|
+
// try the next removal shape
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return lstat(path) === undefined
|
|
201
|
+
}
|
|
202
|
+
|
|
172
203
|
/** skills::_link_or_copy — real symlink preferred, copy fallback (Windows). */
|
|
173
204
|
export function linkOrCopy(target: string, link: string): boolean {
|
|
174
|
-
|
|
205
|
+
removeLink(link)
|
|
175
206
|
try {
|
|
176
207
|
symlinkSync(target, link, process.platform === "win32" ? "dir" : undefined)
|
|
177
208
|
} catch {
|
|
@@ -288,6 +319,15 @@ export function effectSolutionsInstall(ctx: Ctx): (mode: "install" | "upgrade",
|
|
|
288
319
|
}
|
|
289
320
|
|
|
290
321
|
const gbin = capture(bun, ["pm", "-g", "bin"])
|
|
322
|
+
// win32: bun writes an .exe shim (not the bare Unix name), and the
|
|
323
|
+
// ~/.local/bin link step below is Unix-only plumbing (non-interactive
|
|
324
|
+
// agent PATH) — bun's global bin is already the Windows PATH entry.
|
|
325
|
+
if (process.platform === "win32") {
|
|
326
|
+
const found = gbin !== "" && ["exe", "cmd", "bunx"].some((ext) => existsSync(p(gbin, `effect-solutions.${ext}`)))
|
|
327
|
+
if (found) log(`effect-solutions CLI ready (${gbin})`)
|
|
328
|
+
else warn(`effect-solutions installed but no shim found under '${gbin !== "" ? gbin : "<unknown>"}' — check bun pm -g bin`)
|
|
329
|
+
return 0
|
|
330
|
+
}
|
|
291
331
|
if (gbin !== "" && isExecutable(p(gbin, "effect-solutions"))) {
|
|
292
332
|
mkdirSync(p(ctx.home, ".local", "bin"), { recursive: true })
|
|
293
333
|
linkOrCopy(bun, p(ctx.home, ".local", "bin", "bun"))
|
package/package.json
CHANGED