@claudeskill/cli 0.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.
Files changed (65) hide show
  1. package/dist/api.d.ts +148 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +218 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/commands/checkout.d.ts +8 -0
  6. package/dist/commands/checkout.d.ts.map +1 -0
  7. package/dist/commands/checkout.js +134 -0
  8. package/dist/commands/checkout.js.map +1 -0
  9. package/dist/commands/diff.d.ts +8 -0
  10. package/dist/commands/diff.d.ts.map +1 -0
  11. package/dist/commands/diff.js +156 -0
  12. package/dist/commands/diff.js.map +1 -0
  13. package/dist/commands/list.d.ts +14 -0
  14. package/dist/commands/list.d.ts.map +1 -0
  15. package/dist/commands/list.js +251 -0
  16. package/dist/commands/list.js.map +1 -0
  17. package/dist/commands/log.d.ts +8 -0
  18. package/dist/commands/log.d.ts.map +1 -0
  19. package/dist/commands/log.js +74 -0
  20. package/dist/commands/log.js.map +1 -0
  21. package/dist/commands/login.d.ts +8 -0
  22. package/dist/commands/login.d.ts.map +1 -0
  23. package/dist/commands/login.js +201 -0
  24. package/dist/commands/login.js.map +1 -0
  25. package/dist/commands/logout.d.ts +8 -0
  26. package/dist/commands/logout.d.ts.map +1 -0
  27. package/dist/commands/logout.js +33 -0
  28. package/dist/commands/logout.js.map +1 -0
  29. package/dist/commands/pull.d.ts +11 -0
  30. package/dist/commands/pull.d.ts.map +1 -0
  31. package/dist/commands/pull.js +123 -0
  32. package/dist/commands/pull.js.map +1 -0
  33. package/dist/commands/push.d.ts +12 -0
  34. package/dist/commands/push.d.ts.map +1 -0
  35. package/dist/commands/push.js +128 -0
  36. package/dist/commands/push.js.map +1 -0
  37. package/dist/commands/status.d.ts +8 -0
  38. package/dist/commands/status.d.ts.map +1 -0
  39. package/dist/commands/status.js +104 -0
  40. package/dist/commands/status.js.map +1 -0
  41. package/dist/config.d.ts +25 -0
  42. package/dist/config.d.ts.map +1 -0
  43. package/dist/config.js +54 -0
  44. package/dist/config.js.map +1 -0
  45. package/dist/credentials.d.ts +24 -0
  46. package/dist/credentials.d.ts.map +1 -0
  47. package/dist/credentials.js +54 -0
  48. package/dist/credentials.js.map +1 -0
  49. package/dist/index.d.ts +8 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +166 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/menu.d.ts +8 -0
  54. package/dist/menu.d.ts.map +1 -0
  55. package/dist/menu.js +130 -0
  56. package/dist/menu.js.map +1 -0
  57. package/dist/onboarding.d.ts +8 -0
  58. package/dist/onboarding.d.ts.map +1 -0
  59. package/dist/onboarding.js +252 -0
  60. package/dist/onboarding.js.map +1 -0
  61. package/dist/sync.d.ts +83 -0
  62. package/dist/sync.d.ts.map +1 -0
  63. package/dist/sync.js +294 -0
  64. package/dist/sync.js.map +1 -0
  65. package/package.json +47 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Interactive onboarding flow for new users
3
+ */
4
+ import * as p from "@clack/prompts";
5
+ import { generateSalt, generateMasterKey, generateRecoveryKey, deriveKeyFromPassphrase, encryptMasterKey, formatRecoveryKey, toBase64, listAllSkills, } from "@claudeskill/core";
6
+ import { saveConfig, getDefaultConfig, getConfigDir } from "./config.js";
7
+ import { saveCredentials } from "./credentials.js";
8
+ import * as api from "./api.js";
9
+ import { pushSkills } from "./sync.js";
10
+ /**
11
+ * Run the interactive onboarding flow
12
+ */
13
+ export const runOnboarding = async () => {
14
+ p.intro("Claude Skill Sync");
15
+ // Step 1: Choose sync mode
16
+ const mode = await p.select({
17
+ message: "How would you like to sync your skills?",
18
+ options: [
19
+ {
20
+ value: "cloud",
21
+ label: "Cloud",
22
+ hint: "claudeskill.io - free, encrypted, zero-knowledge",
23
+ },
24
+ {
25
+ value: "selfhosted",
26
+ label: "Self-hosted",
27
+ hint: "bring your own server",
28
+ },
29
+ {
30
+ value: "local",
31
+ label: "Local only",
32
+ hint: "no sync, just organize",
33
+ },
34
+ ],
35
+ });
36
+ if (p.isCancel(mode)) {
37
+ p.cancel("Setup cancelled.");
38
+ process.exit(0);
39
+ }
40
+ const config = getDefaultConfig();
41
+ config.mode = mode;
42
+ // Step 2: Self-hosted server URL
43
+ if (mode === "selfhosted") {
44
+ const serverUrl = await p.text({
45
+ message: "Enter your server URL:",
46
+ placeholder: "https://skills.example.com",
47
+ validate: (value) => {
48
+ if (!value)
49
+ return "Server URL is required";
50
+ try {
51
+ new URL(value);
52
+ }
53
+ catch {
54
+ return "Invalid URL";
55
+ }
56
+ },
57
+ });
58
+ if (p.isCancel(serverUrl)) {
59
+ p.cancel("Setup cancelled.");
60
+ process.exit(0);
61
+ }
62
+ config.serverUrl = serverUrl;
63
+ }
64
+ // Save config early so API client can use it
65
+ await saveConfig(config);
66
+ // For cloud/selfhosted: authentication
67
+ if (mode === "cloud" || mode === "selfhosted") {
68
+ // Check server connectivity
69
+ const spinner = p.spinner();
70
+ spinner.start("Connecting to server...");
71
+ const healthResult = await api.checkHealth();
72
+ if (!healthResult.ok) {
73
+ spinner.stop("Connection failed");
74
+ p.log.error(`Cannot connect to server: ${healthResult.error}`);
75
+ p.log.info("Make sure the server is running and accessible.");
76
+ process.exit(1);
77
+ }
78
+ spinner.stop(`Connected to ${healthResult.data.name} v${healthResult.data.version}`);
79
+ // Step 3: Email
80
+ const email = await p.text({
81
+ message: "Email for your account:",
82
+ placeholder: "you@example.com",
83
+ validate: (value) => {
84
+ if (!value)
85
+ return "Email is required";
86
+ if (!value.includes("@"))
87
+ return "Invalid email";
88
+ },
89
+ });
90
+ if (p.isCancel(email)) {
91
+ p.cancel("Setup cancelled.");
92
+ process.exit(0);
93
+ }
94
+ config.email = email;
95
+ await saveConfig(config);
96
+ // Step 4: Request OTP
97
+ spinner.start("Sending verification code...");
98
+ const otpResult = await api.requestOtp(email);
99
+ if (!otpResult.ok) {
100
+ spinner.stop("Failed to send code");
101
+ p.log.error(otpResult.error);
102
+ process.exit(1);
103
+ }
104
+ spinner.stop("Verification code sent!");
105
+ // Step 5: Verify OTP
106
+ const code = await p.text({
107
+ message: "Enter the 6-digit code from your email:",
108
+ validate: (value) => {
109
+ if (!value)
110
+ return "Code is required";
111
+ if (!/^\d{6}$/.test(value))
112
+ return "Code must be 6 digits";
113
+ },
114
+ });
115
+ if (p.isCancel(code)) {
116
+ p.cancel("Setup cancelled.");
117
+ process.exit(0);
118
+ }
119
+ spinner.start("Verifying...");
120
+ const verifyResult = await api.verifyOtp(email, code);
121
+ if (!verifyResult.ok) {
122
+ spinner.stop("Verification failed");
123
+ p.log.error(verifyResult.error);
124
+ process.exit(1);
125
+ }
126
+ spinner.stop("Verified!");
127
+ const { accessToken, refreshToken, user } = verifyResult.data;
128
+ // Step 6: Create passphrase
129
+ const passphrase = await p.password({
130
+ message: "Create a vault passphrase:",
131
+ mask: "*",
132
+ validate: (value) => {
133
+ if (!value)
134
+ return "Passphrase is required";
135
+ if (value.length < 8)
136
+ return "Passphrase must be at least 8 characters";
137
+ },
138
+ });
139
+ if (p.isCancel(passphrase)) {
140
+ p.cancel("Setup cancelled.");
141
+ process.exit(0);
142
+ }
143
+ const confirmPassphrase = await p.password({
144
+ message: "Confirm passphrase:",
145
+ mask: "*",
146
+ validate: (value) => {
147
+ if (value !== passphrase)
148
+ return "Passphrases do not match";
149
+ },
150
+ });
151
+ if (p.isCancel(confirmPassphrase)) {
152
+ p.cancel("Setup cancelled.");
153
+ process.exit(0);
154
+ }
155
+ // Step 7: Generate encryption keys
156
+ spinner.start("Generating encryption keys...");
157
+ const salt = generateSalt();
158
+ const masterKey = generateMasterKey();
159
+ const recoveryKey = generateRecoveryKey();
160
+ const derivedKey = deriveKeyFromPassphrase(passphrase, salt);
161
+ const encryptedMaster = encryptMasterKey(masterKey, derivedKey.key);
162
+ // Combine IV + tag + ciphertext for storage
163
+ const encryptedMasterKeyFull = new Uint8Array(encryptedMaster.iv.length +
164
+ encryptedMaster.tag.length +
165
+ encryptedMaster.encrypted.length);
166
+ encryptedMasterKeyFull.set(encryptedMaster.iv, 0);
167
+ encryptedMasterKeyFull.set(encryptedMaster.tag, encryptedMaster.iv.length);
168
+ encryptedMasterKeyFull.set(encryptedMaster.encrypted, encryptedMaster.iv.length + encryptedMaster.tag.length);
169
+ // Save credentials first so API calls have the access token
170
+ await saveCredentials({
171
+ accessToken,
172
+ refreshToken,
173
+ encryptedMasterKey: toBase64(encryptedMasterKeyFull),
174
+ salt: toBase64(salt),
175
+ });
176
+ // Upload salt to server
177
+ const saltResult = await api.setSalt(toBase64(salt));
178
+ if (!saltResult.ok) {
179
+ spinner.stop("Failed to save encryption settings");
180
+ p.log.error(saltResult.error);
181
+ process.exit(1);
182
+ }
183
+ spinner.stop("Encryption keys generated");
184
+ // Step 8: Show recovery key
185
+ const formattedRecoveryKey = formatRecoveryKey(recoveryKey);
186
+ p.note(`
187
+ ${formattedRecoveryKey}
188
+
189
+ This is the ONLY way to recover your skills if you
190
+ forget your passphrase. Store it somewhere safe
191
+ (password manager, printed paper, etc).
192
+
193
+ We cannot recover your data.
194
+ `.trim(), "SAVE YOUR RECOVERY KEY");
195
+ const savedRecoveryKey = await p.confirm({
196
+ message: "I have saved my recovery key",
197
+ });
198
+ if (p.isCancel(savedRecoveryKey) || !savedRecoveryKey) {
199
+ const showAgain = await p.confirm({
200
+ message: "Show recovery key again?",
201
+ });
202
+ if (showAgain) {
203
+ p.note(formattedRecoveryKey, "RECOVERY KEY");
204
+ }
205
+ const reallySaved = await p.confirm({
206
+ message: "I have saved my recovery key",
207
+ });
208
+ if (p.isCancel(reallySaved) || !reallySaved) {
209
+ p.cancel("You must save your recovery key to continue.");
210
+ process.exit(0);
211
+ }
212
+ }
213
+ p.log.success("Encryption configured");
214
+ // Step 10: Check for existing skills and offer to sync
215
+ const skills = await listAllSkills();
216
+ if (skills.length > 0) {
217
+ const syncNow = await p.confirm({
218
+ message: `Found ${skills.length} item${skills.length === 1 ? "" : "s"} (skills, commands, agents). Push them now?`,
219
+ });
220
+ if (!p.isCancel(syncNow) && syncNow) {
221
+ spinner.start("Pushing skills...");
222
+ const { pushed, errors } = await pushSkills(masterKey, (msg) => {
223
+ spinner.message(msg);
224
+ }, undefined);
225
+ spinner.stop("Push complete");
226
+ if (pushed > 0) {
227
+ p.log.success(`Pushed ${pushed} skill${pushed === 1 ? "" : "s"}`);
228
+ }
229
+ if (errors.length > 0) {
230
+ p.log.warning("Some skills failed:");
231
+ errors.forEach((error) => {
232
+ p.log.error(` ${error}`);
233
+ });
234
+ }
235
+ }
236
+ }
237
+ }
238
+ else {
239
+ // Local mode - just save config
240
+ await saveConfig(config);
241
+ }
242
+ // Done!
243
+ p.note(`
244
+ claude-skill-sync list List all skills
245
+ claude-skill-sync pull Pull from cloud
246
+ claude-skill-sync push Push to cloud
247
+ claude-skill-sync status Check sync status
248
+ `.trim(), "Quick commands");
249
+ p.log.info(`Config saved to ${getConfigDir()}`);
250
+ p.outro("You're all set!");
251
+ };
252
+ //# sourceMappingURL=onboarding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"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"}
package/dist/sync.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Sync logic for skills
3
+ *
4
+ * Handles encrypting, uploading, downloading, and decrypting skills.
5
+ */
6
+ import { type Skill, type SkillType } from "@claudeskill/core";
7
+ /** Local index file tracking synced skills */
8
+ type SyncIndex = {
9
+ /** Map of skill key to sync info */
10
+ skills: Record<string, {
11
+ /** Content hash of the skill (version identifier) */
12
+ hash: string;
13
+ /** Legacy blob ID (for migration) */
14
+ blobId: string | null;
15
+ /** Local content hash for change detection */
16
+ localHash: string;
17
+ /** When the remote was last updated */
18
+ remoteUpdatedAt: string;
19
+ }>;
20
+ lastSyncAt: string;
21
+ };
22
+ /**
23
+ * Load sync index
24
+ */
25
+ export declare const loadSyncIndex: () => Promise<SyncIndex>;
26
+ /**
27
+ * Save sync index
28
+ */
29
+ export declare const saveSyncIndex: (index: SyncIndex) => Promise<void>;
30
+ /**
31
+ * Get the master key from credentials
32
+ */
33
+ export declare const getMasterKey: (passphrase: string) => Promise<Uint8Array<ArrayBufferLike> | null>;
34
+ /**
35
+ * Encrypt a skill for upload
36
+ */
37
+ export declare const encryptSkill: (skill: Skill, masterKey: Uint8Array) => {
38
+ encryptedData: string;
39
+ iv: string;
40
+ tag: string;
41
+ };
42
+ /** Decrypted skill payload */
43
+ type DecryptedSkillPayload = {
44
+ name: string;
45
+ content: string;
46
+ path: string;
47
+ modifiedAt: string;
48
+ type: SkillType | undefined;
49
+ files: {
50
+ name: string;
51
+ content: string;
52
+ }[] | undefined;
53
+ };
54
+ /**
55
+ * Decrypt a skill from download
56
+ */
57
+ export declare const decryptSkill: (encryptedData: string, iv: string, tag: string, masterKey: Uint8Array) => DecryptedSkillPayload;
58
+ /**
59
+ * Push local skills to server
60
+ */
61
+ export declare const pushSkills: (masterKey: Uint8Array, onProgress: ((message: string) => void) | undefined, message: string | undefined) => Promise<{
62
+ pushed: number;
63
+ errors: string[];
64
+ }>;
65
+ /**
66
+ * Pull remote skills to local
67
+ */
68
+ export declare const pullSkills: (masterKey: Uint8Array, onProgress: ((message: string) => void) | undefined) => Promise<{
69
+ pulled: number;
70
+ errors: string[];
71
+ }>;
72
+ /**
73
+ * Get sync status for all skills
74
+ */
75
+ export declare const getSyncStatus: () => Promise<{
76
+ local: number;
77
+ synced: number;
78
+ pendingPush: number;
79
+ pendingPull: number;
80
+ lastSyncAt: string | null;
81
+ }>;
82
+ export {};
83
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAUL,KAAK,KAAK,EACV,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAK3B,8CAA8C;AAC9C,KAAK,SAAS,GAAG;IACf,oCAAoC;IACpC,MAAM,EAAE,MAAM,CACZ,MAAM,EACN;QACE,qDAAqD;QACrD,IAAI,EAAE,MAAM,CAAC;QACb,qCAAqC;QACrC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,8CAA8C;QAC9C,SAAS,EAAE,MAAM,CAAC;QAClB,uCAAuC;QACvC,eAAe,EAAE,MAAM,CAAC;KACzB,CACF,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAYF;;GAEG;AACH,eAAO,MAAM,aAAa,0BAOzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAU,OAAO,SAAS,kBAInD,CAAC;AAcF;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,YAAY,MAAM,gDAwBnB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,OAAO,KAAK,EACZ,WAAW,UAAU,KACpB;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAalD,CAAC;AAEF,8BAA8B;AAC9B,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,SAAS,CAAC;CACxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,eAAe,MAAM,EACrB,IAAI,MAAM,EACV,KAAK,MAAM,EACX,WAAW,UAAU,KACpB,qBAGF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,WAAW,UAAU,EACrB,YAAY,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,EACnD,SAAS,MAAM,GAAG,SAAS;;;EA4E5B,CAAC;AAaF;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,WAAW,UAAU,EACrB,YAAY,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS;;;EA0HpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;EA8BzB,CAAC"}
package/dist/sync.js ADDED
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Sync logic for skills
3
+ *
4
+ * Handles encrypting, uploading, downloading, and decrypting skills.
5
+ */
6
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
7
+ import { join, dirname } from "node:path";
8
+ import { listAllSkills, encryptString, decryptString, deriveKeyFromPassphrase, decryptMasterKey, fromBase64, getClaudeDir, computeSkillHash, getSkillKey, } from "@claudeskill/core";
9
+ import { loadCredentials } from "./credentials.js";
10
+ import * as api from "./api.js";
11
+ const SYNC_INDEX_FILE = "sync-index.json";
12
+ /**
13
+ * Get path to sync index file
14
+ */
15
+ const getSyncIndexPath = async () => {
16
+ const { getConfigDir } = await import("./config.js");
17
+ return join(getConfigDir(), SYNC_INDEX_FILE);
18
+ };
19
+ /**
20
+ * Load sync index
21
+ */
22
+ export const loadSyncIndex = async () => {
23
+ try {
24
+ const content = await readFile(await getSyncIndexPath(), "utf-8");
25
+ return JSON.parse(content);
26
+ }
27
+ catch {
28
+ return { skills: {}, lastSyncAt: "" };
29
+ }
30
+ };
31
+ /**
32
+ * Save sync index
33
+ */
34
+ export const saveSyncIndex = async (index) => {
35
+ const indexPath = await getSyncIndexPath();
36
+ await mkdir(dirname(indexPath), { recursive: true });
37
+ await writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
38
+ };
39
+ /**
40
+ * Simple hash for change detection
41
+ */
42
+ const hashContent = (content) => {
43
+ const hash = Array.from(content).reduce((hash, char) => {
44
+ const code = char.charCodeAt(0);
45
+ const newHash = (hash << 5) - hash + code;
46
+ return newHash & newHash; // Convert to 32-bit integer
47
+ }, 0);
48
+ return hash.toString(16);
49
+ };
50
+ /**
51
+ * Get the master key from credentials
52
+ */
53
+ export const getMasterKey = async (passphrase) => {
54
+ const credentials = await loadCredentials();
55
+ if (!credentials?.encryptedMasterKey || !credentials?.salt) {
56
+ return null;
57
+ }
58
+ try {
59
+ const salt = fromBase64(credentials.salt);
60
+ const encryptedMasterKey = fromBase64(credentials.encryptedMasterKey);
61
+ // The encrypted master key includes IV and tag
62
+ // Format: iv (12 bytes) + tag (16 bytes) + ciphertext
63
+ const iv = encryptedMasterKey.slice(0, 12);
64
+ const tag = encryptedMasterKey.slice(12, 28);
65
+ const ciphertext = encryptedMasterKey.slice(28);
66
+ const derivedKey = deriveKeyFromPassphrase(passphrase, salt);
67
+ const masterKey = decryptMasterKey(ciphertext, derivedKey.key, iv, tag);
68
+ return masterKey;
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ };
74
+ /**
75
+ * Encrypt a skill for upload
76
+ */
77
+ export const encryptSkill = (skill, masterKey) => {
78
+ // Create a JSON payload with skill metadata
79
+ const payload = JSON.stringify({
80
+ name: skill.name,
81
+ content: skill.content,
82
+ path: skill.path,
83
+ modifiedAt: skill.modifiedAt.toISOString(),
84
+ type: skill.type,
85
+ files: skill.files,
86
+ });
87
+ const { ciphertext, iv, tag } = encryptString(payload, masterKey);
88
+ return { encryptedData: ciphertext, iv, tag };
89
+ };
90
+ /**
91
+ * Decrypt a skill from download
92
+ */
93
+ export const decryptSkill = (encryptedData, iv, tag, masterKey) => {
94
+ const payload = decryptString(encryptedData, masterKey, iv, tag);
95
+ return JSON.parse(payload);
96
+ };
97
+ /**
98
+ * Push local skills to server
99
+ */
100
+ export const pushSkills = async (masterKey, onProgress, message) => {
101
+ const skills = await listAllSkills();
102
+ const index = await loadSyncIndex();
103
+ const results = await Promise.all(skills.map(async (skill) => {
104
+ // Use type:name as key to allow same name across types
105
+ const skillKey = getSkillKey(skill);
106
+ const contentHash = computeSkillHash(skill);
107
+ const localHash = hashContent(skill.content + JSON.stringify(skill.files ?? []));
108
+ const existing = index.skills[skillKey];
109
+ // Skip if unchanged (same content hash)
110
+ if (existing?.hash === contentHash) {
111
+ onProgress?.(`Skipping ${skill.name} (unchanged)`);
112
+ return { type: 'skipped' };
113
+ }
114
+ onProgress?.(`Pushing ${skill.type}/${skill.name} [${contentHash}]...`);
115
+ // Encrypt skill
116
+ const encrypted = encryptSkill(skill, masterKey);
117
+ // Push new version
118
+ const result = await api.pushSkillVersion(skillKey, contentHash, encrypted.encryptedData, encrypted.iv, encrypted.tag, message);
119
+ if (result.ok) {
120
+ return {
121
+ type: 'success',
122
+ skillKey,
123
+ hash: contentHash,
124
+ localHash,
125
+ remoteUpdatedAt: result.data.createdAt,
126
+ };
127
+ }
128
+ return {
129
+ type: 'error',
130
+ message: `Failed to push ${skill.type}/${skill.name}: ${result.error}`,
131
+ };
132
+ }));
133
+ // Process results
134
+ index.skills = results
135
+ .filter((result) => result.type === 'success')
136
+ .reduce((acc, result) => {
137
+ if (result.type === 'success') {
138
+ acc[result.skillKey] = {
139
+ hash: result.hash,
140
+ blobId: null,
141
+ localHash: result.localHash,
142
+ remoteUpdatedAt: result.remoteUpdatedAt,
143
+ };
144
+ }
145
+ return acc;
146
+ }, index.skills);
147
+ const pushed = results.filter((r) => r.type === 'success').length;
148
+ const errors = results
149
+ .filter((r) => r.type === 'error')
150
+ .map((r) => r.type === 'error' ? r.message : '');
151
+ // Save updated index
152
+ index.lastSyncAt = new Date().toISOString();
153
+ await saveSyncIndex(index);
154
+ return { pushed, errors };
155
+ };
156
+ /** Get the target directory for a skill type */
157
+ const getSkillTypeDir = (type) => {
158
+ const claudeDir = getClaudeDir();
159
+ const typeDirs = {
160
+ command: "commands",
161
+ skill: "skills",
162
+ agent: "agents",
163
+ };
164
+ return join(claudeDir, typeDirs[type]);
165
+ };
166
+ /**
167
+ * Pull remote skills to local
168
+ */
169
+ export const pullSkills = async (masterKey, onProgress) => {
170
+ const index = await loadSyncIndex();
171
+ // Get list of remote skills
172
+ const listResult = await api.listSkills();
173
+ if (!listResult.ok) {
174
+ return { pulled: 0, errors: [`Failed to list skills: ${listResult.error}`] };
175
+ }
176
+ const results = await Promise.all(listResult.data.skills.map(async (remoteSkill) => {
177
+ const existing = index.skills[remoteSkill.skillKey];
178
+ // Skip if unchanged (same hash)
179
+ if (existing?.hash === remoteSkill.currentHash) {
180
+ onProgress?.(`Skipping ${remoteSkill.skillKey} (unchanged)`);
181
+ return { type: 'skipped' };
182
+ }
183
+ // Skip if no versions
184
+ if (!remoteSkill.currentHash) {
185
+ onProgress?.(`Skipping ${remoteSkill.skillKey} (no versions)`);
186
+ return { type: 'skipped' };
187
+ }
188
+ onProgress?.(`Pulling ${remoteSkill.skillKey} [${remoteSkill.currentHash}]...`);
189
+ // Download current version
190
+ const skillResult = await api.getSkill(remoteSkill.skillKey);
191
+ if (!skillResult.ok) {
192
+ return {
193
+ type: 'error',
194
+ message: `Failed to get ${remoteSkill.skillKey}: ${skillResult.error}`,
195
+ };
196
+ }
197
+ // Decrypt
198
+ let decrypted;
199
+ try {
200
+ decrypted = decryptSkill(skillResult.data.encryptedData, skillResult.data.iv, skillResult.data.tag, masterKey);
201
+ }
202
+ catch (err) {
203
+ return {
204
+ type: 'error',
205
+ message: `Failed to decrypt ${remoteSkill.skillKey}: ${err}`,
206
+ };
207
+ }
208
+ const skillType = decrypted.type ?? "command";
209
+ const skillKey = remoteSkill.skillKey;
210
+ const baseDir = getSkillTypeDir(skillType);
211
+ try {
212
+ if (skillType === "skill" && decrypted.files) {
213
+ // Directory-based skill
214
+ const skillDir = join(baseDir, decrypted.name);
215
+ await mkdir(skillDir, { recursive: true });
216
+ // Write main skill file
217
+ await writeFile(join(skillDir, "SKILL.md"), decrypted.content, "utf-8");
218
+ // Write supporting files
219
+ await Promise.all(decrypted.files.map((f) => writeFile(join(skillDir, f.name), f.content, "utf-8")));
220
+ }
221
+ else {
222
+ // File-based skill (command or agent)
223
+ await mkdir(baseDir, { recursive: true });
224
+ await writeFile(join(baseDir, `${decrypted.name}.md`), decrypted.content, "utf-8");
225
+ }
226
+ }
227
+ catch (err) {
228
+ return {
229
+ type: 'error',
230
+ message: `Failed to write ${decrypted.name}: ${err}`,
231
+ };
232
+ }
233
+ onProgress?.(`Pulled ${skillType}/${decrypted.name} [${remoteSkill.currentHash}]`);
234
+ return {
235
+ type: 'success',
236
+ skillKey,
237
+ hash: remoteSkill.currentHash,
238
+ content: decrypted.content,
239
+ files: decrypted.files,
240
+ updatedAt: remoteSkill.updatedAt,
241
+ };
242
+ }));
243
+ // Process results
244
+ index.skills = results
245
+ .filter((result) => result.type === 'success')
246
+ .reduce((acc, result) => {
247
+ if (result.type === 'success') {
248
+ acc[result.skillKey] = {
249
+ hash: result.hash,
250
+ blobId: null,
251
+ localHash: hashContent(result.content + JSON.stringify(result.files ?? [])),
252
+ remoteUpdatedAt: result.updatedAt,
253
+ };
254
+ }
255
+ return acc;
256
+ }, index.skills);
257
+ const pulled = results.filter((r) => r.type === 'success').length;
258
+ const errors = results
259
+ .filter((r) => r.type === 'error')
260
+ .map((r) => r.type === 'error' ? r.message : '');
261
+ // Save updated index
262
+ index.lastSyncAt = new Date().toISOString();
263
+ await saveSyncIndex(index);
264
+ return { pulled, errors };
265
+ };
266
+ /**
267
+ * Get sync status for all skills
268
+ */
269
+ export const getSyncStatus = async () => {
270
+ const skills = await listAllSkills();
271
+ const index = await loadSyncIndex();
272
+ const skillStatuses = skills.map((skill) => {
273
+ const skillKey = getSkillKey(skill);
274
+ const contentHash = computeSkillHash(skill);
275
+ const existing = index.skills[skillKey];
276
+ return existing?.hash === contentHash ? 'synced' : 'pending';
277
+ });
278
+ const synced = skillStatuses.filter((s) => s === 'synced').length;
279
+ const pendingPush = skillStatuses.filter((s) => s === 'pending').length;
280
+ // Check for remote-only skills (pending pull)
281
+ const listResult = await api.listSkills();
282
+ const localSkillKeys = new Set(skills.map((s) => getSkillKey(s)));
283
+ const pendingPull = listResult.ok
284
+ ? listResult.data.skills.filter((s) => s.currentHash && !localSkillKeys.has(s.skillKey)).length
285
+ : 0;
286
+ return {
287
+ local: skills.length,
288
+ synced,
289
+ pendingPush,
290
+ pendingPull,
291
+ lastSyncAt: index.lastSyncAt || null,
292
+ };
293
+ };
294
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}