@slats/claude-assets-sync 0.0.3 → 0.0.5

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 (52) hide show
  1. package/dist/commands/add.cjs +2 -7
  2. package/dist/commands/add.mjs +2 -7
  3. package/dist/commands/index.d.ts +22 -0
  4. package/dist/commands/remove.cjs +13 -13
  5. package/dist/commands/remove.mjs +7 -7
  6. package/dist/commands/types.d.ts +2 -4
  7. package/dist/commands/update.cjs +178 -0
  8. package/dist/commands/update.d.ts +13 -0
  9. package/dist/commands/update.mjs +176 -0
  10. package/dist/components/add/AddCommand.cjs +11 -0
  11. package/dist/components/add/AddCommand.mjs +11 -0
  12. package/dist/components/list/ListCommand.cjs +65 -47
  13. package/dist/components/list/ListCommand.mjs +66 -48
  14. package/dist/components/status/StatusDisplay.cjs +4 -3
  15. package/dist/components/status/StatusDisplay.d.ts +2 -4
  16. package/dist/components/status/StatusDisplay.mjs +4 -3
  17. package/dist/components/tree/AssetTreeNode.cjs +21 -4
  18. package/dist/components/tree/AssetTreeNode.d.ts +1 -1
  19. package/dist/components/tree/AssetTreeNode.mjs +21 -4
  20. package/dist/components/tree/TreeSelect.cjs +14 -6
  21. package/dist/components/tree/TreeSelect.mjs +14 -6
  22. package/dist/core/assetStructure.d.ts +1 -1
  23. package/dist/core/cli.cjs +18 -0
  24. package/dist/core/cli.mjs +18 -0
  25. package/dist/core/constants.cjs +4 -1
  26. package/dist/core/constants.d.ts +8 -1
  27. package/dist/core/constants.mjs +4 -1
  28. package/dist/core/filesystem.cjs +18 -12
  29. package/dist/core/filesystem.d.ts +3 -2
  30. package/dist/core/filesystem.mjs +18 -12
  31. package/dist/core/github.cjs +31 -3
  32. package/dist/core/github.d.ts +14 -1
  33. package/dist/core/github.mjs +31 -4
  34. package/dist/core/migration.cjs +17 -15
  35. package/dist/core/migration.mjs +12 -10
  36. package/dist/core/packageScanner.cjs +110 -29
  37. package/dist/core/packageScanner.d.ts +6 -1
  38. package/dist/core/packageScanner.mjs +109 -30
  39. package/dist/core/sync.cjs +83 -59
  40. package/dist/core/sync.mjs +55 -31
  41. package/dist/core/syncMeta.cjs +153 -9
  42. package/dist/core/syncMeta.d.ts +22 -18
  43. package/dist/core/syncMeta.mjs +149 -9
  44. package/dist/utils/nameTransform.cjs +7 -6
  45. package/dist/utils/nameTransform.d.ts +22 -33
  46. package/dist/utils/nameTransform.mjs +8 -6
  47. package/dist/utils/types.d.ts +24 -7
  48. package/dist/version.cjs +1 -1
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.mjs +1 -1
  51. package/package.json +2 -2
  52. package/CHANGELOG.md +0 -189
@@ -3,6 +3,7 @@
3
3
  var fs = require('node:fs');
4
4
  var path = require('node:path');
5
5
  var _package = require('../utils/package.cjs');
6
+ var nameTransform = require('../utils/nameTransform.cjs');
6
7
  var constants = require('./constants.cjs');
7
8
  var github = require('./github.cjs');
8
9
 
@@ -72,15 +73,25 @@ async function scanRemoteAssets(packageName, ref) {
72
73
  throw new Error(`Invalid GitHub repository URL in package ${packageName}`);
73
74
  }
74
75
  const assetBasePath = pkgInfo.claude.assetPath;
76
+ const tag = ref ?? 'HEAD';
75
77
  const trees = [];
76
78
  for (const assetType of constants.DEFAULT_ASSET_TYPES) {
77
79
  const assetPath = repoInfo.directory
78
80
  ? `${repoInfo.directory}/${assetBasePath}/${assetType}`
79
81
  : `${assetBasePath}/${assetType}`;
80
82
  try {
81
- const entries = await github.fetchDirectoryContents(repoInfo, assetPath, ref ?? 'HEAD');
83
+ const entries = await github.fetchDirectoryContents(repoInfo, assetPath, tag);
82
84
  if (entries && entries.length > 0) {
83
- const tree = buildTreeFromGitHubEntries(assetType, entries, assetType);
85
+ const dirContentsMap = new Map();
86
+ for (const entry of entries) {
87
+ if (entry.type === 'dir') {
88
+ const dirEntries = await github.fetchDirectoryContents(repoInfo, `${assetPath}/${entry.name}`, tag);
89
+ if (dirEntries) {
90
+ dirContentsMap.set(entry.name, dirEntries);
91
+ }
92
+ }
93
+ }
94
+ const tree = buildTreeFromGitHubEntries(assetType, entries, assetType, dirContentsMap);
84
95
  if (tree.children && tree.children.length > 0) {
85
96
  trees.push(tree);
86
97
  }
@@ -92,6 +103,26 @@ async function scanRemoteAssets(packageName, ref) {
92
103
  }
93
104
  return trees;
94
105
  }
106
+ function scanDirectoryRecursive(dirPath, prefix) {
107
+ const results = [];
108
+ try {
109
+ const entries = fs.readdirSync(dirPath);
110
+ for (const entry of entries) {
111
+ const fullPath = path.join(dirPath, entry);
112
+ const relativePath = prefix ? `${prefix}/${entry}` : entry;
113
+ const stat = fs.statSync(fullPath);
114
+ if (stat.isDirectory()) {
115
+ results.push(...scanDirectoryRecursive(fullPath, relativePath));
116
+ }
117
+ else {
118
+ results.push(relativePath);
119
+ }
120
+ }
121
+ }
122
+ catch {
123
+ }
124
+ return results;
125
+ }
95
126
  function buildTreeFromLocalDir(label, dirPath, basePath) {
96
127
  const entries = fs.readdirSync(dirPath);
97
128
  const children = [];
@@ -100,13 +131,26 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
100
131
  const stat = fs.statSync(fullPath);
101
132
  const relativePath = path.join(basePath, entry);
102
133
  if (stat.isDirectory()) {
103
- const isSkill = fs.existsSync(path.join(fullPath, 'Skill.md'));
134
+ const isSkill = fs.existsSync(path.join(fullPath, 'SKILL.md')) ||
135
+ fs.existsSync(path.join(fullPath, 'Skill.md'));
104
136
  if (isSkill) {
137
+ const internalFiles = scanDirectoryRecursive(fullPath, '');
138
+ const internalChildren = internalFiles.map((f) => ({
139
+ id: `${relativePath}/${f}`,
140
+ label: f,
141
+ path: `${relativePath}/${f}`,
142
+ type: 'file',
143
+ selected: true,
144
+ expanded: false,
145
+ disabled: true,
146
+ }));
105
147
  children.push({
106
148
  id: relativePath,
107
149
  label: entry,
108
150
  path: relativePath,
109
151
  type: 'skill-directory',
152
+ children: internalChildren,
153
+ viewOnly: true,
110
154
  selected: true,
111
155
  expanded: false,
112
156
  });
@@ -139,36 +183,50 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
139
183
  expanded: true,
140
184
  };
141
185
  }
142
- function buildTreeFromGitHubEntries(label, entries, basePath) {
186
+ function buildTreeFromGitHubEntries(label, entries, basePath, dirContentsMap) {
143
187
  const children = [];
144
- const grouped = groupByTopLevel(entries);
145
- for (const [name, items] of Object.entries(grouped)) {
146
- if (items.length === 1 && items[0].type === 'file') {
147
- const filePath = items[0].path;
188
+ for (const entry of entries) {
189
+ if (entry.type === 'file') {
148
190
  children.push({
149
- id: filePath,
150
- label: name,
151
- path: filePath,
191
+ id: entry.path,
192
+ label: entry.name,
193
+ path: entry.path,
152
194
  type: 'file',
153
195
  selected: true,
154
196
  expanded: false,
155
197
  });
156
198
  }
157
- else {
158
- const isSkill = items.some((item) => item.type === 'file' && item.name === 'Skill.md');
159
- if (isSkill) {
160
- const skillPath = `${basePath}/${name}`;
199
+ else if (entry.type === 'dir') {
200
+ const dirEntries = dirContentsMap?.get(entry.name);
201
+ const hasSkillMd = dirEntries
202
+ ? isDirectorySkill(dirEntries)
203
+ : false;
204
+ if (hasSkillMd) {
205
+ const skillPath = `${basePath}/${entry.name}`;
206
+ const internalChildren = dirEntries
207
+ ? dirEntries.map((de) => ({
208
+ id: `${skillPath}/${de.name}`,
209
+ label: de.name,
210
+ path: `${skillPath}/${de.name}`,
211
+ type: 'file',
212
+ selected: true,
213
+ expanded: false,
214
+ disabled: true,
215
+ }))
216
+ : [];
161
217
  children.push({
162
218
  id: skillPath,
163
- label: name,
219
+ label: entry.name,
164
220
  path: skillPath,
165
221
  type: 'skill-directory',
222
+ children: internalChildren,
223
+ viewOnly: true,
166
224
  selected: true,
167
225
  expanded: false,
168
226
  });
169
227
  }
170
- else {
171
- const subTree = buildTreeFromGitHubEntries(name, items, `${basePath}/${name}`);
228
+ else if (dirEntries && dirEntries.length > 0) {
229
+ const subTree = buildTreeFromGitHubEntries(entry.name, dirEntries, `${basePath}/${entry.name}`);
172
230
  if (subTree.children && subTree.children.length > 0) {
173
231
  children.push(subTree);
174
232
  }
@@ -185,17 +243,9 @@ function buildTreeFromGitHubEntries(label, entries, basePath) {
185
243
  expanded: true,
186
244
  };
187
245
  }
188
- function groupByTopLevel(entries) {
189
- const groups = {};
190
- for (const entry of entries) {
191
- const parts = entry.path.split('/');
192
- const topLevel = parts[parts.length - (entry.type === 'file' ? 1 : 0)];
193
- if (!groups[topLevel]) {
194
- groups[topLevel] = [];
195
- }
196
- groups[topLevel].push(entry);
197
- }
198
- return groups;
246
+ function isDirectorySkill(entries) {
247
+ return entries.some((entry) => entry.type === 'file' &&
248
+ (entry.name === 'SKILL.md' || entry.name === 'Skill.md'));
199
249
  }
200
250
  function findLocalPackage(packageName, cwd) {
201
251
  const monorepoRoot = findMonorepoRoot(cwd);
@@ -255,5 +305,36 @@ function searchPackagesRecursively(dir, packageName) {
255
305
  }
256
306
  return null;
257
307
  }
308
+ function buildSkillUnitsFromTree(tree, prefix) {
309
+ if (!tree.children)
310
+ return [];
311
+ const units = [];
312
+ for (const child of tree.children) {
313
+ if (child.type === 'skill-directory') {
314
+ const internalFiles = child.children
315
+ ? child.children.map((c) => c.label)
316
+ : [];
317
+ units.push({
318
+ name: child.label,
319
+ isDirectory: true,
320
+ transformed: nameTransform.toFlatFileName(prefix, child.label),
321
+ internalFiles,
322
+ });
323
+ }
324
+ else if (child.type === 'file') {
325
+ units.push({
326
+ name: child.label,
327
+ isDirectory: false,
328
+ transformed: nameTransform.toFlatFileName(prefix, child.label),
329
+ });
330
+ }
331
+ else if (child.type === 'directory') {
332
+ units.push(...buildSkillUnitsFromTree(child, prefix));
333
+ }
334
+ }
335
+ return units;
336
+ }
258
337
 
338
+ exports.buildSkillUnitsFromTree = buildSkillUnitsFromTree;
339
+ exports.isDirectorySkill = isDirectorySkill;
259
340
  exports.scanPackageAssets = scanPackageAssets;
@@ -1,4 +1,4 @@
1
- import type { GitHubEntry, TreeNode } from '../utils/types.js';
1
+ import type { GitHubEntry, SkillUnit, TreeNode } from '../utils/types.js';
2
2
  /**
3
3
  * Scan package assets from local workspace or GitHub
4
4
  *
@@ -15,3 +15,8 @@ export declare function scanPackageAssets(packageName: string, options: {
15
15
  * Check if entries represent a directory-based skill
16
16
  */
17
17
  export declare function isDirectorySkill(entries: GitHubEntry[]): boolean;
18
+ /**
19
+ * Convert a TreeNode asset-type subtree into SkillUnit[] for writing to meta.
20
+ * Used by both `add` and `list` commands when saving.
21
+ */
22
+ export declare function buildSkillUnitsFromTree(tree: TreeNode, prefix: string): SkillUnit[];
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { parseGitHubRepo } from '../utils/package.mjs';
4
+ import { toFlatFileName } from '../utils/nameTransform.mjs';
4
5
  import { DEFAULT_ASSET_TYPES } from './constants.mjs';
5
6
  import { fetchDirectoryContents } from './github.mjs';
6
7
 
@@ -70,15 +71,25 @@ async function scanRemoteAssets(packageName, ref) {
70
71
  throw new Error(`Invalid GitHub repository URL in package ${packageName}`);
71
72
  }
72
73
  const assetBasePath = pkgInfo.claude.assetPath;
74
+ const tag = ref ?? 'HEAD';
73
75
  const trees = [];
74
76
  for (const assetType of DEFAULT_ASSET_TYPES) {
75
77
  const assetPath = repoInfo.directory
76
78
  ? `${repoInfo.directory}/${assetBasePath}/${assetType}`
77
79
  : `${assetBasePath}/${assetType}`;
78
80
  try {
79
- const entries = await fetchDirectoryContents(repoInfo, assetPath, ref ?? 'HEAD');
81
+ const entries = await fetchDirectoryContents(repoInfo, assetPath, tag);
80
82
  if (entries && entries.length > 0) {
81
- const tree = buildTreeFromGitHubEntries(assetType, entries, assetType);
83
+ const dirContentsMap = new Map();
84
+ for (const entry of entries) {
85
+ if (entry.type === 'dir') {
86
+ const dirEntries = await fetchDirectoryContents(repoInfo, `${assetPath}/${entry.name}`, tag);
87
+ if (dirEntries) {
88
+ dirContentsMap.set(entry.name, dirEntries);
89
+ }
90
+ }
91
+ }
92
+ const tree = buildTreeFromGitHubEntries(assetType, entries, assetType, dirContentsMap);
82
93
  if (tree.children && tree.children.length > 0) {
83
94
  trees.push(tree);
84
95
  }
@@ -90,6 +101,26 @@ async function scanRemoteAssets(packageName, ref) {
90
101
  }
91
102
  return trees;
92
103
  }
104
+ function scanDirectoryRecursive(dirPath, prefix) {
105
+ const results = [];
106
+ try {
107
+ const entries = readdirSync(dirPath);
108
+ for (const entry of entries) {
109
+ const fullPath = join(dirPath, entry);
110
+ const relativePath = prefix ? `${prefix}/${entry}` : entry;
111
+ const stat = statSync(fullPath);
112
+ if (stat.isDirectory()) {
113
+ results.push(...scanDirectoryRecursive(fullPath, relativePath));
114
+ }
115
+ else {
116
+ results.push(relativePath);
117
+ }
118
+ }
119
+ }
120
+ catch {
121
+ }
122
+ return results;
123
+ }
93
124
  function buildTreeFromLocalDir(label, dirPath, basePath) {
94
125
  const entries = readdirSync(dirPath);
95
126
  const children = [];
@@ -98,13 +129,26 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
98
129
  const stat = statSync(fullPath);
99
130
  const relativePath = join(basePath, entry);
100
131
  if (stat.isDirectory()) {
101
- const isSkill = existsSync(join(fullPath, 'Skill.md'));
132
+ const isSkill = existsSync(join(fullPath, 'SKILL.md')) ||
133
+ existsSync(join(fullPath, 'Skill.md'));
102
134
  if (isSkill) {
135
+ const internalFiles = scanDirectoryRecursive(fullPath, '');
136
+ const internalChildren = internalFiles.map((f) => ({
137
+ id: `${relativePath}/${f}`,
138
+ label: f,
139
+ path: `${relativePath}/${f}`,
140
+ type: 'file',
141
+ selected: true,
142
+ expanded: false,
143
+ disabled: true,
144
+ }));
103
145
  children.push({
104
146
  id: relativePath,
105
147
  label: entry,
106
148
  path: relativePath,
107
149
  type: 'skill-directory',
150
+ children: internalChildren,
151
+ viewOnly: true,
108
152
  selected: true,
109
153
  expanded: false,
110
154
  });
@@ -137,36 +181,50 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
137
181
  expanded: true,
138
182
  };
139
183
  }
140
- function buildTreeFromGitHubEntries(label, entries, basePath) {
184
+ function buildTreeFromGitHubEntries(label, entries, basePath, dirContentsMap) {
141
185
  const children = [];
142
- const grouped = groupByTopLevel(entries);
143
- for (const [name, items] of Object.entries(grouped)) {
144
- if (items.length === 1 && items[0].type === 'file') {
145
- const filePath = items[0].path;
186
+ for (const entry of entries) {
187
+ if (entry.type === 'file') {
146
188
  children.push({
147
- id: filePath,
148
- label: name,
149
- path: filePath,
189
+ id: entry.path,
190
+ label: entry.name,
191
+ path: entry.path,
150
192
  type: 'file',
151
193
  selected: true,
152
194
  expanded: false,
153
195
  });
154
196
  }
155
- else {
156
- const isSkill = items.some((item) => item.type === 'file' && item.name === 'Skill.md');
157
- if (isSkill) {
158
- const skillPath = `${basePath}/${name}`;
197
+ else if (entry.type === 'dir') {
198
+ const dirEntries = dirContentsMap?.get(entry.name);
199
+ const hasSkillMd = dirEntries
200
+ ? isDirectorySkill(dirEntries)
201
+ : false;
202
+ if (hasSkillMd) {
203
+ const skillPath = `${basePath}/${entry.name}`;
204
+ const internalChildren = dirEntries
205
+ ? dirEntries.map((de) => ({
206
+ id: `${skillPath}/${de.name}`,
207
+ label: de.name,
208
+ path: `${skillPath}/${de.name}`,
209
+ type: 'file',
210
+ selected: true,
211
+ expanded: false,
212
+ disabled: true,
213
+ }))
214
+ : [];
159
215
  children.push({
160
216
  id: skillPath,
161
- label: name,
217
+ label: entry.name,
162
218
  path: skillPath,
163
219
  type: 'skill-directory',
220
+ children: internalChildren,
221
+ viewOnly: true,
164
222
  selected: true,
165
223
  expanded: false,
166
224
  });
167
225
  }
168
- else {
169
- const subTree = buildTreeFromGitHubEntries(name, items, `${basePath}/${name}`);
226
+ else if (dirEntries && dirEntries.length > 0) {
227
+ const subTree = buildTreeFromGitHubEntries(entry.name, dirEntries, `${basePath}/${entry.name}`);
170
228
  if (subTree.children && subTree.children.length > 0) {
171
229
  children.push(subTree);
172
230
  }
@@ -183,17 +241,9 @@ function buildTreeFromGitHubEntries(label, entries, basePath) {
183
241
  expanded: true,
184
242
  };
185
243
  }
186
- function groupByTopLevel(entries) {
187
- const groups = {};
188
- for (const entry of entries) {
189
- const parts = entry.path.split('/');
190
- const topLevel = parts[parts.length - (entry.type === 'file' ? 1 : 0)];
191
- if (!groups[topLevel]) {
192
- groups[topLevel] = [];
193
- }
194
- groups[topLevel].push(entry);
195
- }
196
- return groups;
244
+ function isDirectorySkill(entries) {
245
+ return entries.some((entry) => entry.type === 'file' &&
246
+ (entry.name === 'SKILL.md' || entry.name === 'Skill.md'));
197
247
  }
198
248
  function findLocalPackage(packageName, cwd) {
199
249
  const monorepoRoot = findMonorepoRoot(cwd);
@@ -253,5 +303,34 @@ function searchPackagesRecursively(dir, packageName) {
253
303
  }
254
304
  return null;
255
305
  }
306
+ function buildSkillUnitsFromTree(tree, prefix) {
307
+ if (!tree.children)
308
+ return [];
309
+ const units = [];
310
+ for (const child of tree.children) {
311
+ if (child.type === 'skill-directory') {
312
+ const internalFiles = child.children
313
+ ? child.children.map((c) => c.label)
314
+ : [];
315
+ units.push({
316
+ name: child.label,
317
+ isDirectory: true,
318
+ transformed: toFlatFileName(prefix, child.label),
319
+ internalFiles,
320
+ });
321
+ }
322
+ else if (child.type === 'file') {
323
+ units.push({
324
+ name: child.label,
325
+ isDirectory: false,
326
+ transformed: toFlatFileName(prefix, child.label),
327
+ });
328
+ }
329
+ else if (child.type === 'directory') {
330
+ units.push(...buildSkillUnitsFromTree(child, prefix));
331
+ }
332
+ }
333
+ return units;
334
+ }
256
335
 
257
- export { scanPackageAssets };
336
+ export { buildSkillUnitsFromTree, isDirectorySkill, scanPackageAssets };