@slats/claude-assets-sync 0.0.1 → 0.0.3

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 (110) hide show
  1. package/CHANGELOG.md +189 -0
  2. package/README.md +541 -45
  3. package/dist/cli.cjs +8 -0
  4. package/dist/cli.d.ts +1 -0
  5. package/dist/cli.mjs +7 -0
  6. package/dist/commands/add.cjs +61 -0
  7. package/dist/commands/add.d.ts +12 -0
  8. package/dist/commands/add.mjs +59 -0
  9. package/dist/commands/index.d.ts +91 -0
  10. package/dist/commands/list.cjs +83 -0
  11. package/dist/commands/list.d.ts +6 -0
  12. package/dist/commands/list.mjs +81 -0
  13. package/dist/commands/migrate.cjs +9 -0
  14. package/dist/commands/migrate.d.ts +6 -0
  15. package/dist/commands/migrate.mjs +7 -0
  16. package/dist/commands/remove.cjs +109 -0
  17. package/dist/commands/remove.d.ts +6 -0
  18. package/dist/commands/remove.mjs +86 -0
  19. package/dist/commands/status.cjs +193 -0
  20. package/dist/commands/status.d.ts +6 -0
  21. package/dist/commands/status.mjs +171 -0
  22. package/dist/commands/sync.cjs +28 -0
  23. package/dist/commands/sync.d.ts +6 -0
  24. package/dist/commands/sync.mjs +26 -0
  25. package/dist/commands/types.d.ts +82 -0
  26. package/dist/components/add/AddCommand.cjs +92 -0
  27. package/dist/components/add/AddCommand.d.ts +14 -0
  28. package/dist/components/add/AddCommand.mjs +90 -0
  29. package/dist/components/add/index.d.ts +2 -0
  30. package/dist/components/index.d.ts +2 -0
  31. package/dist/components/list/EditableTreeItem.d.ts +13 -0
  32. package/dist/components/list/ListCommand.cjs +440 -0
  33. package/dist/components/list/ListCommand.d.ts +8 -0
  34. package/dist/components/list/ListCommand.mjs +418 -0
  35. package/dist/components/list/SyncedPackageTree.d.ts +14 -0
  36. package/dist/components/list/index.d.ts +9 -0
  37. package/dist/components/primitives/Box.d.ts +4 -0
  38. package/dist/components/primitives/Spinner.d.ts +6 -0
  39. package/dist/components/primitives/Text.d.ts +4 -0
  40. package/dist/components/primitives/index.d.ts +3 -0
  41. package/dist/components/status/PackageStatusCard.d.ts +10 -0
  42. package/dist/components/status/StatusDisplay.cjs +25 -0
  43. package/dist/components/status/StatusDisplay.d.ts +25 -0
  44. package/dist/components/status/StatusDisplay.mjs +23 -0
  45. package/dist/components/status/StatusTreeNode.cjs +40 -0
  46. package/dist/components/status/StatusTreeNode.d.ts +15 -0
  47. package/dist/components/status/StatusTreeNode.mjs +38 -0
  48. package/dist/components/status/index.d.ts +6 -0
  49. package/dist/components/tree/AssetTreeNode.cjs +37 -0
  50. package/dist/components/tree/AssetTreeNode.d.ts +12 -0
  51. package/dist/components/tree/AssetTreeNode.mjs +35 -0
  52. package/dist/components/tree/TreeSelect.cjs +121 -0
  53. package/dist/components/tree/TreeSelect.d.ts +12 -0
  54. package/dist/components/tree/TreeSelect.mjs +119 -0
  55. package/dist/components/tree/index.d.ts +4 -0
  56. package/dist/core/assetStructure.cjs +30 -0
  57. package/dist/core/assetStructure.d.ts +36 -0
  58. package/dist/core/assetStructure.mjs +27 -0
  59. package/dist/core/cli.cjs +92 -0
  60. package/dist/core/cli.mjs +89 -0
  61. package/dist/core/constants.cjs +23 -0
  62. package/dist/core/constants.d.ts +83 -0
  63. package/dist/core/constants.mjs +17 -0
  64. package/dist/core/filesystem.cjs +62 -51
  65. package/dist/core/filesystem.d.ts +38 -18
  66. package/dist/core/filesystem.mjs +56 -44
  67. package/dist/core/github.cjs +8 -11
  68. package/dist/core/github.d.ts +4 -6
  69. package/dist/core/github.mjs +8 -11
  70. package/dist/core/io.cjs +46 -0
  71. package/dist/core/io.d.ts +40 -0
  72. package/dist/core/io.mjs +39 -0
  73. package/dist/core/migration.cjs +199 -0
  74. package/dist/core/migration.d.ts +57 -0
  75. package/dist/core/migration.mjs +196 -0
  76. package/dist/core/packageScanner.cjs +259 -0
  77. package/dist/core/packageScanner.d.ts +17 -0
  78. package/dist/core/packageScanner.mjs +257 -0
  79. package/dist/core/sync.cjs +244 -61
  80. package/dist/core/sync.d.ts +6 -2
  81. package/dist/core/sync.mjs +246 -63
  82. package/dist/core/syncMeta.cjs +93 -0
  83. package/dist/core/syncMeta.d.ts +71 -0
  84. package/dist/core/syncMeta.mjs +84 -0
  85. package/dist/index.cjs +7 -8
  86. package/dist/index.d.ts +4 -3
  87. package/dist/index.mjs +2 -10
  88. package/dist/utils/nameTransform.cjs +12 -0
  89. package/dist/utils/nameTransform.d.ts +76 -0
  90. package/dist/utils/nameTransform.mjs +9 -0
  91. package/dist/utils/package.cjs +22 -17
  92. package/dist/utils/package.d.ts +25 -0
  93. package/dist/utils/package.mjs +11 -7
  94. package/dist/utils/packageName.cjs +24 -0
  95. package/dist/utils/packageName.d.ts +32 -0
  96. package/dist/utils/packageName.mjs +21 -0
  97. package/dist/utils/paths.cjs +18 -0
  98. package/dist/utils/paths.d.ts +55 -0
  99. package/dist/utils/paths.mjs +15 -0
  100. package/dist/utils/types.d.ts +153 -6
  101. package/dist/utils/version.cjs +17 -0
  102. package/dist/utils/version.d.ts +55 -0
  103. package/dist/utils/version.mjs +14 -0
  104. package/dist/version.cjs +5 -0
  105. package/dist/version.d.ts +5 -0
  106. package/dist/version.mjs +3 -0
  107. package/package.json +16 -7
  108. package/dist/cli/index.cjs +0 -56
  109. package/dist/cli/index.mjs +0 -53
  110. /package/dist/{cli/index.d.ts → core/cli.d.ts} +0 -0
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var ink = require('ink');
5
+
6
+ function hasPartialSelection(node) {
7
+ if (!node.children || node.children.length === 0) {
8
+ return false;
9
+ }
10
+ const selectedCount = node.children.filter((child) => child.selected).length;
11
+ return selectedCount > 0 && selectedCount < node.children.length;
12
+ }
13
+ const AssetTreeNode = ({ node, depth, isSelected, }) => {
14
+ const indent = ' '.repeat(depth);
15
+ let selectionIcon;
16
+ let iconColor;
17
+ if (hasPartialSelection(node)) {
18
+ selectionIcon = '◐';
19
+ iconColor = 'yellow';
20
+ }
21
+ else if (node.selected) {
22
+ selectionIcon = '●';
23
+ iconColor = 'green';
24
+ }
25
+ else {
26
+ selectionIcon = '○';
27
+ iconColor = 'red';
28
+ }
29
+ const expandIcon = node.type === 'directory' && node.children
30
+ ? node.expanded
31
+ ? '▼'
32
+ : '▶'
33
+ : ' ';
34
+ return (jsxRuntime.jsx(ink.Box, { children: jsxRuntime.jsxs(ink.Text, { color: isSelected ? 'cyan' : undefined, children: [indent, expandIcon, " ", jsxRuntime.jsx(ink.Text, { color: iconColor, children: selectionIcon }), " ", node.label] }) }));
35
+ };
36
+
37
+ exports.AssetTreeNode = AssetTreeNode;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import type { TreeNode } from '../../utils/types.js';
3
+ export interface AssetTreeNodeProps {
4
+ node: TreeNode;
5
+ depth: number;
6
+ isSelected: boolean;
7
+ onToggle: () => void;
8
+ }
9
+ /**
10
+ * Single tree node component for asset selection
11
+ */
12
+ export declare const AssetTreeNode: React.FC<AssetTreeNodeProps>;
@@ -0,0 +1,35 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { Box, Text } from 'ink';
3
+
4
+ function hasPartialSelection(node) {
5
+ if (!node.children || node.children.length === 0) {
6
+ return false;
7
+ }
8
+ const selectedCount = node.children.filter((child) => child.selected).length;
9
+ return selectedCount > 0 && selectedCount < node.children.length;
10
+ }
11
+ const AssetTreeNode = ({ node, depth, isSelected, }) => {
12
+ const indent = ' '.repeat(depth);
13
+ let selectionIcon;
14
+ let iconColor;
15
+ if (hasPartialSelection(node)) {
16
+ selectionIcon = '◐';
17
+ iconColor = 'yellow';
18
+ }
19
+ else if (node.selected) {
20
+ selectionIcon = '●';
21
+ iconColor = 'green';
22
+ }
23
+ else {
24
+ selectionIcon = '○';
25
+ iconColor = 'red';
26
+ }
27
+ const expandIcon = node.type === 'directory' && node.children
28
+ ? node.expanded
29
+ ? '▼'
30
+ : '▶'
31
+ : ' ';
32
+ return (jsx(Box, { children: jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [indent, expandIcon, " ", jsx(Text, { color: iconColor, children: selectionIcon }), " ", node.label] }) }));
33
+ };
34
+
35
+ export { AssetTreeNode };
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var ink = require('ink');
5
+ var React = require('react');
6
+ var AssetTreeNode = require('./AssetTreeNode.cjs');
7
+
8
+ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
9
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
10
+ const [treeData, setTreeData] = React.useState(trees);
11
+ const flattenTree = (nodes, depth = 0) => {
12
+ const result = [];
13
+ const traverse = (items, currentDepth, parentPath = []) => {
14
+ items.forEach((item, index) => {
15
+ const currentPath = [...parentPath, index];
16
+ result.push({ node: item, depth: currentDepth, path: currentPath });
17
+ if (item.expanded && item.children) {
18
+ traverse(item.children, currentDepth + 1, currentPath);
19
+ }
20
+ });
21
+ };
22
+ traverse(nodes, depth);
23
+ return result;
24
+ };
25
+ const flatItems = flattenTree(treeData);
26
+ ink.useInput((input, key) => {
27
+ if (key.upArrow) {
28
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
29
+ }
30
+ else if (key.downArrow) {
31
+ setSelectedIndex((prev) => Math.min(flatItems.length - 1, prev + 1));
32
+ }
33
+ else if (key.rightArrow) {
34
+ const current = flatItems[selectedIndex];
35
+ if (current.node.type === 'directory' && !current.node.expanded) {
36
+ const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
37
+ ...node,
38
+ expanded: true,
39
+ }));
40
+ setTreeData(newTrees);
41
+ }
42
+ }
43
+ else if (key.leftArrow) {
44
+ const current = flatItems[selectedIndex];
45
+ if (current.node.type === 'directory' && current.node.expanded) {
46
+ const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
47
+ ...node,
48
+ expanded: false,
49
+ }));
50
+ setTreeData(newTrees);
51
+ }
52
+ }
53
+ else if (input === ' ') {
54
+ const current = flatItems[selectedIndex];
55
+ const newTrees = toggleNodeSelection(treeData, current.path);
56
+ setTreeData(newTrees);
57
+ }
58
+ else if (input === 'r' && onRefresh) {
59
+ onRefresh();
60
+ }
61
+ else if (key.return) {
62
+ onSubmit(treeData);
63
+ }
64
+ else if (key.escape || input === 'q') {
65
+ onCancel();
66
+ }
67
+ });
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: () => {
69
+ const newTrees = toggleNodeSelection(treeData, item.path);
70
+ setTreeData(newTrees);
71
+ } }, item.path.join('-')))) }));
72
+ };
73
+ function updateNodeAtPath(trees, path, updater) {
74
+ if (path.length === 0)
75
+ return trees;
76
+ const [index, ...rest] = path;
77
+ const newTrees = [...trees];
78
+ if (rest.length === 0) {
79
+ newTrees[index] = updater(newTrees[index]);
80
+ }
81
+ else if (newTrees[index].children) {
82
+ newTrees[index] = {
83
+ ...newTrees[index],
84
+ children: updateNodeAtPath(newTrees[index].children, rest, updater),
85
+ };
86
+ }
87
+ return newTrees;
88
+ }
89
+ function toggleNodeSelection(trees, path) {
90
+ const updatedTrees = updateNodeAtPath(trees, path, (node) => {
91
+ const newSelected = !node.selected;
92
+ return toggleNodeAndChildren(node, newSelected);
93
+ });
94
+ return updateAncestors(updatedTrees, path);
95
+ }
96
+ function toggleNodeAndChildren(node, selected) {
97
+ const newNode = { ...node, selected };
98
+ if (node.children) {
99
+ newNode.children = node.children.map((child) => toggleNodeAndChildren(child, selected));
100
+ }
101
+ return newNode;
102
+ }
103
+ function updateAncestors(trees, path) {
104
+ if (path.length <= 1) {
105
+ return trees;
106
+ }
107
+ const parentPath = path.slice(0, -1);
108
+ const updatedTrees = updateNodeAtPath(trees, parentPath, (parentNode) => {
109
+ if (!parentNode.children || parentNode.children.length === 0) {
110
+ return parentNode;
111
+ }
112
+ const anySelected = parentNode.children.some((child) => child.selected);
113
+ return {
114
+ ...parentNode,
115
+ selected: anySelected,
116
+ };
117
+ });
118
+ return updateAncestors(updatedTrees, parentPath);
119
+ }
120
+
121
+ exports.TreeSelect = TreeSelect;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import type { TreeNode } from '../../utils/types.js';
3
+ export interface TreeSelectProps {
4
+ trees: TreeNode[];
5
+ onSubmit: (trees: TreeNode[]) => void;
6
+ onCancel: () => void;
7
+ onRefresh?: () => void;
8
+ }
9
+ /**
10
+ * Interactive tree selection component
11
+ */
12
+ export declare const TreeSelect: React.FC<TreeSelectProps>;
@@ -0,0 +1,119 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useInput, Box } from 'ink';
3
+ import { useState } from 'react';
4
+ import { AssetTreeNode } from './AssetTreeNode.mjs';
5
+
6
+ const TreeSelect = ({ trees, onSubmit, onCancel, onRefresh, }) => {
7
+ const [selectedIndex, setSelectedIndex] = useState(0);
8
+ const [treeData, setTreeData] = useState(trees);
9
+ const flattenTree = (nodes, depth = 0) => {
10
+ const result = [];
11
+ const traverse = (items, currentDepth, parentPath = []) => {
12
+ items.forEach((item, index) => {
13
+ const currentPath = [...parentPath, index];
14
+ result.push({ node: item, depth: currentDepth, path: currentPath });
15
+ if (item.expanded && item.children) {
16
+ traverse(item.children, currentDepth + 1, currentPath);
17
+ }
18
+ });
19
+ };
20
+ traverse(nodes, depth);
21
+ return result;
22
+ };
23
+ const flatItems = flattenTree(treeData);
24
+ useInput((input, key) => {
25
+ if (key.upArrow) {
26
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
27
+ }
28
+ else if (key.downArrow) {
29
+ setSelectedIndex((prev) => Math.min(flatItems.length - 1, prev + 1));
30
+ }
31
+ else if (key.rightArrow) {
32
+ const current = flatItems[selectedIndex];
33
+ if (current.node.type === 'directory' && !current.node.expanded) {
34
+ const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
35
+ ...node,
36
+ expanded: true,
37
+ }));
38
+ setTreeData(newTrees);
39
+ }
40
+ }
41
+ else if (key.leftArrow) {
42
+ const current = flatItems[selectedIndex];
43
+ if (current.node.type === 'directory' && current.node.expanded) {
44
+ const newTrees = updateNodeAtPath(treeData, current.path, (node) => ({
45
+ ...node,
46
+ expanded: false,
47
+ }));
48
+ setTreeData(newTrees);
49
+ }
50
+ }
51
+ else if (input === ' ') {
52
+ const current = flatItems[selectedIndex];
53
+ const newTrees = toggleNodeSelection(treeData, current.path);
54
+ setTreeData(newTrees);
55
+ }
56
+ else if (input === 'r' && onRefresh) {
57
+ onRefresh();
58
+ }
59
+ else if (key.return) {
60
+ onSubmit(treeData);
61
+ }
62
+ else if (key.escape || input === 'q') {
63
+ onCancel();
64
+ }
65
+ });
66
+ return (jsx(Box, { flexDirection: "column", children: flatItems.map((item, index) => (jsx(AssetTreeNode, { node: item.node, depth: item.depth, isSelected: index === selectedIndex, onToggle: () => {
67
+ const newTrees = toggleNodeSelection(treeData, item.path);
68
+ setTreeData(newTrees);
69
+ } }, item.path.join('-')))) }));
70
+ };
71
+ function updateNodeAtPath(trees, path, updater) {
72
+ if (path.length === 0)
73
+ return trees;
74
+ const [index, ...rest] = path;
75
+ const newTrees = [...trees];
76
+ if (rest.length === 0) {
77
+ newTrees[index] = updater(newTrees[index]);
78
+ }
79
+ else if (newTrees[index].children) {
80
+ newTrees[index] = {
81
+ ...newTrees[index],
82
+ children: updateNodeAtPath(newTrees[index].children, rest, updater),
83
+ };
84
+ }
85
+ return newTrees;
86
+ }
87
+ function toggleNodeSelection(trees, path) {
88
+ const updatedTrees = updateNodeAtPath(trees, path, (node) => {
89
+ const newSelected = !node.selected;
90
+ return toggleNodeAndChildren(node, newSelected);
91
+ });
92
+ return updateAncestors(updatedTrees, path);
93
+ }
94
+ function toggleNodeAndChildren(node, selected) {
95
+ const newNode = { ...node, selected };
96
+ if (node.children) {
97
+ newNode.children = node.children.map((child) => toggleNodeAndChildren(child, selected));
98
+ }
99
+ return newNode;
100
+ }
101
+ function updateAncestors(trees, path) {
102
+ if (path.length <= 1) {
103
+ return trees;
104
+ }
105
+ const parentPath = path.slice(0, -1);
106
+ const updatedTrees = updateNodeAtPath(trees, parentPath, (parentNode) => {
107
+ if (!parentNode.children || parentNode.children.length === 0) {
108
+ return parentNode;
109
+ }
110
+ const anySelected = parentNode.children.some((child) => child.selected);
111
+ return {
112
+ ...parentNode,
113
+ selected: anySelected,
114
+ };
115
+ });
116
+ return updateAncestors(updatedTrees, parentPath);
117
+ }
118
+
119
+ export { TreeSelect };
@@ -0,0 +1,4 @@
1
+ export { TreeSelect } from './TreeSelect.js';
2
+ export { AssetTreeNode } from './AssetTreeNode.js';
3
+ export type { TreeSelectProps } from './TreeSelect.js';
4
+ export type { AssetTreeNodeProps } from './AssetTreeNode.js';
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ require('node:fs');
4
+ require('node:path');
5
+
6
+ const DEFAULT_ASSET_STRUCTURES = {
7
+ commands: 'nested',
8
+ skills: 'flat',
9
+ agents: 'flat',
10
+ };
11
+ function getAssetStructure(assetType, config) {
12
+ if (!config) {
13
+ return DEFAULT_ASSET_STRUCTURES[assetType] || 'flat';
14
+ }
15
+ let assetsConfig;
16
+ if ('assetPath' in config) {
17
+ assetsConfig = config
18
+ .assets;
19
+ }
20
+ else {
21
+ assetsConfig = config;
22
+ }
23
+ if (assetsConfig?.[assetType]?.structure) {
24
+ return assetsConfig[assetType].structure;
25
+ }
26
+ return DEFAULT_ASSET_STRUCTURES[assetType] || 'nested';
27
+ }
28
+
29
+ exports.DEFAULT_ASSET_STRUCTURES = DEFAULT_ASSET_STRUCTURES;
30
+ exports.getAssetStructure = getAssetStructure;
@@ -0,0 +1,36 @@
1
+ import type { AssetStructure, AssetsConfig } from '../utils/types';
2
+ import { DEFAULT_ASSET_TYPES } from './constants';
3
+ export { DEFAULT_ASSET_TYPES };
4
+ /**
5
+ * Default structure configuration for built-in asset types
6
+ */
7
+ export declare const DEFAULT_ASSET_STRUCTURES: Record<string, AssetStructure>;
8
+ /**
9
+ * Get the structure type for a given asset type
10
+ * @param assetType - The asset type to query
11
+ * @param config - Optional ClaudeConfig or AssetsConfig
12
+ * @returns The structure type ('nested' or 'flat')
13
+ */
14
+ export declare function getAssetStructure(assetType: string, config?: AssetsConfig | {
15
+ assetPath: string;
16
+ assets?: AssetsConfig;
17
+ }): AssetStructure;
18
+ /**
19
+ * Detect the structure type of an asset directory
20
+ * @param dir - Directory path to check
21
+ * @returns Structure type ('flat' | 'nested') or null if directory doesn't exist
22
+ */
23
+ export declare function detectStructureType(dir: string): AssetStructure | null;
24
+ /**
25
+ * Validate that an asset structure value is valid
26
+ * @param structure - Structure value to validate
27
+ * @returns True if valid, false otherwise
28
+ */
29
+ export declare function validateAssetStructure(structure: unknown): structure is AssetStructure;
30
+ /**
31
+ * Validate and normalize asset structure value
32
+ * @param structure - Structure value to normalize
33
+ * @param defaultValue - Default value if validation fails
34
+ * @returns Validated structure or default
35
+ */
36
+ export declare function normalizeAssetStructure(structure: unknown, defaultValue?: AssetStructure): AssetStructure;
@@ -0,0 +1,27 @@
1
+ import 'node:fs';
2
+ import 'node:path';
3
+
4
+ const DEFAULT_ASSET_STRUCTURES = {
5
+ commands: 'nested',
6
+ skills: 'flat',
7
+ agents: 'flat',
8
+ };
9
+ function getAssetStructure(assetType, config) {
10
+ if (!config) {
11
+ return DEFAULT_ASSET_STRUCTURES[assetType] || 'flat';
12
+ }
13
+ let assetsConfig;
14
+ if ('assetPath' in config) {
15
+ assetsConfig = config
16
+ .assets;
17
+ }
18
+ else {
19
+ assetsConfig = config;
20
+ }
21
+ if (assetsConfig?.[assetType]?.structure) {
22
+ return assetsConfig[assetType].structure;
23
+ }
24
+ return DEFAULT_ASSET_STRUCTURES[assetType] || 'nested';
25
+ }
26
+
27
+ export { DEFAULT_ASSET_STRUCTURES, getAssetStructure };
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ var commander = require('commander');
4
+ var sync = require('../commands/sync.cjs');
5
+ var list = require('../commands/list.cjs');
6
+ var remove = require('../commands/remove.cjs');
7
+ var status = require('../commands/status.cjs');
8
+ var migrate = require('../commands/migrate.cjs');
9
+ var add = require('../commands/add.cjs');
10
+ var version = require('../version.cjs');
11
+
12
+ const createProgram = () => {
13
+ const program = new commander.Command();
14
+ program
15
+ .name('claude-assets-sync')
16
+ .description('Sync Claude commands and skills from npm packages to your project')
17
+ .version(version.VERSION);
18
+ program
19
+ .command('sync', { isDefault: true })
20
+ .description('Sync Claude assets from npm packages')
21
+ .option('-p, --package <name>', 'Package name to sync (can be specified multiple times)', (value, previous) => [...previous, value], [])
22
+ .option('-f, --force', 'Force sync even if version matches', false)
23
+ .option('--dry-run', 'Preview changes without writing files', false)
24
+ .option('-l, --local', 'Read packages from local workspace instead of node_modules', false)
25
+ .option('-r, --ref <ref>', 'Git ref (branch, tag, or commit) to fetch from (overrides version tag)')
26
+ .option('--no-flat', 'Use legacy nested directory structure instead of flat structure (default: flat)')
27
+ .action(async (opts) => {
28
+ await sync.runSyncCommand({
29
+ package: opts.package,
30
+ force: opts.force,
31
+ dryRun: opts.dryRun,
32
+ local: opts.local,
33
+ ref: opts.ref,
34
+ flat: opts.flat,
35
+ });
36
+ });
37
+ program
38
+ .command('add')
39
+ .description('Add a package with interactive asset selection')
40
+ .requiredOption('-p, --package <name>', 'Package name to add')
41
+ .option('-l, --local', 'Read packages from local workspace instead of node_modules', false)
42
+ .option('-r, --ref <ref>', 'Git ref (branch, tag, or commit) to fetch from')
43
+ .action(async (opts) => {
44
+ await add.runAddCommand({
45
+ package: opts.package,
46
+ local: opts.local,
47
+ ref: opts.ref,
48
+ });
49
+ });
50
+ program
51
+ .command('list')
52
+ .description('List all synced packages')
53
+ .option('--json', 'Output as JSON')
54
+ .action(async (opts) => {
55
+ await list.runListCommand({ json: opts.json });
56
+ });
57
+ program
58
+ .command('remove')
59
+ .description('Remove a synced package')
60
+ .requiredOption('-p, --package <name>', 'Package name to remove')
61
+ .option('-y, --yes', 'Skip confirmation prompt')
62
+ .option('--dry-run', 'Preview changes without removing files')
63
+ .action(async (opts) => {
64
+ await remove.runRemoveCommand({
65
+ package: opts.package,
66
+ yes: opts.yes,
67
+ dryRun: opts.dryRun,
68
+ });
69
+ });
70
+ program
71
+ .command('status')
72
+ .description('Show sync status of all packages')
73
+ .option('--no-remote', 'Skip remote version check')
74
+ .action(async (opts) => {
75
+ await status.runStatusCommand({ noRemote: !opts.remote });
76
+ });
77
+ program
78
+ .command('migrate')
79
+ .description('Migrate from legacy nested structure to flat structure')
80
+ .option('--dry-run', 'Preview migration without making changes')
81
+ .action(async (opts) => {
82
+ await migrate.runMigrateCommand({ dryRun: opts.dryRun });
83
+ });
84
+ return program;
85
+ };
86
+ const run = async () => {
87
+ const program = createProgram();
88
+ await program.parseAsync(process.argv);
89
+ };
90
+
91
+ exports.createProgram = createProgram;
92
+ exports.run = run;
@@ -0,0 +1,89 @@
1
+ import { Command } from 'commander';
2
+ import { runSyncCommand } from '../commands/sync.mjs';
3
+ import { runListCommand } from '../commands/list.mjs';
4
+ import { runRemoveCommand } from '../commands/remove.mjs';
5
+ import { runStatusCommand } from '../commands/status.mjs';
6
+ import { runMigrateCommand } from '../commands/migrate.mjs';
7
+ import { runAddCommand } from '../commands/add.mjs';
8
+ import { VERSION } from '../version.mjs';
9
+
10
+ const createProgram = () => {
11
+ const program = new Command();
12
+ program
13
+ .name('claude-assets-sync')
14
+ .description('Sync Claude commands and skills from npm packages to your project')
15
+ .version(VERSION);
16
+ program
17
+ .command('sync', { isDefault: true })
18
+ .description('Sync Claude assets from npm packages')
19
+ .option('-p, --package <name>', 'Package name to sync (can be specified multiple times)', (value, previous) => [...previous, value], [])
20
+ .option('-f, --force', 'Force sync even if version matches', false)
21
+ .option('--dry-run', 'Preview changes without writing files', false)
22
+ .option('-l, --local', 'Read packages from local workspace instead of node_modules', false)
23
+ .option('-r, --ref <ref>', 'Git ref (branch, tag, or commit) to fetch from (overrides version tag)')
24
+ .option('--no-flat', 'Use legacy nested directory structure instead of flat structure (default: flat)')
25
+ .action(async (opts) => {
26
+ await runSyncCommand({
27
+ package: opts.package,
28
+ force: opts.force,
29
+ dryRun: opts.dryRun,
30
+ local: opts.local,
31
+ ref: opts.ref,
32
+ flat: opts.flat,
33
+ });
34
+ });
35
+ program
36
+ .command('add')
37
+ .description('Add a package with interactive asset selection')
38
+ .requiredOption('-p, --package <name>', 'Package name to add')
39
+ .option('-l, --local', 'Read packages from local workspace instead of node_modules', false)
40
+ .option('-r, --ref <ref>', 'Git ref (branch, tag, or commit) to fetch from')
41
+ .action(async (opts) => {
42
+ await runAddCommand({
43
+ package: opts.package,
44
+ local: opts.local,
45
+ ref: opts.ref,
46
+ });
47
+ });
48
+ program
49
+ .command('list')
50
+ .description('List all synced packages')
51
+ .option('--json', 'Output as JSON')
52
+ .action(async (opts) => {
53
+ await runListCommand({ json: opts.json });
54
+ });
55
+ program
56
+ .command('remove')
57
+ .description('Remove a synced package')
58
+ .requiredOption('-p, --package <name>', 'Package name to remove')
59
+ .option('-y, --yes', 'Skip confirmation prompt')
60
+ .option('--dry-run', 'Preview changes without removing files')
61
+ .action(async (opts) => {
62
+ await runRemoveCommand({
63
+ package: opts.package,
64
+ yes: opts.yes,
65
+ dryRun: opts.dryRun,
66
+ });
67
+ });
68
+ program
69
+ .command('status')
70
+ .description('Show sync status of all packages')
71
+ .option('--no-remote', 'Skip remote version check')
72
+ .action(async (opts) => {
73
+ await runStatusCommand({ noRemote: !opts.remote });
74
+ });
75
+ program
76
+ .command('migrate')
77
+ .description('Migrate from legacy nested structure to flat structure')
78
+ .option('--dry-run', 'Preview migration without making changes')
79
+ .action(async (opts) => {
80
+ await runMigrateCommand({ dryRun: opts.dryRun });
81
+ });
82
+ return program;
83
+ };
84
+ const run = async () => {
85
+ const program = createProgram();
86
+ await program.parseAsync(process.argv);
87
+ };
88
+
89
+ export { createProgram, run };
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ var version = require('../version.cjs');
4
+
5
+ const CLAUDE_BASE_DIR = '.claude';
6
+ const META_FILES = {
7
+ SYNC_META: '.sync-meta.json',
8
+ UNIFIED_SYNC_META: '.claude/.sync-meta.json',
9
+ };
10
+ const SCHEMA_VERSIONS = {
11
+ UNIFIED_SYNC_META: version.VERSION};
12
+ const DEFAULT_ASSET_TYPES = ['commands', 'skills', 'agents'];
13
+ const FS_PATTERNS = {
14
+ GITHUB_HTTPS_URL: /https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/,
15
+ GITHUB_SSH_URL: /git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/,
16
+ GITHUB_SHORTHAND: /^github:([^/]+)\/([^/]+)$/,
17
+ };
18
+
19
+ exports.CLAUDE_BASE_DIR = CLAUDE_BASE_DIR;
20
+ exports.DEFAULT_ASSET_TYPES = DEFAULT_ASSET_TYPES;
21
+ exports.FS_PATTERNS = FS_PATTERNS;
22
+ exports.META_FILES = META_FILES;
23
+ exports.SCHEMA_VERSIONS = SCHEMA_VERSIONS;