skillrepo 2.0.0 → 3.1.0
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.
- package/README.md +276 -145
- package/bin/skillrepo.mjs +224 -36
- package/package.json +6 -3
- package/src/commands/add.mjs +176 -0
- package/src/commands/get.mjs +116 -0
- package/src/commands/init.mjs +589 -143
- package/src/commands/list.mjs +176 -0
- package/src/commands/remove.mjs +162 -0
- package/src/commands/search.mjs +188 -0
- package/src/commands/session-sync.mjs +152 -0
- package/src/commands/uninstall.mjs +484 -0
- package/src/commands/update.mjs +184 -0
- package/src/lib/artifact-registry.mjs +265 -0
- package/src/lib/cli-config.mjs +230 -0
- package/src/lib/config.mjs +238 -0
- package/src/lib/detect-ides.mjs +0 -19
- package/src/lib/errors.mjs +264 -0
- package/src/lib/file-write.mjs +705 -0
- package/src/lib/fs-utils.mjs +83 -1
- package/src/lib/http.mjs +817 -37
- package/src/lib/identifier.mjs +153 -0
- package/src/lib/mcp-merge.mjs +275 -0
- package/src/lib/mergers/gitignore.mjs +73 -18
- package/src/lib/mergers/session-hook.mjs +298 -0
- package/src/lib/paths.mjs +67 -17
- package/src/lib/prompt.mjs +11 -44
- package/src/lib/removers/claude-mcp.mjs +67 -0
- package/src/lib/removers/cursor-mcp.mjs +60 -0
- package/src/lib/removers/env-local.mjs +55 -0
- package/src/lib/removers/gitignore.mjs +108 -0
- package/src/lib/removers/settings.mjs +183 -0
- package/src/lib/removers/vscode-mcp.mjs +87 -0
- package/src/lib/removers/windsurf-mcp.mjs +65 -0
- package/src/lib/sync.mjs +305 -0
- package/src/test/commands/add.test.mjs +285 -0
- package/src/test/commands/get.test.mjs +176 -0
- package/src/test/commands/init.test.mjs +697 -0
- package/src/test/commands/list.test.mjs +172 -0
- package/src/test/commands/remove.test.mjs +234 -0
- package/src/test/commands/search.test.mjs +204 -0
- package/src/test/commands/session-sync.test.mjs +350 -0
- package/src/test/commands/uninstall.test.mjs +768 -0
- package/src/test/commands/update.test.mjs +322 -0
- package/src/test/detect-ides.test.mjs +9 -14
- package/src/test/dispatcher.test.mjs +224 -0
- package/src/test/e2e/cli-commands.test.mjs +576 -0
- package/src/test/e2e/mock-server.mjs +364 -22
- package/src/test/helpers/capture-stream.mjs +48 -0
- package/src/test/integration/file-write.integration.test.mjs +279 -0
- package/src/test/lib/artifact-registry.test.mjs +268 -0
- package/src/test/lib/cli-config.test.mjs +407 -0
- package/src/test/lib/config.test.mjs +257 -0
- package/src/test/lib/errors.test.mjs +359 -0
- package/src/test/lib/file-write.test.mjs +784 -0
- package/src/test/lib/http.test.mjs +1198 -0
- package/src/test/lib/identifier.test.mjs +157 -0
- package/src/test/lib/mcp-merge.test.mjs +345 -0
- package/src/test/lib/paths.test.mjs +83 -0
- package/src/test/lib/sync.test.mjs +514 -0
- package/src/test/mergers/gitignore.test.mjs +145 -20
- package/src/test/mergers/session-hook.test.mjs +745 -0
- package/src/test/mergers/uninstall-claude-mcp.test.mjs +145 -0
- package/src/test/mergers/uninstall-cursor-mcp.test.mjs +108 -0
- package/src/test/mergers/uninstall-env-local.test.mjs +144 -0
- package/src/test/mergers/uninstall-gitignore.test.mjs +209 -0
- package/src/test/mergers/uninstall-settings.test.mjs +285 -0
- package/src/test/mergers/uninstall-vscode-mcp.test.mjs +215 -0
- package/src/test/mergers/uninstall-windsurf-mcp.test.mjs +122 -0
- package/src/lib/write-configs.mjs +0 -202
- package/src/test/e2e/HANDOFF.md +0 -223
- package/src/test/e2e/cli-init.test.mjs +0 -213
- package/src/test/e2e/payload-factory.mjs +0 -22
package/src/lib/fs-utils.mjs
CHANGED
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
* Creates directories as needed, handles errors cleanly.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
readFileSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
statSync,
|
|
12
|
+
chmodSync,
|
|
13
|
+
renameSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
} from "node:fs";
|
|
7
16
|
import { dirname } from "node:path";
|
|
8
17
|
|
|
9
18
|
/**
|
|
@@ -50,3 +59,76 @@ export function writeExecutable(filePath, content) {
|
|
|
50
59
|
export function pathExists(p) {
|
|
51
60
|
return existsSync(p);
|
|
52
61
|
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Atomic write via temp-file + rename. Matches the pattern in
|
|
65
|
+
* `config.mjs`: write to `<path>.tmp`, then `renameSync` into place
|
|
66
|
+
* so the destination is never observed in a half-written state, and
|
|
67
|
+
* unlink the temp file on rename failure so a partial credential
|
|
68
|
+
* value is never left behind on disk.
|
|
69
|
+
*
|
|
70
|
+
* Use this (not `writeFileSafe`) for any module that modifies a user
|
|
71
|
+
* config file containing credentials or shared state. The uninstall
|
|
72
|
+
* removers (#885) touch `.env.local`, `.mcp.json`, and
|
|
73
|
+
* `settings.local.json` — all three benefit from atomic writes, the
|
|
74
|
+
* first two because of the credential-leak risk, the third because
|
|
75
|
+
* Claude Code parses it on startup and a half-written JSON would
|
|
76
|
+
* break the user's session.
|
|
77
|
+
*
|
|
78
|
+
* Parent-directory semantics match `writeFileSafe`: created
|
|
79
|
+
* recursively if missing. Directory-as-file collision is rejected
|
|
80
|
+
* before the temp-file is written.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} filePath - Absolute path to the final destination.
|
|
83
|
+
* @param {string} content - UTF-8 content to persist.
|
|
84
|
+
* @param {object} [options]
|
|
85
|
+
* @param {number} [options.mode] - chmod applied to the temp file
|
|
86
|
+
* BEFORE rename so the destination never exists with looser
|
|
87
|
+
* permissions than intended. Skipped on Windows.
|
|
88
|
+
*/
|
|
89
|
+
export function writeFileAtomic(filePath, content, { mode } = {}) {
|
|
90
|
+
const dir = dirname(filePath);
|
|
91
|
+
if (!existsSync(dir)) {
|
|
92
|
+
mkdirSync(dir, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
if (existsSync(filePath) && statSync(filePath).isDirectory()) {
|
|
95
|
+
throw new Error(`${filePath} is a directory, expected a file`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const tmpPath = `${filePath}.tmp`;
|
|
99
|
+
try {
|
|
100
|
+
writeFileSync(tmpPath, content, "utf-8");
|
|
101
|
+
} catch (err) {
|
|
102
|
+
// Re-throw with a clearer message but preserve the cause for
|
|
103
|
+
// --verbose. A temp-file write failure is almost always a
|
|
104
|
+
// permissions or disk-full issue; the original error surfaces it.
|
|
105
|
+
throw new Error(`Cannot write ${tmpPath}: ${err.message}`, { cause: err });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (mode !== undefined && process.platform !== "win32") {
|
|
109
|
+
try {
|
|
110
|
+
chmodSync(tmpPath, mode);
|
|
111
|
+
} catch {
|
|
112
|
+
// Non-fatal — same rationale as config.mjs: chmod failure
|
|
113
|
+
// doesn't corrupt the file, the destination just has looser
|
|
114
|
+
// permissions than intended. Callers that care can stat the
|
|
115
|
+
// file after the write.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
renameSync(tmpPath, filePath);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
// Clean up the stale temp file so a partial credential value
|
|
123
|
+
// isn't left behind. Best-effort — if the unlink also fails,
|
|
124
|
+
// the original rename error is still what we surface.
|
|
125
|
+
try {
|
|
126
|
+
unlinkSync(tmpPath);
|
|
127
|
+
} catch {
|
|
128
|
+
/* best-effort */
|
|
129
|
+
}
|
|
130
|
+
throw new Error(`Cannot install ${filePath}: ${err.message}`, {
|
|
131
|
+
cause: err,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|