@slats/claude-assets-sync 0.0.4 → 0.0.6

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 (46) 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 +6 -6
  5. package/dist/commands/remove.mjs +6 -6
  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/cli.cjs +18 -0
  23. package/dist/core/cli.mjs +18 -0
  24. package/dist/core/constants.cjs +4 -1
  25. package/dist/core/constants.d.ts +8 -1
  26. package/dist/core/constants.mjs +4 -1
  27. package/dist/core/filesystem.cjs +7 -13
  28. package/dist/core/filesystem.mjs +7 -13
  29. package/dist/core/localSource.cjs +112 -0
  30. package/dist/core/localSource.d.ts +33 -0
  31. package/dist/core/localSource.mjs +106 -0
  32. package/dist/core/migration.cjs +10 -9
  33. package/dist/core/migration.mjs +10 -9
  34. package/dist/core/packageScanner.cjs +94 -1
  35. package/dist/core/packageScanner.d.ts +6 -1
  36. package/dist/core/packageScanner.mjs +94 -2
  37. package/dist/core/sync.cjs +118 -46
  38. package/dist/core/sync.mjs +119 -47
  39. package/dist/core/syncMeta.cjs +153 -9
  40. package/dist/core/syncMeta.d.ts +22 -18
  41. package/dist/core/syncMeta.mjs +149 -9
  42. package/dist/utils/types.d.ts +24 -7
  43. package/dist/version.cjs +1 -1
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.mjs +1 -1
  46. package/package.json +1 -1
@@ -10,12 +10,14 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
10
10
  const [treeData, setTreeData] = React.useState(trees);
11
11
  const flattenTree = (nodes, depth = 0) => {
12
12
  const result = [];
13
- const traverse = (items, currentDepth, parentPath = []) => {
13
+ const traverse = (items, currentDepth, parentPath = [], ancestorIsLast = []) => {
14
14
  items.forEach((item, index) => {
15
+ const isLast = index === items.length - 1;
16
+ const currentIsLast = [...ancestorIsLast, isLast];
15
17
  const currentPath = [...parentPath, index];
16
- result.push({ node: item, depth: currentDepth, path: currentPath });
18
+ result.push({ node: item, depth: currentDepth, path: currentPath, isLastAtDepth: currentIsLast });
17
19
  if (item.expanded && item.children) {
18
- traverse(item.children, currentDepth + 1, currentPath);
20
+ traverse(item.children, currentDepth + 1, currentPath, currentIsLast);
19
21
  }
20
22
  });
21
23
  };
@@ -32,7 +34,9 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
32
34
  }
33
35
  else if (key.rightArrow) {
34
36
  const current = flatItems[selectedIndex];
35
- if (current.node.type === 'directory' && !current.node.expanded) {
37
+ if ((current.node.type === 'directory' || current.node.type === 'skill-directory') &&
38
+ !current.node.expanded &&
39
+ current.node.children) {
36
40
  const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
37
41
  ...node,
38
42
  expanded: true,
@@ -42,7 +46,8 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
42
46
  }
43
47
  else if (key.leftArrow) {
44
48
  const current = flatItems[selectedIndex];
45
- if (current.node.type === 'directory' && current.node.expanded) {
49
+ if ((current.node.type === 'directory' || current.node.type === 'skill-directory') &&
50
+ current.node.expanded) {
46
51
  const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
47
52
  ...node,
48
53
  expanded: false,
@@ -52,6 +57,9 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
52
57
  }
53
58
  else if (input === ' ') {
54
59
  const current = flatItems[selectedIndex];
60
+ if (current.node.disabled) {
61
+ return;
62
+ }
55
63
  const newTrees = toggleNodeSelection(treeData, current.path);
56
64
  setTreeData(newTrees);
57
65
  }
@@ -65,7 +73,7 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
65
73
  onCancel();
66
74
  }
67
75
  });
68
- return (jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: flatItems.map((item, index) => (jsxRuntime.jsx(AssetTreeNode.AssetTreeNode, { node: item.node, depth: item.depth, isSelected: index === selectedIndex, onToggle: () => {
76
+ return (jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: flatItems.map((item, index) => (jsxRuntime.jsx(AssetTreeNode.AssetTreeNode, { node: item.node, isLastAtDepth: item.isLastAtDepth, isSelected: index === selectedIndex, onToggle: () => {
69
77
  const newTrees = toggleNodeSelection(treeData, item.path);
70
78
  setTreeData(newTrees);
71
79
  } }, item.path.join('-')))) }));
@@ -8,12 +8,14 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
8
8
  const [treeData, setTreeData] = useState(trees);
9
9
  const flattenTree = (nodes, depth = 0) => {
10
10
  const result = [];
11
- const traverse = (items, currentDepth, parentPath = []) => {
11
+ const traverse = (items, currentDepth, parentPath = [], ancestorIsLast = []) => {
12
12
  items.forEach((item, index) => {
13
+ const isLast = index === items.length - 1;
14
+ const currentIsLast = [...ancestorIsLast, isLast];
13
15
  const currentPath = [...parentPath, index];
14
- result.push({ node: item, depth: currentDepth, path: currentPath });
16
+ result.push({ node: item, depth: currentDepth, path: currentPath, isLastAtDepth: currentIsLast });
15
17
  if (item.expanded && item.children) {
16
- traverse(item.children, currentDepth + 1, currentPath);
18
+ traverse(item.children, currentDepth + 1, currentPath, currentIsLast);
17
19
  }
18
20
  });
19
21
  };
@@ -30,7 +32,9 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
30
32
  }
31
33
  else if (key.rightArrow) {
32
34
  const current = flatItems[selectedIndex];
33
- if (current.node.type === 'directory' && !current.node.expanded) {
35
+ if ((current.node.type === 'directory' || current.node.type === 'skill-directory') &&
36
+ !current.node.expanded &&
37
+ current.node.children) {
34
38
  const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
35
39
  ...node,
36
40
  expanded: true,
@@ -40,7 +44,8 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
40
44
  }
41
45
  else if (key.leftArrow) {
42
46
  const current = flatItems[selectedIndex];
43
- if (current.node.type === 'directory' && current.node.expanded) {
47
+ if ((current.node.type === 'directory' || current.node.type === 'skill-directory') &&
48
+ current.node.expanded) {
44
49
  const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
45
50
  ...node,
46
51
  expanded: false,
@@ -50,6 +55,9 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
50
55
  }
51
56
  else if (input === ' ') {
52
57
  const current = flatItems[selectedIndex];
58
+ if (current.node.disabled) {
59
+ return;
60
+ }
53
61
  const newTrees = toggleNodeSelection(treeData, current.path);
54
62
  setTreeData(newTrees);
55
63
  }
@@ -63,7 +71,7 @@ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
63
71
  onCancel();
64
72
  }
65
73
  });
66
- return (jsx(Box, { flexDirection: "column", children: flatItems.map((item, index) => (jsx(AssetTreeNode, { node: item.node, depth: item.depth, isSelected: index === selectedIndex, onToggle: () => {
74
+ return (jsx(Box, { flexDirection: "column", children: flatItems.map((item, index) => (jsx(AssetTreeNode, { node: item.node, isLastAtDepth: item.isLastAtDepth, isSelected: index === selectedIndex, onToggle: () => {
67
75
  const newTrees = toggleNodeSelection(treeData, item.path);
68
76
  setTreeData(newTrees);
69
77
  } }, item.path.join('-')))) }));
package/dist/core/cli.cjs CHANGED
@@ -7,6 +7,7 @@ var remove = require('../commands/remove.cjs');
7
7
  var status = require('../commands/status.cjs');
8
8
  var migrate = require('../commands/migrate.cjs');
9
9
  var add = require('../commands/add.cjs');
10
+ var update = require('../commands/update.cjs');
10
11
  var version = require('../version.cjs');
11
12
 
12
13
  const createProgram = () => {
@@ -81,6 +82,23 @@ const createProgram = () => {
81
82
  .action(async (opts) => {
82
83
  await migrate.runMigrateCommand({ dryRun: opts.dryRun });
83
84
  });
85
+ program
86
+ .command('update')
87
+ .description('Update package metadata in .sync-meta.json')
88
+ .option('-p, --package <name>', 'Package name to update (default: all)')
89
+ .option('-l, --local', 'Read packages from local workspace instead of node_modules', false)
90
+ .option('-r, --ref <ref>', 'Git ref (branch, tag, or commit) to fetch from')
91
+ .option('--dry-run', 'Preview changes without writing files', false)
92
+ .option('--sync', 'Re-sync files after updating metadata', false)
93
+ .action(async (opts) => {
94
+ await update.runUpdateCommand({
95
+ package: opts.package,
96
+ local: opts.local,
97
+ ref: opts.ref,
98
+ dryRun: opts.dryRun,
99
+ sync: opts.sync,
100
+ });
101
+ });
84
102
  return program;
85
103
  };
86
104
  const run = async () => {
package/dist/core/cli.mjs CHANGED
@@ -5,6 +5,7 @@ import { runRemoveCommand } from '../commands/remove.mjs';
5
5
  import { runStatusCommand } from '../commands/status.mjs';
6
6
  import { runMigrateCommand } from '../commands/migrate.mjs';
7
7
  import { runAddCommand } from '../commands/add.mjs';
8
+ import { runUpdateCommand } from '../commands/update.mjs';
8
9
  import { VERSION } from '../version.mjs';
9
10
 
10
11
  const createProgram = () => {
@@ -79,6 +80,23 @@ const createProgram = () => {
79
80
  .action(async (opts) => {
80
81
  await runMigrateCommand({ dryRun: opts.dryRun });
81
82
  });
83
+ program
84
+ .command('update')
85
+ .description('Update package metadata in .sync-meta.json')
86
+ .option('-p, --package <name>', 'Package name to update (default: all)')
87
+ .option('-l, --local', 'Read packages from local workspace instead of node_modules', false)
88
+ .option('-r, --ref <ref>', 'Git ref (branch, tag, or commit) to fetch from')
89
+ .option('--dry-run', 'Preview changes without writing files', false)
90
+ .option('--sync', 'Re-sync files after updating metadata', false)
91
+ .action(async (opts) => {
92
+ await runUpdateCommand({
93
+ package: opts.package,
94
+ local: opts.local,
95
+ ref: opts.ref,
96
+ dryRun: opts.dryRun,
97
+ sync: opts.sync,
98
+ });
99
+ });
82
100
  return program;
83
101
  };
84
102
  const run = async () => {
@@ -8,7 +8,10 @@ const META_FILES = {
8
8
  UNIFIED_SYNC_META: '.claude/.sync-meta.json',
9
9
  };
10
10
  const SCHEMA_VERSIONS = {
11
- UNIFIED_SYNC_META: version.VERSION};
11
+ UNIFIED_SYNC_META: version.VERSION,
12
+ LEGACY_SYNC_META: '1.0.0',
13
+ SKILL_UNIT_FORMAT: '2',
14
+ };
12
15
  const DEFAULT_ASSET_TYPES = ['commands', 'skills', 'agents'];
13
16
  const FS_PATTERNS = {
14
17
  GITHUB_HTTPS_URL: /https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/,
@@ -22,9 +22,16 @@ export declare const META_FILES: {
22
22
  * Schema versions for metadata files
23
23
  */
24
24
  export declare const SCHEMA_VERSIONS: {
25
- readonly UNIFIED_SYNC_META: "0.0.4";
25
+ readonly UNIFIED_SYNC_META: "0.0.6";
26
26
  readonly LEGACY_SYNC_META: "1.0.0";
27
+ readonly SKILL_UNIT_FORMAT: "2";
27
28
  };
29
+ /**
30
+ * Schema version for SkillUnit-based metadata format.
31
+ * Separate from package VERSION to allow independent format evolution.
32
+ * Used to detect whether migration from old format is needed.
33
+ */
34
+ export declare const SKILL_UNIT_SCHEMA_VERSION: "2";
28
35
  /**
29
36
  * Default asset types (exported for backward compatibility)
30
37
  */
@@ -6,7 +6,10 @@ const META_FILES = {
6
6
  UNIFIED_SYNC_META: '.claude/.sync-meta.json',
7
7
  };
8
8
  const SCHEMA_VERSIONS = {
9
- UNIFIED_SYNC_META: VERSION};
9
+ UNIFIED_SYNC_META: VERSION,
10
+ LEGACY_SYNC_META: '1.0.0',
11
+ SKILL_UNIT_FORMAT: '2',
12
+ };
10
13
  const DEFAULT_ASSET_TYPES = ['commands', 'skills', 'agents'];
11
14
  const FS_PATTERNS = {
12
15
  GITHUB_HTTPS_URL: /https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/,
@@ -58,13 +58,13 @@ 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();
62
- for (const fileMapping of filesToRemove) {
63
- const fileName = typeof fileMapping === 'string'
64
- ? fileMapping
65
- : fileMapping.transformed;
66
- if (fileName.includes('/')) {
67
- skillDirs.add(fileName.split('/')[0]);
61
+ for (const unit of filesToRemove) {
62
+ const fileName = unit.transformed ?? unit.name;
63
+ if (unit.isDirectory) {
64
+ const dirPath = path.join(destDir, fileName);
65
+ if (io.fileExists(dirPath)) {
66
+ fs.rmSync(dirPath, { recursive: true, force: true });
67
+ }
68
68
  }
69
69
  else {
70
70
  const filePath = path.join(destDir, fileName);
@@ -73,12 +73,6 @@ const cleanFlatAssetFiles = (cwd, assetType, prefix, existingMeta) => {
73
73
  }
74
74
  }
75
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 });
80
- }
81
- }
82
76
  }
83
77
  }
84
78
  else {
@@ -56,13 +56,13 @@ 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();
60
- for (const fileMapping of filesToRemove) {
61
- const fileName = typeof fileMapping === 'string'
62
- ? fileMapping
63
- : fileMapping.transformed;
64
- if (fileName.includes('/')) {
65
- skillDirs.add(fileName.split('/')[0]);
59
+ for (const unit of filesToRemove) {
60
+ const fileName = unit.transformed ?? unit.name;
61
+ if (unit.isDirectory) {
62
+ const dirPath = join(destDir, fileName);
63
+ if (fileExists(dirPath)) {
64
+ rmSync(dirPath, { recursive: true, force: true });
65
+ }
66
66
  }
67
67
  else {
68
68
  const filePath = join(destDir, fileName);
@@ -71,12 +71,6 @@ const cleanFlatAssetFiles = (cwd, assetType, prefix, existingMeta) => {
71
71
  }
72
72
  }
73
73
  }
74
- for (const dir of skillDirs) {
75
- const dirPath = join(destDir, dir);
76
- if (fileExists(dirPath)) {
77
- rmSync(dirPath, { recursive: true, force: true });
78
- }
79
- }
80
74
  }
81
75
  }
82
76
  else {
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ var fs = require('node:fs');
4
+ var path = require('node:path');
5
+
6
+ const canUseLocalSource = (packageName, requestedVersion, assetPath, cwd) => {
7
+ const docsPath = path.join(cwd, 'node_modules', packageName, assetPath);
8
+ if (!fs.existsSync(docsPath)) {
9
+ return { available: false, reason: `Local docs path not found: ${docsPath}` };
10
+ }
11
+ try {
12
+ const pkgJsonPath = path.join(cwd, 'node_modules', packageName, 'package.json');
13
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
14
+ if (pkgJson.version !== requestedVersion) {
15
+ return {
16
+ available: false,
17
+ reason: `Version mismatch: installed=${pkgJson.version}, requested=${requestedVersion}`,
18
+ };
19
+ }
20
+ }
21
+ catch {
22
+ return { available: false, reason: 'Failed to read package.json from node_modules' };
23
+ }
24
+ return { available: true, docsPath };
25
+ };
26
+ const fetchLocalDirectoryContents = (dirPath) => {
27
+ if (!fs.existsSync(dirPath))
28
+ return null;
29
+ try {
30
+ const entries = fs.readdirSync(dirPath);
31
+ const result = [];
32
+ for (const name of entries) {
33
+ const fullPath = path.join(dirPath, name);
34
+ const stat = fs.statSync(fullPath);
35
+ if (stat.isDirectory()) {
36
+ result.push({
37
+ name,
38
+ path: fullPath,
39
+ type: 'dir',
40
+ download_url: null,
41
+ sha: '',
42
+ });
43
+ }
44
+ else if (stat.isFile() && name.endsWith('.md')) {
45
+ result.push({
46
+ name,
47
+ path: fullPath,
48
+ type: 'file',
49
+ download_url: null,
50
+ sha: '',
51
+ });
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ };
60
+ const expandLocalDirectoryEntries = (dirPath, entries, prefix = '') => {
61
+ const result = [];
62
+ for (const entry of entries) {
63
+ const entryPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
64
+ if (entry.type === 'file') {
65
+ result.push({
66
+ ...entry,
67
+ name: prefix ? entryPrefix : entry.name,
68
+ });
69
+ }
70
+ else if (entry.type === 'dir') {
71
+ const subDirPath = path.join(dirPath, entry.name);
72
+ const subEntries = fetchLocalDirectoryContents(subDirPath);
73
+ if (subEntries) {
74
+ const expanded = expandLocalDirectoryEntries(subDirPath, subEntries, entryPrefix);
75
+ result.push(...expanded);
76
+ }
77
+ }
78
+ }
79
+ return result;
80
+ };
81
+ const fetchLocalAssetFiles = async (docsBasePath, assetTypes) => {
82
+ const assetFiles = {};
83
+ for (const assetType of assetTypes) {
84
+ const assetDirPath = path.join(docsBasePath, assetType);
85
+ const entries = fetchLocalDirectoryContents(assetDirPath);
86
+ if (!entries) {
87
+ assetFiles[assetType] = [];
88
+ continue;
89
+ }
90
+ assetFiles[assetType] = expandLocalDirectoryEntries(assetDirPath, entries);
91
+ }
92
+ return assetFiles;
93
+ };
94
+ const downloadLocalAssetFiles = async (docsBasePath, assetType, entries) => {
95
+ const results = new Map();
96
+ for (const entry of entries) {
97
+ const filePath = path.join(docsBasePath, assetType, entry.name);
98
+ try {
99
+ const content = fs.readFileSync(filePath, 'utf-8');
100
+ results.set(entry.name, content);
101
+ }
102
+ catch {
103
+ }
104
+ }
105
+ return results;
106
+ };
107
+
108
+ exports.canUseLocalSource = canUseLocalSource;
109
+ exports.downloadLocalAssetFiles = downloadLocalAssetFiles;
110
+ exports.expandLocalDirectoryEntries = expandLocalDirectoryEntries;
111
+ exports.fetchLocalAssetFiles = fetchLocalAssetFiles;
112
+ exports.fetchLocalDirectoryContents = fetchLocalDirectoryContents;
@@ -0,0 +1,33 @@
1
+ import type { AssetType, GitHubEntry } from '../utils/types.js';
2
+ export interface LocalSourceResult {
3
+ available: boolean;
4
+ docsPath?: string;
5
+ reason?: string;
6
+ }
7
+ /**
8
+ * Check if local docs source is available in node_modules.
9
+ * Returns available=true if:
10
+ * 1. node_modules/<packageName>/<assetPath> directory exists
11
+ * 2. Installed version matches requestedVersion
12
+ */
13
+ export declare const canUseLocalSource: (packageName: string, requestedVersion: string, assetPath: string, cwd: string) => LocalSourceResult;
14
+ /**
15
+ * Read directory contents and return .md files and subdirs in GitHubEntry format.
16
+ * Returns null if the directory doesn't exist.
17
+ */
18
+ export declare const fetchLocalDirectoryContents: (dirPath: string) => GitHubEntry[] | null;
19
+ /**
20
+ * Recursively expand directory entries into flat file entries with path prefixes.
21
+ * Mirrors github.ts expandDirectoryEntries behaviour but reads from local filesystem.
22
+ */
23
+ export declare const expandLocalDirectoryEntries: (dirPath: string, entries: GitHubEntry[], prefix?: string) => GitHubEntry[];
24
+ /**
25
+ * Fetch asset files from local filesystem.
26
+ * Mirrors github.ts fetchAssetFiles but reads from node_modules instead of GitHub API.
27
+ */
28
+ export declare const fetchLocalAssetFiles: (docsBasePath: string, assetTypes: string[]) => Promise<Record<string, GitHubEntry[]>>;
29
+ /**
30
+ * Read file contents from local filesystem for the given entries.
31
+ * Mirrors github.ts downloadAssetFiles but reads local files instead of HTTP requests.
32
+ */
33
+ export declare const downloadLocalAssetFiles: (docsBasePath: string, assetType: AssetType, entries: GitHubEntry[]) => Promise<Map<string, string>>;
@@ -0,0 +1,106 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ const canUseLocalSource = (packageName, requestedVersion, assetPath, cwd) => {
5
+ const docsPath = join(cwd, 'node_modules', packageName, assetPath);
6
+ if (!existsSync(docsPath)) {
7
+ return { available: false, reason: `Local docs path not found: ${docsPath}` };
8
+ }
9
+ try {
10
+ const pkgJsonPath = join(cwd, 'node_modules', packageName, 'package.json');
11
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
12
+ if (pkgJson.version !== requestedVersion) {
13
+ return {
14
+ available: false,
15
+ reason: `Version mismatch: installed=${pkgJson.version}, requested=${requestedVersion}`,
16
+ };
17
+ }
18
+ }
19
+ catch {
20
+ return { available: false, reason: 'Failed to read package.json from node_modules' };
21
+ }
22
+ return { available: true, docsPath };
23
+ };
24
+ const fetchLocalDirectoryContents = (dirPath) => {
25
+ if (!existsSync(dirPath))
26
+ return null;
27
+ try {
28
+ const entries = readdirSync(dirPath);
29
+ const result = [];
30
+ for (const name of entries) {
31
+ const fullPath = join(dirPath, name);
32
+ const stat = statSync(fullPath);
33
+ if (stat.isDirectory()) {
34
+ result.push({
35
+ name,
36
+ path: fullPath,
37
+ type: 'dir',
38
+ download_url: null,
39
+ sha: '',
40
+ });
41
+ }
42
+ else if (stat.isFile() && name.endsWith('.md')) {
43
+ result.push({
44
+ name,
45
+ path: fullPath,
46
+ type: 'file',
47
+ download_url: null,
48
+ sha: '',
49
+ });
50
+ }
51
+ }
52
+ return result;
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ };
58
+ const expandLocalDirectoryEntries = (dirPath, entries, prefix = '') => {
59
+ const result = [];
60
+ for (const entry of entries) {
61
+ const entryPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
62
+ if (entry.type === 'file') {
63
+ result.push({
64
+ ...entry,
65
+ name: prefix ? entryPrefix : entry.name,
66
+ });
67
+ }
68
+ else if (entry.type === 'dir') {
69
+ const subDirPath = join(dirPath, entry.name);
70
+ const subEntries = fetchLocalDirectoryContents(subDirPath);
71
+ if (subEntries) {
72
+ const expanded = expandLocalDirectoryEntries(subDirPath, subEntries, entryPrefix);
73
+ result.push(...expanded);
74
+ }
75
+ }
76
+ }
77
+ return result;
78
+ };
79
+ const fetchLocalAssetFiles = async (docsBasePath, assetTypes) => {
80
+ const assetFiles = {};
81
+ for (const assetType of assetTypes) {
82
+ const assetDirPath = join(docsBasePath, assetType);
83
+ const entries = fetchLocalDirectoryContents(assetDirPath);
84
+ if (!entries) {
85
+ assetFiles[assetType] = [];
86
+ continue;
87
+ }
88
+ assetFiles[assetType] = expandLocalDirectoryEntries(assetDirPath, entries);
89
+ }
90
+ return assetFiles;
91
+ };
92
+ const downloadLocalAssetFiles = async (docsBasePath, assetType, entries) => {
93
+ const results = new Map();
94
+ for (const entry of entries) {
95
+ const filePath = join(docsBasePath, assetType, entry.name);
96
+ try {
97
+ const content = readFileSync(filePath, 'utf-8');
98
+ results.set(entry.name, content);
99
+ }
100
+ catch {
101
+ }
102
+ }
103
+ return results;
104
+ };
105
+
106
+ export { canUseLocalSource, downloadLocalAssetFiles, expandLocalDirectoryEntries, fetchLocalAssetFiles, fetchLocalDirectoryContents };
@@ -77,8 +77,8 @@ async function migrateToFlat(cwd, options = {}) {
77
77
  continue;
78
78
  }
79
79
  const prefix = packageName.packageNameToPrefix(packageName$1);
80
- const commandFiles = [];
81
- const fileMappings = [];
80
+ const commandUnits = [];
81
+ const skillUnits = [];
82
82
  for (const fileName of legacyMeta.files) {
83
83
  const sourcePath = path.join(packagePath, fileName);
84
84
  if (!fs.existsSync(sourcePath)) {
@@ -94,14 +94,10 @@ async function migrateToFlat(cwd, options = {}) {
94
94
  else {
95
95
  console.log(` 🔍 Would migrate: ${fileName}`);
96
96
  }
97
- commandFiles.push(fileName);
97
+ commandUnits.push({ name: fileName, isDirectory: false });
98
98
  }
99
99
  else {
100
100
  const flatFileName = nameTransform.toFlatFileName(prefix, fileName);
101
- fileMappings.push({
102
- original: fileName,
103
- transformed: flatFileName,
104
- });
105
101
  if (!dryRun) {
106
102
  const content = fs.readFileSync(sourcePath, 'utf-8');
107
103
  filesystem.writeFlatAssetFile(cwd, assetType, flatFileName, content);
@@ -110,14 +106,19 @@ async function migrateToFlat(cwd, options = {}) {
110
106
  else {
111
107
  console.log(` 🔍 Would migrate: ${fileName} → ${flatFileName}`);
112
108
  }
109
+ skillUnits.push({
110
+ name: fileName,
111
+ isDirectory: false,
112
+ transformed: flatFileName,
113
+ });
113
114
  }
114
115
  }
115
116
  const packageInfo = {
116
117
  originalName: packageName$1,
117
118
  version: legacyMeta.version,
118
119
  files: {
119
- commands: assetType === 'commands' ? commandFiles : [],
120
- skills: assetType === 'skills' ? fileMappings : [],
120
+ commands: assetType === 'commands' ? commandUnits : [],
121
+ skills: assetType === 'skills' ? skillUnits : [],
121
122
  agents: [],
122
123
  },
123
124
  };
@@ -75,8 +75,8 @@ async function migrateToFlat(cwd, options = {}) {
75
75
  continue;
76
76
  }
77
77
  const prefix = packageNameToPrefix(packageName);
78
- const commandFiles = [];
79
- const fileMappings = [];
78
+ const commandUnits = [];
79
+ const skillUnits = [];
80
80
  for (const fileName of legacyMeta.files) {
81
81
  const sourcePath = join(packagePath, fileName);
82
82
  if (!existsSync(sourcePath)) {
@@ -92,14 +92,10 @@ async function migrateToFlat(cwd, options = {}) {
92
92
  else {
93
93
  console.log(` 🔍 Would migrate: ${fileName}`);
94
94
  }
95
- commandFiles.push(fileName);
95
+ commandUnits.push({ name: fileName, isDirectory: false });
96
96
  }
97
97
  else {
98
98
  const flatFileName = toFlatFileName(prefix, fileName);
99
- fileMappings.push({
100
- original: fileName,
101
- transformed: flatFileName,
102
- });
103
99
  if (!dryRun) {
104
100
  const content = readFileSync(sourcePath, 'utf-8');
105
101
  writeFlatAssetFile(cwd, assetType, flatFileName, content);
@@ -108,14 +104,19 @@ async function migrateToFlat(cwd, options = {}) {
108
104
  else {
109
105
  console.log(` 🔍 Would migrate: ${fileName} → ${flatFileName}`);
110
106
  }
107
+ skillUnits.push({
108
+ name: fileName,
109
+ isDirectory: false,
110
+ transformed: flatFileName,
111
+ });
111
112
  }
112
113
  }
113
114
  const packageInfo = {
114
115
  originalName: packageName,
115
116
  version: legacyMeta.version,
116
117
  files: {
117
- commands: assetType === 'commands' ? commandFiles : [],
118
- skills: assetType === 'skills' ? fileMappings : [],
118
+ commands: assetType === 'commands' ? commandUnits : [],
119
+ skills: assetType === 'skills' ? skillUnits : [],
119
120
  agents: [],
120
121
  },
121
122
  };