@slats/claude-assets-sync 0.0.2 → 0.0.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.
- package/dist/cli.cjs +8 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +7 -0
- package/dist/commands/remove.cjs +7 -7
- package/dist/commands/remove.mjs +1 -1
- package/dist/core/assetStructure.d.ts +1 -1
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/filesystem.cjs +20 -8
- package/dist/core/filesystem.d.ts +3 -2
- package/dist/core/filesystem.mjs +20 -8
- package/dist/core/github.cjs +31 -3
- package/dist/core/github.d.ts +14 -1
- package/dist/core/github.mjs +31 -4
- package/dist/core/migration.cjs +7 -6
- package/dist/core/migration.mjs +2 -1
- package/dist/core/packageScanner.cjs +34 -29
- package/dist/core/packageScanner.mjs +34 -30
- package/dist/core/sync.cjs +30 -29
- package/dist/core/sync.mjs +2 -1
- package/dist/core/syncMeta.d.ts +1 -1
- package/dist/index.cjs +4 -8
- package/dist/index.d.ts +0 -1
- package/dist/index.mjs +1 -10
- package/dist/utils/nameTransform.cjs +7 -6
- package/dist/utils/nameTransform.d.ts +22 -33
- package/dist/utils/nameTransform.mjs +8 -6
- package/dist/version.cjs +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +3 -3
- package/CHANGELOG.md +0 -189
package/dist/cli.cjs
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.mjs
ADDED
package/dist/commands/remove.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var readline = require('node:readline/promises');
|
|
|
6
6
|
var pc = require('picocolors');
|
|
7
7
|
var syncMeta = require('../core/syncMeta.cjs');
|
|
8
8
|
var logger = require('../utils/logger.cjs');
|
|
9
|
-
var
|
|
9
|
+
var packageName = require('../utils/packageName.cjs');
|
|
10
10
|
|
|
11
11
|
function _interopNamespaceDefault(e) {
|
|
12
12
|
var n = Object.create(null);
|
|
@@ -30,17 +30,17 @@ var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
|
30
30
|
var readline__namespace = /*#__PURE__*/_interopNamespaceDefault(readline);
|
|
31
31
|
|
|
32
32
|
const runRemoveCommand = async (options, cwd = process.cwd()) => {
|
|
33
|
-
const { package: packageName, yes, dryRun } = options;
|
|
34
|
-
const prefix =
|
|
33
|
+
const { package: packageName$1, yes, dryRun } = options;
|
|
34
|
+
const prefix = packageName.packageNameToPrefix(packageName$1);
|
|
35
35
|
const meta = syncMeta.readUnifiedSyncMeta(cwd);
|
|
36
36
|
if (!meta || !meta.packages || !meta.packages[prefix]) {
|
|
37
|
-
logger.logger.error(`Package ${packageName} is not synced.`);
|
|
37
|
+
logger.logger.error(`Package ${packageName$1} is not synced.`);
|
|
38
38
|
process.exit(1);
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
const packageInfo = meta.packages[prefix];
|
|
42
42
|
if (!packageInfo || !packageInfo.files) {
|
|
43
|
-
logger.logger.error(`Package ${packageName} has no files to remove.`);
|
|
43
|
+
logger.logger.error(`Package ${packageName$1} has no files to remove.`);
|
|
44
44
|
process.exit(1);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
@@ -57,7 +57,7 @@ const runRemoveCommand = async (options, cwd = process.cwd()) => {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
|
-
const dirPath = path__namespace.join(cwd, '.claude', assetType, packageName);
|
|
60
|
+
const dirPath = path__namespace.join(cwd, '.claude', assetType, packageName$1);
|
|
61
61
|
filesToRemove.push({ assetType, path: dirPath });
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -103,7 +103,7 @@ const runRemoveCommand = async (options, cwd = process.cwd()) => {
|
|
|
103
103
|
delete meta.packages[prefix];
|
|
104
104
|
meta.syncedAt = new Date().toISOString();
|
|
105
105
|
syncMeta.writeUnifiedSyncMeta(cwd, meta);
|
|
106
|
-
logger.logger.success(`\nRemoved package ${packageName}`);
|
|
106
|
+
logger.logger.success(`\nRemoved package ${packageName$1}`);
|
|
107
107
|
};
|
|
108
108
|
|
|
109
109
|
exports.runRemoveCommand = runRemoveCommand;
|
package/dist/commands/remove.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import * as readline from 'node:readline/promises';
|
|
|
4
4
|
import pc from 'picocolors';
|
|
5
5
|
import { readUnifiedSyncMeta, writeUnifiedSyncMeta } from '../core/syncMeta.mjs';
|
|
6
6
|
import { logger } from '../utils/logger.mjs';
|
|
7
|
-
import { packageNameToPrefix } from '../utils/
|
|
7
|
+
import { packageNameToPrefix } from '../utils/packageName.mjs';
|
|
8
8
|
|
|
9
9
|
const runRemoveCommand = async (options, cwd = process.cwd()) => {
|
|
10
10
|
const { package: packageName, yes, dryRun } = options;
|
package/dist/core/constants.d.ts
CHANGED
package/dist/core/filesystem.cjs
CHANGED
|
@@ -58,24 +58,36 @@ const cleanFlatAssetFiles = (cwd, assetType, prefix, existingMeta) => {
|
|
|
58
58
|
const packageInfo = existingMeta.packages[prefix];
|
|
59
59
|
const filesToRemove = packageInfo.files[assetType];
|
|
60
60
|
if (Array.isArray(filesToRemove)) {
|
|
61
|
+
const skillDirs = new Set();
|
|
61
62
|
for (const fileMapping of filesToRemove) {
|
|
62
63
|
const fileName = typeof fileMapping === 'string'
|
|
63
64
|
? fileMapping
|
|
64
65
|
: fileMapping.transformed;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
if (fileName.includes('/')) {
|
|
67
|
+
skillDirs.add(fileName.split('/')[0]);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const filePath = path.join(destDir, fileName);
|
|
71
|
+
if (io.fileExists(filePath)) {
|
|
72
|
+
fs.rmSync(filePath, { force: true });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const dir of skillDirs) {
|
|
77
|
+
const dirPath = path.join(destDir, dir);
|
|
78
|
+
if (io.fileExists(dirPath)) {
|
|
79
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
68
80
|
}
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
}
|
|
72
84
|
else {
|
|
73
85
|
const pattern = `${prefix}_`;
|
|
74
|
-
const
|
|
75
|
-
for (const
|
|
76
|
-
if (
|
|
77
|
-
const
|
|
78
|
-
fs.rmSync(
|
|
86
|
+
const entries = io.listDirectory(destDir);
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (entry.startsWith(pattern)) {
|
|
89
|
+
const entryPath = path.join(destDir, entry);
|
|
90
|
+
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssetType, SyncMeta, UnifiedSyncMeta } from '../utils/types';
|
|
1
|
+
import type { AssetType, SyncMeta, UnifiedSyncMeta } from '../utils/types.js';
|
|
2
2
|
import { ensureDirectory, writeTextFile } from './io';
|
|
3
3
|
/**
|
|
4
4
|
* Ensure directory exists (creates recursively if needed)
|
|
@@ -71,7 +71,8 @@ export declare const createSyncMeta: (version: string, files: string[]) => SyncM
|
|
|
71
71
|
export declare const writeFlatAssetFile: (cwd: string, assetType: AssetType, flatFileName: string, content: string) => void;
|
|
72
72
|
/**
|
|
73
73
|
* Clean flat asset files with specific prefix
|
|
74
|
-
* Removes only files belonging to the specified package, preserving others
|
|
74
|
+
* Removes only files belonging to the specified package, preserving others.
|
|
75
|
+
* Handles both single flat files (prefix_file.md) and directory-based skills (prefix_dir/).
|
|
75
76
|
* @param cwd - Current working directory
|
|
76
77
|
* @param assetType - Asset type (commands, skills, agents, or any custom string)
|
|
77
78
|
* @param prefix - Package prefix (e.g., "canard-schemaForm")
|
package/dist/core/filesystem.mjs
CHANGED
|
@@ -56,24 +56,36 @@ const cleanFlatAssetFiles = (cwd, assetType, prefix, existingMeta) => {
|
|
|
56
56
|
const packageInfo = existingMeta.packages[prefix];
|
|
57
57
|
const filesToRemove = packageInfo.files[assetType];
|
|
58
58
|
if (Array.isArray(filesToRemove)) {
|
|
59
|
+
const skillDirs = new Set();
|
|
59
60
|
for (const fileMapping of filesToRemove) {
|
|
60
61
|
const fileName = typeof fileMapping === 'string'
|
|
61
62
|
? fileMapping
|
|
62
63
|
: fileMapping.transformed;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
if (fileName.includes('/')) {
|
|
65
|
+
skillDirs.add(fileName.split('/')[0]);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const filePath = join(destDir, fileName);
|
|
69
|
+
if (fileExists(filePath)) {
|
|
70
|
+
rmSync(filePath, { force: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const dir of skillDirs) {
|
|
75
|
+
const dirPath = join(destDir, dir);
|
|
76
|
+
if (fileExists(dirPath)) {
|
|
77
|
+
rmSync(dirPath, { recursive: true, force: true });
|
|
66
78
|
}
|
|
67
79
|
}
|
|
68
80
|
}
|
|
69
81
|
}
|
|
70
82
|
else {
|
|
71
83
|
const pattern = `${prefix}_`;
|
|
72
|
-
const
|
|
73
|
-
for (const
|
|
74
|
-
if (
|
|
75
|
-
const
|
|
76
|
-
rmSync(
|
|
84
|
+
const entries = listDirectory(destDir);
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry.startsWith(pattern)) {
|
|
87
|
+
const entryPath = join(destDir, entry);
|
|
88
|
+
rmSync(entryPath, { recursive: true, force: true });
|
|
77
89
|
}
|
|
78
90
|
}
|
|
79
91
|
}
|
package/dist/core/github.cjs
CHANGED
|
@@ -37,17 +37,44 @@ const fetchDirectoryContents = async (repoInfo, path, tag) => {
|
|
|
37
37
|
if (!response.ok)
|
|
38
38
|
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
39
39
|
const data = await response.json();
|
|
40
|
-
return data.filter((entry) => entry.type === 'file' && entry.name.endsWith('.md'))
|
|
40
|
+
return data.filter((entry) => (entry.type === 'file' && entry.name.endsWith('.md')) ||
|
|
41
|
+
entry.type === 'dir');
|
|
42
|
+
};
|
|
43
|
+
const expandDirectoryEntries = async (repoInfo, parentPath, entries, tag, prefix = '') => {
|
|
44
|
+
const result = [];
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
const entryPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
47
|
+
if (entry.type === 'file') {
|
|
48
|
+
result.push({
|
|
49
|
+
...entry,
|
|
50
|
+
name: prefix ? entryPrefix : entry.name,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else if (entry.type === 'dir') {
|
|
54
|
+
const subEntries = await fetchDirectoryContents(repoInfo, `${parentPath}/${entry.name}`, tag);
|
|
55
|
+
if (subEntries) {
|
|
56
|
+
const expanded = await expandDirectoryEntries(repoInfo, `${parentPath}/${entry.name}`, subEntries, tag, entryPrefix);
|
|
57
|
+
result.push(...expanded);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
41
62
|
};
|
|
42
63
|
const fetchAssetFiles = async (repoInfo, assetPath, tag, assetTypes) => {
|
|
43
64
|
const basePath = repoInfo.directory
|
|
44
65
|
? `${repoInfo.directory}/${assetPath}`
|
|
45
66
|
: assetPath;
|
|
46
67
|
const fetchPromises = assetTypes.map((assetType) => fetchDirectoryContents(repoInfo, `${basePath}/${assetType}`, tag));
|
|
47
|
-
const
|
|
68
|
+
const rawResults = await Promise.all(fetchPromises);
|
|
69
|
+
const expandedResults = await Promise.all(rawResults.map((entries, index) => {
|
|
70
|
+
if (!entries)
|
|
71
|
+
return Promise.resolve([]);
|
|
72
|
+
const assetDirPath = `${basePath}/${assetTypes[index]}`;
|
|
73
|
+
return expandDirectoryEntries(repoInfo, assetDirPath, entries, tag);
|
|
74
|
+
}));
|
|
48
75
|
const assetFiles = {};
|
|
49
76
|
assetTypes.forEach((assetType, index) => {
|
|
50
|
-
assetFiles[assetType] =
|
|
77
|
+
assetFiles[assetType] = expandedResults[index] || [];
|
|
51
78
|
});
|
|
52
79
|
return assetFiles;
|
|
53
80
|
};
|
|
@@ -83,5 +110,6 @@ exports.NotFoundError = NotFoundError;
|
|
|
83
110
|
exports.RateLimitError = RateLimitError;
|
|
84
111
|
exports.downloadAssetFiles = downloadAssetFiles;
|
|
85
112
|
exports.downloadFile = downloadFile;
|
|
113
|
+
exports.expandDirectoryEntries = expandDirectoryEntries;
|
|
86
114
|
exports.fetchAssetFiles = fetchAssetFiles;
|
|
87
115
|
exports.fetchDirectoryContents = fetchDirectoryContents;
|
package/dist/core/github.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssetType, GitHubEntry, GitHubRepoInfo } from '../utils/types';
|
|
1
|
+
import type { AssetType, GitHubEntry, GitHubRepoInfo } from '../utils/types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Error thrown when GitHub API rate limit is exceeded
|
|
4
4
|
*/
|
|
@@ -19,6 +19,19 @@ export declare class NotFoundError extends Error {
|
|
|
19
19
|
* @returns Array of GitHubEntry or null if directory doesn't exist
|
|
20
20
|
*/
|
|
21
21
|
export declare const fetchDirectoryContents: (repoInfo: GitHubRepoInfo, path: string, tag: string) => Promise<GitHubEntry[] | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Expand directory entries into flat file entries with recursive traversal.
|
|
24
|
+
* Fetches contents of each directory and prefixes file names with the directory path.
|
|
25
|
+
* Recursively traverses subdirectories to collect all nested files.
|
|
26
|
+
*
|
|
27
|
+
* @param repoInfo - GitHub repository information
|
|
28
|
+
* @param parentPath - Parent directory path in the repository
|
|
29
|
+
* @param entries - Array of GitHubEntry (may contain both file and dir types)
|
|
30
|
+
* @param tag - Git tag or ref to fetch from
|
|
31
|
+
* @param prefix - Accumulated path prefix for nested entries
|
|
32
|
+
* @returns Flat array of file GitHubEntry with dir-prefixed names
|
|
33
|
+
*/
|
|
34
|
+
export declare const expandDirectoryEntries: (repoInfo: GitHubRepoInfo, parentPath: string, entries: GitHubEntry[], tag: string, prefix?: string) => Promise<GitHubEntry[]>;
|
|
22
35
|
/**
|
|
23
36
|
* Fetch asset files dynamically from GitHub
|
|
24
37
|
* @param repoInfo - GitHub repository information
|
package/dist/core/github.mjs
CHANGED
|
@@ -35,17 +35,44 @@ const fetchDirectoryContents = async (repoInfo, path, tag) => {
|
|
|
35
35
|
if (!response.ok)
|
|
36
36
|
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
37
37
|
const data = await response.json();
|
|
38
|
-
return data.filter((entry) => entry.type === 'file' && entry.name.endsWith('.md'))
|
|
38
|
+
return data.filter((entry) => (entry.type === 'file' && entry.name.endsWith('.md')) ||
|
|
39
|
+
entry.type === 'dir');
|
|
40
|
+
};
|
|
41
|
+
const expandDirectoryEntries = async (repoInfo, parentPath, entries, tag, prefix = '') => {
|
|
42
|
+
const result = [];
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
const entryPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
45
|
+
if (entry.type === 'file') {
|
|
46
|
+
result.push({
|
|
47
|
+
...entry,
|
|
48
|
+
name: prefix ? entryPrefix : entry.name,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else if (entry.type === 'dir') {
|
|
52
|
+
const subEntries = await fetchDirectoryContents(repoInfo, `${parentPath}/${entry.name}`, tag);
|
|
53
|
+
if (subEntries) {
|
|
54
|
+
const expanded = await expandDirectoryEntries(repoInfo, `${parentPath}/${entry.name}`, subEntries, tag, entryPrefix);
|
|
55
|
+
result.push(...expanded);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
39
60
|
};
|
|
40
61
|
const fetchAssetFiles = async (repoInfo, assetPath, tag, assetTypes) => {
|
|
41
62
|
const basePath = repoInfo.directory
|
|
42
63
|
? `${repoInfo.directory}/${assetPath}`
|
|
43
64
|
: assetPath;
|
|
44
65
|
const fetchPromises = assetTypes.map((assetType) => fetchDirectoryContents(repoInfo, `${basePath}/${assetType}`, tag));
|
|
45
|
-
const
|
|
66
|
+
const rawResults = await Promise.all(fetchPromises);
|
|
67
|
+
const expandedResults = await Promise.all(rawResults.map((entries, index) => {
|
|
68
|
+
if (!entries)
|
|
69
|
+
return Promise.resolve([]);
|
|
70
|
+
const assetDirPath = `${basePath}/${assetTypes[index]}`;
|
|
71
|
+
return expandDirectoryEntries(repoInfo, assetDirPath, entries, tag);
|
|
72
|
+
}));
|
|
46
73
|
const assetFiles = {};
|
|
47
74
|
assetTypes.forEach((assetType, index) => {
|
|
48
|
-
assetFiles[assetType] =
|
|
75
|
+
assetFiles[assetType] = expandedResults[index] || [];
|
|
49
76
|
});
|
|
50
77
|
return assetFiles;
|
|
51
78
|
};
|
|
@@ -77,4 +104,4 @@ const downloadAssetFiles = async (repoInfo, assetPath, assetType, entries, tag)
|
|
|
77
104
|
return results;
|
|
78
105
|
};
|
|
79
106
|
|
|
80
|
-
export { NotFoundError, RateLimitError, downloadAssetFiles, downloadFile, fetchAssetFiles, fetchDirectoryContents };
|
|
107
|
+
export { NotFoundError, RateLimitError, downloadAssetFiles, downloadFile, expandDirectoryEntries, fetchAssetFiles, fetchDirectoryContents };
|
package/dist/core/migration.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var fs = require('node:fs');
|
|
4
4
|
var path = require('node:path');
|
|
5
5
|
var nameTransform = require('../utils/nameTransform.cjs');
|
|
6
|
+
var packageName = require('../utils/packageName.cjs');
|
|
6
7
|
var filesystem = require('./filesystem.cjs');
|
|
7
8
|
var syncMeta = require('./syncMeta.cjs');
|
|
8
9
|
|
|
@@ -62,8 +63,8 @@ async function migrateToFlat(cwd, options = {}) {
|
|
|
62
63
|
});
|
|
63
64
|
for (const packageDir of packageDirs) {
|
|
64
65
|
const packagePath = path.join(scopePath, packageDir);
|
|
65
|
-
const packageName = `${scopeDir}/${packageDir}`;
|
|
66
|
-
console.log(` 📦 Processing ${packageName}...`);
|
|
66
|
+
const packageName$1 = `${scopeDir}/${packageDir}`;
|
|
67
|
+
console.log(` 📦 Processing ${packageName$1}...`);
|
|
67
68
|
try {
|
|
68
69
|
const metaPath = path.join(packagePath, '.sync-meta.json');
|
|
69
70
|
let legacyMeta = null;
|
|
@@ -75,7 +76,7 @@ async function migrateToFlat(cwd, options = {}) {
|
|
|
75
76
|
console.log(` ⚠️ No .sync-meta.json found, skipping`);
|
|
76
77
|
continue;
|
|
77
78
|
}
|
|
78
|
-
const prefix =
|
|
79
|
+
const prefix = packageName.packageNameToPrefix(packageName$1);
|
|
79
80
|
const commandFiles = [];
|
|
80
81
|
const fileMappings = [];
|
|
81
82
|
for (const fileName of legacyMeta.files) {
|
|
@@ -112,7 +113,7 @@ async function migrateToFlat(cwd, options = {}) {
|
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
const packageInfo = {
|
|
115
|
-
originalName: packageName,
|
|
116
|
+
originalName: packageName$1,
|
|
116
117
|
version: legacyMeta.version,
|
|
117
118
|
files: {
|
|
118
119
|
commands: assetType === 'commands' ? commandFiles : [],
|
|
@@ -136,11 +137,11 @@ async function migrateToFlat(cwd, options = {}) {
|
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
unifiedMeta = syncMeta.updatePackageInMeta(unifiedMeta, prefix, packageInfo);
|
|
139
|
-
migratedPackages.push(packageName);
|
|
140
|
+
migratedPackages.push(packageName$1);
|
|
140
141
|
legacyDirs.push(packagePath);
|
|
141
142
|
}
|
|
142
143
|
catch (error) {
|
|
143
|
-
const errorMsg = `Failed to migrate ${packageName}: ${error}`;
|
|
144
|
+
const errorMsg = `Failed to migrate ${packageName$1}: ${error}`;
|
|
144
145
|
console.error(` ❌ ${errorMsg}`);
|
|
145
146
|
errors.push(errorMsg);
|
|
146
147
|
}
|
package/dist/core/migration.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, statSync, readFileSync, rmSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { toFlatFileName } from '../utils/nameTransform.mjs';
|
|
4
|
+
import { packageNameToPrefix } from '../utils/packageName.mjs';
|
|
4
5
|
import { writeFlatAssetFile } from './filesystem.mjs';
|
|
5
6
|
import { readUnifiedSyncMeta, createEmptyUnifiedMeta, updatePackageInMeta, writeUnifiedSyncMeta } from './syncMeta.mjs';
|
|
6
7
|
|
|
@@ -72,15 +72,25 @@ async function scanRemoteAssets(packageName, ref) {
|
|
|
72
72
|
throw new Error(`Invalid GitHub repository URL in package ${packageName}`);
|
|
73
73
|
}
|
|
74
74
|
const assetBasePath = pkgInfo.claude.assetPath;
|
|
75
|
+
const tag = ref ?? 'HEAD';
|
|
75
76
|
const trees = [];
|
|
76
77
|
for (const assetType of constants.DEFAULT_ASSET_TYPES) {
|
|
77
78
|
const assetPath = repoInfo.directory
|
|
78
79
|
? `${repoInfo.directory}/${assetBasePath}/${assetType}`
|
|
79
80
|
: `${assetBasePath}/${assetType}`;
|
|
80
81
|
try {
|
|
81
|
-
const entries = await github.fetchDirectoryContents(repoInfo, assetPath,
|
|
82
|
+
const entries = await github.fetchDirectoryContents(repoInfo, assetPath, tag);
|
|
82
83
|
if (entries && entries.length > 0) {
|
|
83
|
-
const
|
|
84
|
+
const dirContentsMap = new Map();
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry.type === 'dir') {
|
|
87
|
+
const dirEntries = await github.fetchDirectoryContents(repoInfo, `${assetPath}/${entry.name}`, tag);
|
|
88
|
+
if (dirEntries) {
|
|
89
|
+
dirContentsMap.set(entry.name, dirEntries);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const tree = buildTreeFromGitHubEntries(assetType, entries, assetType, dirContentsMap);
|
|
84
94
|
if (tree.children && tree.children.length > 0) {
|
|
85
95
|
trees.push(tree);
|
|
86
96
|
}
|
|
@@ -100,7 +110,8 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
|
|
|
100
110
|
const stat = fs.statSync(fullPath);
|
|
101
111
|
const relativePath = path.join(basePath, entry);
|
|
102
112
|
if (stat.isDirectory()) {
|
|
103
|
-
const isSkill = fs.existsSync(path.join(fullPath, '
|
|
113
|
+
const isSkill = fs.existsSync(path.join(fullPath, 'SKILL.md')) ||
|
|
114
|
+
fs.existsSync(path.join(fullPath, 'Skill.md'));
|
|
104
115
|
if (isSkill) {
|
|
105
116
|
children.push({
|
|
106
117
|
id: relativePath,
|
|
@@ -139,36 +150,37 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
|
|
|
139
150
|
expanded: true,
|
|
140
151
|
};
|
|
141
152
|
}
|
|
142
|
-
function buildTreeFromGitHubEntries(label, entries, basePath) {
|
|
153
|
+
function buildTreeFromGitHubEntries(label, entries, basePath, dirContentsMap) {
|
|
143
154
|
const children = [];
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
if (items.length === 1 && items[0].type === 'file') {
|
|
147
|
-
const filePath = items[0].path;
|
|
155
|
+
for (const entry of entries) {
|
|
156
|
+
if (entry.type === 'file') {
|
|
148
157
|
children.push({
|
|
149
|
-
id:
|
|
150
|
-
label: name,
|
|
151
|
-
path:
|
|
158
|
+
id: entry.path,
|
|
159
|
+
label: entry.name,
|
|
160
|
+
path: entry.path,
|
|
152
161
|
type: 'file',
|
|
153
162
|
selected: true,
|
|
154
163
|
expanded: false,
|
|
155
164
|
});
|
|
156
165
|
}
|
|
157
|
-
else {
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
else if (entry.type === 'dir') {
|
|
167
|
+
const dirEntries = dirContentsMap?.get(entry.name);
|
|
168
|
+
const hasSkillMd = dirEntries
|
|
169
|
+
? isDirectorySkill(dirEntries)
|
|
170
|
+
: false;
|
|
171
|
+
if (hasSkillMd) {
|
|
172
|
+
const skillPath = `${basePath}/${entry.name}`;
|
|
161
173
|
children.push({
|
|
162
174
|
id: skillPath,
|
|
163
|
-
label: name,
|
|
175
|
+
label: entry.name,
|
|
164
176
|
path: skillPath,
|
|
165
177
|
type: 'skill-directory',
|
|
166
178
|
selected: true,
|
|
167
179
|
expanded: false,
|
|
168
180
|
});
|
|
169
181
|
}
|
|
170
|
-
else {
|
|
171
|
-
const subTree = buildTreeFromGitHubEntries(name,
|
|
182
|
+
else if (dirEntries && dirEntries.length > 0) {
|
|
183
|
+
const subTree = buildTreeFromGitHubEntries(entry.name, dirEntries, `${basePath}/${entry.name}`);
|
|
172
184
|
if (subTree.children && subTree.children.length > 0) {
|
|
173
185
|
children.push(subTree);
|
|
174
186
|
}
|
|
@@ -185,17 +197,9 @@ function buildTreeFromGitHubEntries(label, entries, basePath) {
|
|
|
185
197
|
expanded: true,
|
|
186
198
|
};
|
|
187
199
|
}
|
|
188
|
-
function
|
|
189
|
-
|
|
190
|
-
|
|
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;
|
|
200
|
+
function isDirectorySkill(entries) {
|
|
201
|
+
return entries.some((entry) => entry.type === 'file' &&
|
|
202
|
+
(entry.name === 'SKILL.md' || entry.name === 'Skill.md'));
|
|
199
203
|
}
|
|
200
204
|
function findLocalPackage(packageName, cwd) {
|
|
201
205
|
const monorepoRoot = findMonorepoRoot(cwd);
|
|
@@ -256,4 +260,5 @@ function searchPackagesRecursively(dir, packageName) {
|
|
|
256
260
|
return null;
|
|
257
261
|
}
|
|
258
262
|
|
|
263
|
+
exports.isDirectorySkill = isDirectorySkill;
|
|
259
264
|
exports.scanPackageAssets = scanPackageAssets;
|
|
@@ -70,15 +70,25 @@ async function scanRemoteAssets(packageName, ref) {
|
|
|
70
70
|
throw new Error(`Invalid GitHub repository URL in package ${packageName}`);
|
|
71
71
|
}
|
|
72
72
|
const assetBasePath = pkgInfo.claude.assetPath;
|
|
73
|
+
const tag = ref ?? 'HEAD';
|
|
73
74
|
const trees = [];
|
|
74
75
|
for (const assetType of DEFAULT_ASSET_TYPES) {
|
|
75
76
|
const assetPath = repoInfo.directory
|
|
76
77
|
? `${repoInfo.directory}/${assetBasePath}/${assetType}`
|
|
77
78
|
: `${assetBasePath}/${assetType}`;
|
|
78
79
|
try {
|
|
79
|
-
const entries = await fetchDirectoryContents(repoInfo, assetPath,
|
|
80
|
+
const entries = await fetchDirectoryContents(repoInfo, assetPath, tag);
|
|
80
81
|
if (entries && entries.length > 0) {
|
|
81
|
-
const
|
|
82
|
+
const dirContentsMap = new Map();
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
if (entry.type === 'dir') {
|
|
85
|
+
const dirEntries = await fetchDirectoryContents(repoInfo, `${assetPath}/${entry.name}`, tag);
|
|
86
|
+
if (dirEntries) {
|
|
87
|
+
dirContentsMap.set(entry.name, dirEntries);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const tree = buildTreeFromGitHubEntries(assetType, entries, assetType, dirContentsMap);
|
|
82
92
|
if (tree.children && tree.children.length > 0) {
|
|
83
93
|
trees.push(tree);
|
|
84
94
|
}
|
|
@@ -98,7 +108,8 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
|
|
|
98
108
|
const stat = statSync(fullPath);
|
|
99
109
|
const relativePath = join(basePath, entry);
|
|
100
110
|
if (stat.isDirectory()) {
|
|
101
|
-
const isSkill = existsSync(join(fullPath, '
|
|
111
|
+
const isSkill = existsSync(join(fullPath, 'SKILL.md')) ||
|
|
112
|
+
existsSync(join(fullPath, 'Skill.md'));
|
|
102
113
|
if (isSkill) {
|
|
103
114
|
children.push({
|
|
104
115
|
id: relativePath,
|
|
@@ -137,36 +148,37 @@ function buildTreeFromLocalDir(label, dirPath, basePath) {
|
|
|
137
148
|
expanded: true,
|
|
138
149
|
};
|
|
139
150
|
}
|
|
140
|
-
function buildTreeFromGitHubEntries(label, entries, basePath) {
|
|
151
|
+
function buildTreeFromGitHubEntries(label, entries, basePath, dirContentsMap) {
|
|
141
152
|
const children = [];
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
if (items.length === 1 && items[0].type === 'file') {
|
|
145
|
-
const filePath = items[0].path;
|
|
153
|
+
for (const entry of entries) {
|
|
154
|
+
if (entry.type === 'file') {
|
|
146
155
|
children.push({
|
|
147
|
-
id:
|
|
148
|
-
label: name,
|
|
149
|
-
path:
|
|
156
|
+
id: entry.path,
|
|
157
|
+
label: entry.name,
|
|
158
|
+
path: entry.path,
|
|
150
159
|
type: 'file',
|
|
151
160
|
selected: true,
|
|
152
161
|
expanded: false,
|
|
153
162
|
});
|
|
154
163
|
}
|
|
155
|
-
else {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
else if (entry.type === 'dir') {
|
|
165
|
+
const dirEntries = dirContentsMap?.get(entry.name);
|
|
166
|
+
const hasSkillMd = dirEntries
|
|
167
|
+
? isDirectorySkill(dirEntries)
|
|
168
|
+
: false;
|
|
169
|
+
if (hasSkillMd) {
|
|
170
|
+
const skillPath = `${basePath}/${entry.name}`;
|
|
159
171
|
children.push({
|
|
160
172
|
id: skillPath,
|
|
161
|
-
label: name,
|
|
173
|
+
label: entry.name,
|
|
162
174
|
path: skillPath,
|
|
163
175
|
type: 'skill-directory',
|
|
164
176
|
selected: true,
|
|
165
177
|
expanded: false,
|
|
166
178
|
});
|
|
167
179
|
}
|
|
168
|
-
else {
|
|
169
|
-
const subTree = buildTreeFromGitHubEntries(name,
|
|
180
|
+
else if (dirEntries && dirEntries.length > 0) {
|
|
181
|
+
const subTree = buildTreeFromGitHubEntries(entry.name, dirEntries, `${basePath}/${entry.name}`);
|
|
170
182
|
if (subTree.children && subTree.children.length > 0) {
|
|
171
183
|
children.push(subTree);
|
|
172
184
|
}
|
|
@@ -183,17 +195,9 @@ function buildTreeFromGitHubEntries(label, entries, basePath) {
|
|
|
183
195
|
expanded: true,
|
|
184
196
|
};
|
|
185
197
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
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;
|
|
198
|
+
function isDirectorySkill(entries) {
|
|
199
|
+
return entries.some((entry) => entry.type === 'file' &&
|
|
200
|
+
(entry.name === 'SKILL.md' || entry.name === 'Skill.md'));
|
|
197
201
|
}
|
|
198
202
|
function findLocalPackage(packageName, cwd) {
|
|
199
203
|
const monorepoRoot = findMonorepoRoot(cwd);
|
|
@@ -254,4 +258,4 @@ function searchPackagesRecursively(dir, packageName) {
|
|
|
254
258
|
return null;
|
|
255
259
|
}
|
|
256
260
|
|
|
257
|
-
export { scanPackageAssets };
|
|
261
|
+
export { isDirectorySkill, scanPackageAssets };
|