@slats/claude-assets-sync 0.0.1 → 0.0.2

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 (107) hide show
  1. package/CHANGELOG.md +189 -0
  2. package/README.md +541 -45
  3. package/dist/commands/add.cjs +61 -0
  4. package/dist/commands/add.d.ts +12 -0
  5. package/dist/commands/add.mjs +59 -0
  6. package/dist/commands/index.d.ts +91 -0
  7. package/dist/commands/list.cjs +83 -0
  8. package/dist/commands/list.d.ts +6 -0
  9. package/dist/commands/list.mjs +81 -0
  10. package/dist/commands/migrate.cjs +9 -0
  11. package/dist/commands/migrate.d.ts +6 -0
  12. package/dist/commands/migrate.mjs +7 -0
  13. package/dist/commands/remove.cjs +109 -0
  14. package/dist/commands/remove.d.ts +6 -0
  15. package/dist/commands/remove.mjs +86 -0
  16. package/dist/commands/status.cjs +193 -0
  17. package/dist/commands/status.d.ts +6 -0
  18. package/dist/commands/status.mjs +171 -0
  19. package/dist/commands/sync.cjs +28 -0
  20. package/dist/commands/sync.d.ts +6 -0
  21. package/dist/commands/sync.mjs +26 -0
  22. package/dist/commands/types.d.ts +82 -0
  23. package/dist/components/add/AddCommand.cjs +92 -0
  24. package/dist/components/add/AddCommand.d.ts +14 -0
  25. package/dist/components/add/AddCommand.mjs +90 -0
  26. package/dist/components/add/index.d.ts +2 -0
  27. package/dist/components/index.d.ts +2 -0
  28. package/dist/components/list/EditableTreeItem.d.ts +13 -0
  29. package/dist/components/list/ListCommand.cjs +440 -0
  30. package/dist/components/list/ListCommand.d.ts +8 -0
  31. package/dist/components/list/ListCommand.mjs +418 -0
  32. package/dist/components/list/SyncedPackageTree.d.ts +14 -0
  33. package/dist/components/list/index.d.ts +9 -0
  34. package/dist/components/primitives/Box.d.ts +4 -0
  35. package/dist/components/primitives/Spinner.d.ts +6 -0
  36. package/dist/components/primitives/Text.d.ts +4 -0
  37. package/dist/components/primitives/index.d.ts +3 -0
  38. package/dist/components/status/PackageStatusCard.d.ts +10 -0
  39. package/dist/components/status/StatusDisplay.cjs +25 -0
  40. package/dist/components/status/StatusDisplay.d.ts +25 -0
  41. package/dist/components/status/StatusDisplay.mjs +23 -0
  42. package/dist/components/status/StatusTreeNode.cjs +40 -0
  43. package/dist/components/status/StatusTreeNode.d.ts +15 -0
  44. package/dist/components/status/StatusTreeNode.mjs +38 -0
  45. package/dist/components/status/index.d.ts +6 -0
  46. package/dist/components/tree/AssetTreeNode.cjs +37 -0
  47. package/dist/components/tree/AssetTreeNode.d.ts +12 -0
  48. package/dist/components/tree/AssetTreeNode.mjs +35 -0
  49. package/dist/components/tree/TreeSelect.cjs +121 -0
  50. package/dist/components/tree/TreeSelect.d.ts +12 -0
  51. package/dist/components/tree/TreeSelect.mjs +119 -0
  52. package/dist/components/tree/index.d.ts +4 -0
  53. package/dist/core/assetStructure.cjs +30 -0
  54. package/dist/core/assetStructure.d.ts +36 -0
  55. package/dist/core/assetStructure.mjs +27 -0
  56. package/dist/core/cli.cjs +92 -0
  57. package/dist/core/cli.mjs +89 -0
  58. package/dist/core/constants.cjs +23 -0
  59. package/dist/core/constants.d.ts +83 -0
  60. package/dist/core/constants.mjs +17 -0
  61. package/dist/core/filesystem.cjs +62 -51
  62. package/dist/core/filesystem.d.ts +38 -18
  63. package/dist/core/filesystem.mjs +56 -44
  64. package/dist/core/github.cjs +8 -11
  65. package/dist/core/github.d.ts +4 -6
  66. package/dist/core/github.mjs +8 -11
  67. package/dist/core/io.cjs +46 -0
  68. package/dist/core/io.d.ts +40 -0
  69. package/dist/core/io.mjs +39 -0
  70. package/dist/core/migration.cjs +199 -0
  71. package/dist/core/migration.d.ts +57 -0
  72. package/dist/core/migration.mjs +196 -0
  73. package/dist/core/packageScanner.cjs +259 -0
  74. package/dist/core/packageScanner.d.ts +17 -0
  75. package/dist/core/packageScanner.mjs +257 -0
  76. package/dist/core/sync.cjs +244 -61
  77. package/dist/core/sync.d.ts +6 -2
  78. package/dist/core/sync.mjs +246 -63
  79. package/dist/core/syncMeta.cjs +93 -0
  80. package/dist/core/syncMeta.d.ts +71 -0
  81. package/dist/core/syncMeta.mjs +84 -0
  82. package/dist/index.cjs +7 -4
  83. package/dist/index.d.ts +4 -2
  84. package/dist/index.mjs +3 -2
  85. package/dist/utils/nameTransform.cjs +12 -0
  86. package/dist/utils/nameTransform.d.ts +76 -0
  87. package/dist/utils/nameTransform.mjs +9 -0
  88. package/dist/utils/package.cjs +22 -17
  89. package/dist/utils/package.d.ts +25 -0
  90. package/dist/utils/package.mjs +11 -7
  91. package/dist/utils/packageName.cjs +24 -0
  92. package/dist/utils/packageName.d.ts +32 -0
  93. package/dist/utils/packageName.mjs +21 -0
  94. package/dist/utils/paths.cjs +18 -0
  95. package/dist/utils/paths.d.ts +55 -0
  96. package/dist/utils/paths.mjs +15 -0
  97. package/dist/utils/types.d.ts +153 -6
  98. package/dist/utils/version.cjs +17 -0
  99. package/dist/utils/version.d.ts +55 -0
  100. package/dist/utils/version.mjs +14 -0
  101. package/dist/version.cjs +5 -0
  102. package/dist/version.d.ts +5 -0
  103. package/dist/version.mjs +3 -0
  104. package/package.json +15 -6
  105. package/dist/cli/index.cjs +0 -56
  106. package/dist/cli/index.mjs +0 -53
  107. /package/dist/{cli/index.d.ts → core/cli.d.ts} +0 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Name transformation utilities for Claude assets synchronization
3
+ * Handles conversion between package names and flat file naming conventions
4
+ */
5
+ import { packageNameToPrefix as packageNameToPrefixUtil } from './packageName';
6
+ /**
7
+ * Converts kebab-case string to camelCase
8
+ * All hyphens are removed and the following character is capitalized
9
+ *
10
+ * @param str - Input string in kebab-case format
11
+ * @returns String in camelCase format
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * kebabToCamel('schema-form') // 'schemaForm'
16
+ * kebabToCamel('schema-form-plugin') // 'schemaFormPlugin'
17
+ * kebabToCamel('my-long-name') // 'myLongName'
18
+ * ```
19
+ */
20
+ export declare function kebabToCamel(str: string): string;
21
+ /**
22
+ * Converts a scoped package name to a flat prefix
23
+ * @deprecated Import from './packageName' instead
24
+ * @param packageName - Scoped package name (e.g., '@canard/schema-form')
25
+ * @returns Flat prefix (e.g., 'canard-schemaForm')
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * packageNameToPrefix('@canard/schema-form') // 'canard-schemaForm'
30
+ * packageNameToPrefix('@canard/schema-form-plugin') // 'canard-schemaFormPlugin'
31
+ * packageNameToPrefix('@winglet/react-utils') // 'winglet-reactUtils'
32
+ * packageNameToPrefix('@lerx/promise-modal') // 'lerx-promiseModal'
33
+ * ```
34
+ */
35
+ export declare const packageNameToPrefix: typeof packageNameToPrefixUtil;
36
+ /**
37
+ * Creates a flat file name by combining prefix and original file name
38
+ *
39
+ * @param prefix - Package prefix (e.g., 'canard-schemaForm')
40
+ * @param fileName - Original file name (e.g., 'guide.md')
41
+ * @returns Flat file name (e.g., 'canard-schemaForm_guide.md')
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * toFlatFileName('canard-schemaForm', 'guide.md') // 'canard-schemaForm_guide.md'
46
+ * toFlatFileName('winglet-reactUtils', 'README.md') // 'winglet-reactUtils_README.md'
47
+ * toFlatFileName('lerx-promiseModal', 'api/types.md') // 'lerx-promiseModal_api_types.md'
48
+ * ```
49
+ */
50
+ export declare function toFlatFileName(prefix: string, fileName: string): string;
51
+ /**
52
+ * Parses a flat file name back into prefix and original file name
53
+ * Returns null if the file name doesn't match the expected pattern
54
+ *
55
+ * @param flatName - Flat file name (e.g., 'canard-schemaForm_guide.md')
56
+ * @returns Object with prefix and original name, or null if invalid
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * parseFlatFileName('canard-schemaForm_guide.md')
61
+ * // { prefix: 'canard-schemaForm', original: 'guide.md' }
62
+ *
63
+ * parseFlatFileName('winglet-reactUtils_README.md')
64
+ * // { prefix: 'winglet-reactUtils', original: 'README.md' }
65
+ *
66
+ * parseFlatFileName('lerx-promiseModal_api_types.md')
67
+ * // { prefix: 'lerx-promiseModal', original: 'api/types.md' }
68
+ *
69
+ * parseFlatFileName('invalid-name.md')
70
+ * // null (no underscore separator)
71
+ * ```
72
+ */
73
+ export declare function parseFlatFileName(flatName: string): {
74
+ prefix: string;
75
+ original: string;
76
+ } | null;
@@ -0,0 +1,9 @@
1
+ import { packageNameToPrefix as packageNameToPrefix$1 } from './packageName.mjs';
2
+
3
+ const packageNameToPrefix = packageNameToPrefix$1;
4
+ function toFlatFileName(prefix, fileName) {
5
+ const flatFileName = fileName.replace(/[/\\]/g, '_');
6
+ return `${prefix}_${flatFileName}`;
7
+ }
8
+
9
+ export { packageNameToPrefix, toFlatFileName };
@@ -1,16 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  var node_child_process = require('node:child_process');
4
- var node_fs = require('node:fs');
5
- var node_path = require('node:path');
4
+ var fs = require('node:fs');
5
+ var path = require('node:path');
6
+ var constants = require('../core/constants.cjs');
6
7
 
7
- const GITHUB_HTTPS_URL_PATTERN = /https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/;
8
- const GITHUB_SSH_URL_PATTERN = /git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/;
9
- const GITHUB_SHORTHAND_PATTERN = /^github:([^/]+)\/([^/]+)$/;
10
8
  const readPackageJson = (packageName, cwd = process.cwd()) => {
11
9
  try {
12
- const packageJsonPath = node_path.join(cwd, 'node_modules', packageName, 'package.json');
13
- const content = node_fs.readFileSync(packageJsonPath, 'utf-8');
10
+ const packageJsonPath = path.join(cwd, 'node_modules', packageName, 'package.json');
11
+ const content = fs.readFileSync(packageJsonPath, 'utf-8');
14
12
  const json = JSON.parse(content);
15
13
  if (!json.name || !json.version || !json.repository)
16
14
  return null;
@@ -29,21 +27,21 @@ const parseGitHubRepo = (repository) => {
29
27
  if (!repository || typeof repository.url !== 'string')
30
28
  return null;
31
29
  const url = repository.url;
32
- const httpsMatch = url.match(GITHUB_HTTPS_URL_PATTERN);
30
+ const httpsMatch = url.match(constants.FS_PATTERNS.GITHUB_HTTPS_URL);
33
31
  if (httpsMatch)
34
32
  return {
35
33
  owner: httpsMatch[1],
36
34
  repo: httpsMatch[2],
37
35
  directory: repository.directory,
38
36
  };
39
- const sshMatch = url.match(GITHUB_SSH_URL_PATTERN);
37
+ const sshMatch = url.match(constants.FS_PATTERNS.GITHUB_SSH_URL);
40
38
  if (sshMatch)
41
39
  return {
42
40
  owner: sshMatch[1],
43
41
  repo: sshMatch[2],
44
42
  directory: repository.directory,
45
43
  };
46
- const shorthandMatch = url.match(GITHUB_SHORTHAND_PATTERN);
44
+ const shorthandMatch = url.match(constants.FS_PATTERNS.GITHUB_SHORTHAND);
47
45
  if (shorthandMatch)
48
46
  return {
49
47
  owner: shorthandMatch[1],
@@ -70,10 +68,10 @@ const findGitRoot = (cwd = process.cwd()) => {
70
68
  const findWorkspaceRoot = (startDir = process.cwd()) => {
71
69
  let currentDir = startDir;
72
70
  while (currentDir !== '/') {
73
- const packageJsonPath = node_path.join(currentDir, 'package.json');
74
- if (node_fs.existsSync(packageJsonPath)) {
71
+ const packageJsonPath = path.join(currentDir, 'package.json');
72
+ if (fs.existsSync(packageJsonPath)) {
75
73
  try {
76
- const content = node_fs.readFileSync(packageJsonPath, 'utf-8');
74
+ const content = fs.readFileSync(packageJsonPath, 'utf-8');
77
75
  const json = JSON.parse(content);
78
76
  if (json.workspaces)
79
77
  return currentDir;
@@ -81,7 +79,7 @@ const findWorkspaceRoot = (startDir = process.cwd()) => {
81
79
  catch {
82
80
  }
83
81
  }
84
- currentDir = node_path.dirname(currentDir);
82
+ currentDir = path.dirname(currentDir);
85
83
  }
86
84
  return null;
87
85
  };
@@ -105,7 +103,7 @@ const getWorkspaceList = (workspaceRoot) => {
105
103
  const findWorkspaceLocation = (packageName, workspaceRoot) => {
106
104
  const workspaces = getWorkspaceList(workspaceRoot);
107
105
  const workspace = workspaces.find((ws) => ws.name === packageName);
108
- return workspace ? node_path.join(workspaceRoot, workspace.location) : null;
106
+ return workspace ? path.join(workspaceRoot, workspace.location) : null;
109
107
  };
110
108
  const readLocalPackageJson = (packageName, cwd = process.cwd()) => {
111
109
  try {
@@ -115,8 +113,8 @@ const readLocalPackageJson = (packageName, cwd = process.cwd()) => {
115
113
  const packageLocation = findWorkspaceLocation(packageName, workspaceRoot);
116
114
  if (!packageLocation)
117
115
  return null;
118
- const packageJsonPath = node_path.join(packageLocation, 'package.json');
119
- const content = node_fs.readFileSync(packageJsonPath, 'utf-8');
116
+ const packageJsonPath = path.join(packageLocation, 'package.json');
117
+ const content = fs.readFileSync(packageJsonPath, 'utf-8');
120
118
  const json = JSON.parse(content);
121
119
  if (!json.name || !json.version || !json.repository)
122
120
  return null;
@@ -131,12 +129,19 @@ const readLocalPackageJson = (packageName, cwd = process.cwd()) => {
131
129
  return null;
132
130
  }
133
131
  };
132
+ function getAssetTypes(config) {
133
+ if (!config.assets) {
134
+ return Array.from(constants.DEFAULT_ASSET_TYPES);
135
+ }
136
+ return Object.keys(config.assets);
137
+ }
134
138
 
135
139
  exports.buildAssetPath = buildAssetPath;
136
140
  exports.buildVersionTag = buildVersionTag;
137
141
  exports.findGitRoot = findGitRoot;
138
142
  exports.findWorkspaceLocation = findWorkspaceLocation;
139
143
  exports.findWorkspaceRoot = findWorkspaceRoot;
144
+ exports.getAssetTypes = getAssetTypes;
140
145
  exports.getWorkspaceList = getWorkspaceList;
141
146
  exports.parseGitHubRepo = parseGitHubRepo;
142
147
  exports.readLocalPackageJson = readLocalPackageJson;
@@ -1,4 +1,5 @@
1
1
  import type { GitHubRepoInfo, PackageInfo, WorkspaceInfo } from './types';
2
+ export { getAssetStructure } from '../core/assetStructure';
2
3
  /**
3
4
  * Read package.json from node_modules
4
5
  * @param packageName - Package name (e.g., "@canard/schema-form")
@@ -64,3 +65,27 @@ export declare const findWorkspaceLocation: (packageName: string, workspaceRoot:
64
65
  * @returns PackageInfo or null if not found
65
66
  */
66
67
  export declare const readLocalPackageJson: (packageName: string, cwd?: string) => PackageInfo | null;
68
+ /**
69
+ * Parse assets configuration from package.json with defaults
70
+ * @param config - ClaudeConfig from package.json
71
+ * @returns Normalized AssetsConfig with defaults
72
+ */
73
+ export declare function parseAssetsConfig(config: {
74
+ assetPath: string;
75
+ assets?: Record<string, {
76
+ structure: 'nested' | 'flat';
77
+ }>;
78
+ }): Record<string, {
79
+ structure: 'nested' | 'flat';
80
+ }>;
81
+ /**
82
+ * Get list of asset types to sync from configuration
83
+ * @param config - ClaudeConfig from package.json
84
+ * @returns Array of asset type names
85
+ */
86
+ export declare function getAssetTypes(config: {
87
+ assetPath: string;
88
+ assets?: Record<string, {
89
+ structure: 'nested' | 'flat';
90
+ }>;
91
+ }): string[];
@@ -1,10 +1,8 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { readFileSync, existsSync } from 'node:fs';
3
3
  import { join, dirname } from 'node:path';
4
+ import { FS_PATTERNS, DEFAULT_ASSET_TYPES } from '../core/constants.mjs';
4
5
 
5
- const GITHUB_HTTPS_URL_PATTERN = /https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/;
6
- const GITHUB_SSH_URL_PATTERN = /git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/;
7
- const GITHUB_SHORTHAND_PATTERN = /^github:([^/]+)\/([^/]+)$/;
8
6
  const readPackageJson = (packageName, cwd = process.cwd()) => {
9
7
  try {
10
8
  const packageJsonPath = join(cwd, 'node_modules', packageName, 'package.json');
@@ -27,21 +25,21 @@ const parseGitHubRepo = (repository) => {
27
25
  if (!repository || typeof repository.url !== 'string')
28
26
  return null;
29
27
  const url = repository.url;
30
- const httpsMatch = url.match(GITHUB_HTTPS_URL_PATTERN);
28
+ const httpsMatch = url.match(FS_PATTERNS.GITHUB_HTTPS_URL);
31
29
  if (httpsMatch)
32
30
  return {
33
31
  owner: httpsMatch[1],
34
32
  repo: httpsMatch[2],
35
33
  directory: repository.directory,
36
34
  };
37
- const sshMatch = url.match(GITHUB_SSH_URL_PATTERN);
35
+ const sshMatch = url.match(FS_PATTERNS.GITHUB_SSH_URL);
38
36
  if (sshMatch)
39
37
  return {
40
38
  owner: sshMatch[1],
41
39
  repo: sshMatch[2],
42
40
  directory: repository.directory,
43
41
  };
44
- const shorthandMatch = url.match(GITHUB_SHORTHAND_PATTERN);
42
+ const shorthandMatch = url.match(FS_PATTERNS.GITHUB_SHORTHAND);
45
43
  if (shorthandMatch)
46
44
  return {
47
45
  owner: shorthandMatch[1],
@@ -129,5 +127,11 @@ const readLocalPackageJson = (packageName, cwd = process.cwd()) => {
129
127
  return null;
130
128
  }
131
129
  };
130
+ function getAssetTypes(config) {
131
+ if (!config.assets) {
132
+ return Array.from(DEFAULT_ASSET_TYPES);
133
+ }
134
+ return Object.keys(config.assets);
135
+ }
132
136
 
133
- export { buildAssetPath, buildVersionTag, findGitRoot, findWorkspaceLocation, findWorkspaceRoot, getWorkspaceList, parseGitHubRepo, readLocalPackageJson, readPackageJson };
137
+ export { buildAssetPath, buildVersionTag, findGitRoot, findWorkspaceLocation, findWorkspaceRoot, getAssetTypes, getWorkspaceList, parseGitHubRepo, readLocalPackageJson, readPackageJson };
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ function parsePackageName(packageName) {
4
+ if (packageName.startsWith('@')) {
5
+ const [scope, name] = packageName.split('/');
6
+ return [scope, name];
7
+ }
8
+ return ['', packageName];
9
+ }
10
+ function kebabToCamel(str) {
11
+ return str.replace(/-./g, (match) => match[1].toUpperCase());
12
+ }
13
+ function packageNameToPrefix(packageName) {
14
+ const [scope, name] = parsePackageName(packageName);
15
+ if (!scope) {
16
+ return kebabToCamel(name);
17
+ }
18
+ const scopeWithoutAt = scope.replace('@', '');
19
+ const camelName = kebabToCamel(name);
20
+ return `${scopeWithoutAt}-${camelName}`;
21
+ }
22
+
23
+ exports.packageNameToPrefix = packageNameToPrefix;
24
+ exports.parsePackageName = parsePackageName;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Parse scoped package name into scope and name
3
+ * @param packageName - Package name (e.g., "@canard/schema-form")
4
+ * @returns [scope, name] tuple (e.g., ["@canard", "schema-form"])
5
+ *
6
+ * @example
7
+ * parsePackageName('@canard/schema-form') // ['@canard', 'schema-form']
8
+ * parsePackageName('lodash') // ['', 'lodash']
9
+ */
10
+ export declare function parsePackageName(packageName: string): [string, string];
11
+ /**
12
+ * Converts a scoped package name to a flat prefix
13
+ * @param packageName - Scoped package name (e.g., '@canard/schema-form')
14
+ * @returns Flat prefix (e.g., 'canard-schemaForm')
15
+ *
16
+ * @example
17
+ * packageNameToPrefix('@canard/schema-form') // 'canard-schemaForm'
18
+ * packageNameToPrefix('@winglet/react-utils') // 'winglet-reactUtils'
19
+ * packageNameToPrefix('lodash') // 'lodash'
20
+ */
21
+ export declare function packageNameToPrefix(packageName: string): string;
22
+ /**
23
+ * Converts a flat prefix back to package name
24
+ * @param prefix - Flat prefix (e.g., 'canard-schemaForm')
25
+ * @returns Package name (e.g., '@canard/schema-form')
26
+ *
27
+ * @example
28
+ * prefixToPackageName('canard-schemaForm') // '@canard/schema-form'
29
+ * prefixToPackageName('winglet-reactUtils') // '@winglet/react-utils'
30
+ * prefixToPackageName('lodash') // 'lodash'
31
+ */
32
+ export declare function prefixToPackageName(prefix: string): string;
@@ -0,0 +1,21 @@
1
+ function parsePackageName(packageName) {
2
+ if (packageName.startsWith('@')) {
3
+ const [scope, name] = packageName.split('/');
4
+ return [scope, name];
5
+ }
6
+ return ['', packageName];
7
+ }
8
+ function kebabToCamel(str) {
9
+ return str.replace(/-./g, (match) => match[1].toUpperCase());
10
+ }
11
+ function packageNameToPrefix(packageName) {
12
+ const [scope, name] = parsePackageName(packageName);
13
+ if (!scope) {
14
+ return kebabToCamel(name);
15
+ }
16
+ const scopeWithoutAt = scope.replace('@', '');
17
+ const camelName = kebabToCamel(name);
18
+ return `${scopeWithoutAt}-${camelName}`;
19
+ }
20
+
21
+ export { packageNameToPrefix, parsePackageName };
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var path = require('node:path');
4
+ var constants = require('../core/constants.cjs');
5
+ var packageName = require('./packageName.cjs');
6
+
7
+ function getDestinationDir(cwd, packageName$1, assetType) {
8
+ const [scope, name] = packageName.parsePackageName(packageName$1);
9
+ if (scope)
10
+ return path.join(cwd, constants.CLAUDE_BASE_DIR, assetType, scope, name);
11
+ return path.join(cwd, constants.CLAUDE_BASE_DIR, assetType, name);
12
+ }
13
+ function getFlatDestinationDir(cwd, assetType) {
14
+ return path.join(cwd, constants.CLAUDE_BASE_DIR, assetType);
15
+ }
16
+
17
+ exports.getDestinationDir = getDestinationDir;
18
+ exports.getFlatDestinationDir = getFlatDestinationDir;
@@ -0,0 +1,55 @@
1
+ import type { AssetType } from './types';
2
+ /**
3
+ * Get the destination directory for synced assets (nested structure)
4
+ * @param cwd - Current working directory
5
+ * @param packageName - Package name
6
+ * @param assetType - Asset type (commands or skills)
7
+ * @returns Full path to destination directory
8
+ *
9
+ * @example
10
+ * getDestinationDir('/project', '@canard/schema-form', 'commands')
11
+ * // => '/project/.claude/commands/@canard/schema-form'
12
+ */
13
+ export declare function getDestinationDir(cwd: string, packageName: string, assetType: AssetType): string;
14
+ /**
15
+ * Get the flat destination directory for synced assets (no nesting)
16
+ * @param cwd - Current working directory
17
+ * @param assetType - Asset type (commands or skills)
18
+ * @returns Full path to flat destination directory
19
+ *
20
+ * @example
21
+ * getFlatDestinationDir('/project', 'commands')
22
+ * // => '/project/.claude/commands'
23
+ */
24
+ export declare function getFlatDestinationDir(cwd: string, assetType: AssetType): string;
25
+ /**
26
+ * Get the path to sync metadata file for nested structure
27
+ * @param cwd - Current working directory
28
+ * @param packageName - Package name
29
+ * @param assetType - Asset type
30
+ * @returns Full path to .sync-meta.json
31
+ */
32
+ export declare function getMetaFilePath(cwd: string, packageName: string, assetType: AssetType): string;
33
+ /**
34
+ * Get the path to unified sync metadata file
35
+ * @param cwd - Current working directory
36
+ * @returns Full path to .claude/.sync-meta.json
37
+ */
38
+ export declare function getUnifiedMetaFilePath(cwd: string): string;
39
+ /**
40
+ * Get the path to an asset file in nested structure
41
+ * @param cwd - Current working directory
42
+ * @param packageName - Package name
43
+ * @param assetType - Asset type
44
+ * @param fileName - File name
45
+ * @returns Full path to asset file
46
+ */
47
+ export declare function getAssetFilePath(cwd: string, packageName: string, assetType: AssetType, fileName: string): string;
48
+ /**
49
+ * Get the path to an asset file in flat structure
50
+ * @param cwd - Current working directory
51
+ * @param assetType - Asset type
52
+ * @param flatFileName - Flat file name with prefix
53
+ * @returns Full path to flat asset file
54
+ */
55
+ export declare function getFlatAssetFilePath(cwd: string, assetType: AssetType, flatFileName: string): string;
@@ -0,0 +1,15 @@
1
+ import { join } from 'node:path';
2
+ import { CLAUDE_BASE_DIR } from '../core/constants.mjs';
3
+ import { parsePackageName } from './packageName.mjs';
4
+
5
+ function getDestinationDir(cwd, packageName, assetType) {
6
+ const [scope, name] = parsePackageName(packageName);
7
+ if (scope)
8
+ return join(cwd, CLAUDE_BASE_DIR, assetType, scope, name);
9
+ return join(cwd, CLAUDE_BASE_DIR, assetType, name);
10
+ }
11
+ function getFlatDestinationDir(cwd, assetType) {
12
+ return join(cwd, CLAUDE_BASE_DIR, assetType);
13
+ }
14
+
15
+ export { getDestinationDir, getFlatDestinationDir };
@@ -1,9 +1,27 @@
1
+ /**
2
+ * Asset structure type (nested or flat)
3
+ */
4
+ export type AssetStructure = 'nested' | 'flat';
5
+ /**
6
+ * Configuration for a specific asset type
7
+ */
8
+ export interface AssetTypeConfig {
9
+ structure: AssetStructure;
10
+ }
11
+ /**
12
+ * Configuration mapping asset types to their structure
13
+ */
14
+ export interface AssetsConfig {
15
+ [assetType: string]: AssetTypeConfig;
16
+ }
1
17
  /**
2
18
  * Claude configuration in package.json
3
19
  */
4
20
  export interface ClaudeConfig {
5
21
  /** Path to Claude assets directory (e.g., "docs/claude") */
6
22
  assetPath: string;
23
+ /** Optional configuration for asset types */
24
+ assets?: AssetsConfig;
7
25
  }
8
26
  /**
9
27
  * Repository information from package.json
@@ -52,9 +70,9 @@ export interface SyncMeta {
52
70
  files: string[];
53
71
  }
54
72
  /**
55
- * Asset type (commands or skills)
73
+ * Asset type (can be any string, not limited to commands/skills/agents)
56
74
  */
57
- export type AssetType = 'commands' | 'skills';
75
+ export type AssetType = string;
58
76
  /**
59
77
  * Sync result for a single package
60
78
  */
@@ -63,10 +81,7 @@ export interface SyncResult {
63
81
  success: boolean;
64
82
  skipped: boolean;
65
83
  reason?: string;
66
- syncedFiles?: {
67
- commands: string[];
68
- skills: string[];
69
- };
84
+ syncedFiles?: Record<string, string[]>;
70
85
  }
71
86
  /**
72
87
  * CLI options
@@ -78,6 +93,8 @@ export interface CliOptions {
78
93
  local: boolean;
79
94
  /** Custom git ref (branch, tag, or commit) to fetch from */
80
95
  ref?: string;
96
+ /** Use flat structure (default: true in v2) */
97
+ flat?: boolean;
81
98
  }
82
99
  /**
83
100
  * Workspace info from yarn workspaces
@@ -86,3 +103,133 @@ export interface WorkspaceInfo {
86
103
  name: string;
87
104
  location: string;
88
105
  }
106
+ /**
107
+ * Unified sync metadata for all packages (flat structure)
108
+ */
109
+ export interface UnifiedSyncMeta {
110
+ /** Schema version for migration support */
111
+ schemaVersion: string;
112
+ /** Last sync timestamp */
113
+ syncedAt: string;
114
+ /** Package metadata keyed by transformed name (e.g., "canard-schemaForm") */
115
+ packages: Record<string, PackageSyncInfo>;
116
+ }
117
+ /**
118
+ * Individual package sync info within unified meta
119
+ */
120
+ export interface PackageSyncInfo {
121
+ /** Original package name (e.g., "@canard/schema-form") */
122
+ originalName: string;
123
+ /** Package version at sync time */
124
+ version: string;
125
+ /** Whether this package is from local workspace (true) or remote npm (false/undefined) */
126
+ local?: boolean;
127
+ /** Synced files - dynamic structure based on asset configuration:
128
+ * - For nested structure: array of original filenames
129
+ * - For flat structure: array of FileMapping with original → transformed
130
+ */
131
+ files: Record<string, string[] | FileMapping[]>;
132
+ /** Optional exclusions for selective syncing */
133
+ exclusions?: {
134
+ /** Excluded directory paths (e.g., ["skills/deprecated"]) */
135
+ directories: string[];
136
+ /** Excluded file paths (e.g., ["commands/old-cmd.md"]) */
137
+ files: string[];
138
+ };
139
+ }
140
+ /**
141
+ * File name mapping (original to transformed)
142
+ */
143
+ export interface FileMapping {
144
+ /** Original file name (e.g., "guide.md") */
145
+ original: string;
146
+ /** Transformed file name (e.g., "canard-schemaForm_guide.md") */
147
+ transformed: string;
148
+ }
149
+ /**
150
+ * Add command selection result
151
+ */
152
+ export interface AddCommandSelection {
153
+ /** Package name */
154
+ packageName: string;
155
+ /** Source type */
156
+ source: 'local' | 'npm';
157
+ /** Git ref (branch, tag, or commit) */
158
+ ref?: string;
159
+ /** Included assets by type */
160
+ includedAssets: Record<string, string[]>;
161
+ /** Excluded assets by type */
162
+ excludedAssets: Record<string, string[]>;
163
+ }
164
+ /**
165
+ * Tree node for asset selection
166
+ */
167
+ export interface TreeNode {
168
+ /** Unique identifier */
169
+ id: string;
170
+ /** Node label */
171
+ label: string;
172
+ /** Full path */
173
+ path: string;
174
+ /** Node type */
175
+ type: 'directory' | 'file' | 'skill-directory';
176
+ /** Child nodes */
177
+ children?: TreeNode[];
178
+ /** Selection state */
179
+ selected: boolean;
180
+ /** Expanded state */
181
+ expanded: boolean;
182
+ /** Disabled state */
183
+ disabled?: boolean;
184
+ /** Additional metadata */
185
+ metadata?: {
186
+ assetType: string;
187
+ originalName?: string;
188
+ transformedName?: string;
189
+ };
190
+ }
191
+ /**
192
+ * Re-export asset structure utilities from core module
193
+ *
194
+ * @deprecated These exports are kept for backward compatibility only.
195
+ * Please update your imports to use the recommended paths below.
196
+ *
197
+ * Migration Guide:
198
+ * ================
199
+ *
200
+ * ❌ OLD (Deprecated - will be removed in v1.0.0)
201
+ * ```typescript
202
+ * import {
203
+ * DEFAULT_ASSET_TYPES,
204
+ * DEFAULT_ASSET_STRUCTURES,
205
+ * getAssetStructure,
206
+ * detectStructureType,
207
+ * validateAssetStructure,
208
+ * normalizeAssetStructure,
209
+ * } from './utils/types';
210
+ * ```
211
+ *
212
+ * ✅ NEW (Recommended)
213
+ * ```typescript
214
+ * import {
215
+ * DEFAULT_ASSET_TYPES,
216
+ * DEFAULT_ASSET_STRUCTURES,
217
+ * getAssetStructure,
218
+ * detectStructureType,
219
+ * validateAssetStructure,
220
+ * normalizeAssetStructure,
221
+ * } from './core/assetStructure';
222
+ * ```
223
+ *
224
+ * Why the change?
225
+ * - Clearer module organization (asset structure logic lives in core/)
226
+ * - Direct imports are more maintainable and tree-shake friendly
227
+ * - Reduces coupling between utils and core modules
228
+ *
229
+ * Timeline:
230
+ * - v0.x: Old imports work (with deprecation warnings)
231
+ * - v1.0.0: Re-exports removed
232
+ *
233
+ * @see ../core/assetStructure for the actual implementations
234
+ */
235
+ export { DEFAULT_ASSET_TYPES, DEFAULT_ASSET_STRUCTURES, getAssetStructure, detectStructureType, validateAssetStructure, normalizeAssetStructure, } from '../core/assetStructure';
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ function compareVersions(v1, v2) {
4
+ return v1 === v2;
5
+ }
6
+ function needsVersionSync(currentVersion, syncedVersion) {
7
+ if (syncedVersion === undefined) {
8
+ return true;
9
+ }
10
+ if (currentVersion === '' || syncedVersion === '') {
11
+ return true;
12
+ }
13
+ return !compareVersions(currentVersion, syncedVersion);
14
+ }
15
+
16
+ exports.compareVersions = compareVersions;
17
+ exports.needsVersionSync = needsVersionSync;