@slats/claude-assets-sync 0.0.1

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.
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Logger utility with colored output
3
+ */
4
+ export declare const logger: {
5
+ /**
6
+ * Log info message
7
+ */
8
+ info(message: string): void;
9
+ /**
10
+ * Log success message
11
+ */
12
+ success(message: string): void;
13
+ /**
14
+ * Log warning message
15
+ */
16
+ warn(message: string): void;
17
+ /**
18
+ * Log error message
19
+ */
20
+ error(message: string): void;
21
+ /**
22
+ * Log debug message (only in verbose mode)
23
+ */
24
+ debug(message: string): void;
25
+ /**
26
+ * Log a step in the sync process
27
+ */
28
+ step(step: string, detail?: string): void;
29
+ /**
30
+ * Log file operation
31
+ */
32
+ file(operation: "create" | "update" | "skip", path: string): void;
33
+ /**
34
+ * Log package sync start
35
+ */
36
+ packageStart(packageName: string): void;
37
+ /**
38
+ * Log package sync result
39
+ */
40
+ packageEnd(_packageName: string, result: {
41
+ success: boolean;
42
+ skipped: boolean;
43
+ reason?: string;
44
+ }): void;
45
+ /**
46
+ * Log summary at the end
47
+ */
48
+ summary(results: {
49
+ success: number;
50
+ skipped: number;
51
+ failed: number;
52
+ }): void;
53
+ /**
54
+ * Log dry-run notice
55
+ */
56
+ dryRunNotice(): void;
57
+ };
@@ -0,0 +1,65 @@
1
+ import pc from 'picocolors';
2
+
3
+ const logger = {
4
+ info(message) {
5
+ console.log(pc.blue('info'), message);
6
+ },
7
+ success(message) {
8
+ console.log(pc.green('success'), message);
9
+ },
10
+ warn(message) {
11
+ console.log(pc.yellow('warn'), message);
12
+ },
13
+ error(message) {
14
+ console.log(pc.red('error'), message);
15
+ },
16
+ debug(message) {
17
+ if (process.env.VERBOSE) {
18
+ console.log(pc.gray('debug'), message);
19
+ }
20
+ },
21
+ step(step, detail) {
22
+ const stepText = pc.cyan(`[${step}]`);
23
+ console.log(stepText, detail || '');
24
+ },
25
+ file(operation, path) {
26
+ const colors = {
27
+ create: pc.green,
28
+ update: pc.yellow,
29
+ skip: pc.gray,
30
+ };
31
+ const symbols = {
32
+ create: '+',
33
+ update: '~',
34
+ skip: '-',
35
+ };
36
+ console.log(` ${colors[operation](symbols[operation])} ${path}`);
37
+ },
38
+ packageStart(packageName) {
39
+ console.log();
40
+ console.log(pc.bold(pc.cyan(`Syncing ${packageName}...`)));
41
+ },
42
+ packageEnd(_packageName, result) {
43
+ if (result.skipped)
44
+ console.log(pc.gray(` Skipped: ${result.reason || 'Unknown reason'}`));
45
+ else if (result.success)
46
+ console.log(pc.green(` Completed successfully`));
47
+ else
48
+ console.log(pc.red(` Failed: ${result.reason || 'Unknown error'}`));
49
+ },
50
+ summary(results) {
51
+ console.log();
52
+ console.log(pc.bold('Summary:'));
53
+ console.log(` ${pc.green('Success:')} ${results.success}`);
54
+ console.log(` ${pc.gray('Skipped:')} ${results.skipped}`);
55
+ if (results.failed > 0)
56
+ console.log(` ${pc.red('Failed:')} ${results.failed}`);
57
+ },
58
+ dryRunNotice() {
59
+ console.log();
60
+ console.log(pc.yellow(pc.bold('[DRY RUN] No files will be created or modified.')));
61
+ console.log();
62
+ },
63
+ };
64
+
65
+ export { logger };
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+
3
+ var node_child_process = require('node:child_process');
4
+ var node_fs = require('node:fs');
5
+ var node_path = require('node:path');
6
+
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
+ const readPackageJson = (packageName, cwd = process.cwd()) => {
11
+ try {
12
+ const packageJsonPath = node_path.join(cwd, 'node_modules', packageName, 'package.json');
13
+ const content = node_fs.readFileSync(packageJsonPath, 'utf-8');
14
+ const json = JSON.parse(content);
15
+ if (!json.name || !json.version || !json.repository)
16
+ return null;
17
+ return {
18
+ name: json.name,
19
+ version: json.version,
20
+ repository: json.repository,
21
+ claude: json.claude,
22
+ };
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ };
28
+ const parseGitHubRepo = (repository) => {
29
+ if (!repository || typeof repository.url !== 'string')
30
+ return null;
31
+ const url = repository.url;
32
+ const httpsMatch = url.match(GITHUB_HTTPS_URL_PATTERN);
33
+ if (httpsMatch)
34
+ return {
35
+ owner: httpsMatch[1],
36
+ repo: httpsMatch[2],
37
+ directory: repository.directory,
38
+ };
39
+ const sshMatch = url.match(GITHUB_SSH_URL_PATTERN);
40
+ if (sshMatch)
41
+ return {
42
+ owner: sshMatch[1],
43
+ repo: sshMatch[2],
44
+ directory: repository.directory,
45
+ };
46
+ const shorthandMatch = url.match(GITHUB_SHORTHAND_PATTERN);
47
+ if (shorthandMatch)
48
+ return {
49
+ owner: shorthandMatch[1],
50
+ repo: shorthandMatch[2],
51
+ directory: repository.directory,
52
+ };
53
+ return null;
54
+ };
55
+ const buildVersionTag = (packageName, version) => `${packageName}@${version}`;
56
+ const buildAssetPath = (assetPath, directory) => (assetPath);
57
+ const findGitRoot = (cwd = process.cwd()) => {
58
+ try {
59
+ const gitRoot = node_child_process.execSync('git rev-parse --show-toplevel', {
60
+ cwd,
61
+ encoding: 'utf-8',
62
+ stdio: ['pipe', 'pipe', 'pipe'],
63
+ }).trim();
64
+ return gitRoot;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ };
70
+ const findWorkspaceRoot = (startDir = process.cwd()) => {
71
+ let currentDir = startDir;
72
+ while (currentDir !== '/') {
73
+ const packageJsonPath = node_path.join(currentDir, 'package.json');
74
+ if (node_fs.existsSync(packageJsonPath)) {
75
+ try {
76
+ const content = node_fs.readFileSync(packageJsonPath, 'utf-8');
77
+ const json = JSON.parse(content);
78
+ if (json.workspaces)
79
+ return currentDir;
80
+ }
81
+ catch {
82
+ }
83
+ }
84
+ currentDir = node_path.dirname(currentDir);
85
+ }
86
+ return null;
87
+ };
88
+ const getWorkspaceList = (workspaceRoot) => {
89
+ try {
90
+ const output = node_child_process.execSync('yarn workspaces list --json', {
91
+ cwd: workspaceRoot,
92
+ encoding: 'utf-8',
93
+ stdio: ['pipe', 'pipe', 'pipe'],
94
+ });
95
+ return output
96
+ .trim()
97
+ .split('\n')
98
+ .filter(Boolean)
99
+ .map((line) => JSON.parse(line));
100
+ }
101
+ catch {
102
+ return [];
103
+ }
104
+ };
105
+ const findWorkspaceLocation = (packageName, workspaceRoot) => {
106
+ const workspaces = getWorkspaceList(workspaceRoot);
107
+ const workspace = workspaces.find((ws) => ws.name === packageName);
108
+ return workspace ? node_path.join(workspaceRoot, workspace.location) : null;
109
+ };
110
+ const readLocalPackageJson = (packageName, cwd = process.cwd()) => {
111
+ try {
112
+ const workspaceRoot = findWorkspaceRoot(cwd);
113
+ if (!workspaceRoot)
114
+ return null;
115
+ const packageLocation = findWorkspaceLocation(packageName, workspaceRoot);
116
+ if (!packageLocation)
117
+ return null;
118
+ const packageJsonPath = node_path.join(packageLocation, 'package.json');
119
+ const content = node_fs.readFileSync(packageJsonPath, 'utf-8');
120
+ const json = JSON.parse(content);
121
+ if (!json.name || !json.version || !json.repository)
122
+ return null;
123
+ return {
124
+ name: json.name,
125
+ version: json.version,
126
+ repository: json.repository,
127
+ claude: json.claude,
128
+ };
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ };
134
+
135
+ exports.buildAssetPath = buildAssetPath;
136
+ exports.buildVersionTag = buildVersionTag;
137
+ exports.findGitRoot = findGitRoot;
138
+ exports.findWorkspaceLocation = findWorkspaceLocation;
139
+ exports.findWorkspaceRoot = findWorkspaceRoot;
140
+ exports.getWorkspaceList = getWorkspaceList;
141
+ exports.parseGitHubRepo = parseGitHubRepo;
142
+ exports.readLocalPackageJson = readLocalPackageJson;
143
+ exports.readPackageJson = readPackageJson;
@@ -0,0 +1,66 @@
1
+ import type { GitHubRepoInfo, PackageInfo, WorkspaceInfo } from './types';
2
+ /**
3
+ * Read package.json from node_modules
4
+ * @param packageName - Package name (e.g., "@canard/schema-form")
5
+ * @param cwd - Current working directory
6
+ * @returns PackageInfo or null if not found
7
+ */
8
+ export declare const readPackageJson: (packageName: string, cwd?: string) => PackageInfo | null;
9
+ /**
10
+ * Parse GitHub repository URL to extract owner and repo
11
+ * Supports formats:
12
+ * - https://github.com/owner/repo.git
13
+ * - https://github.com/owner/repo
14
+ * - git@github.com:owner/repo.git
15
+ * - github:owner/repo
16
+ *
17
+ * @param repository - Repository info from package.json
18
+ * @returns GitHubRepoInfo or null if parsing failed
19
+ */
20
+ export declare const parseGitHubRepo: (repository: PackageInfo["repository"]) => GitHubRepoInfo | null;
21
+ /**
22
+ * Build version tag for GitHub
23
+ * @param packageName - Package name (e.g., "@canard/schema-form")
24
+ * @param version - Package version (e.g., "0.10.0")
25
+ * @returns Version tag (e.g., "@canard/schema-form@0.10.0")
26
+ */
27
+ export declare const buildVersionTag: (packageName: string, version: string) => string;
28
+ /**
29
+ * Build asset path for a package
30
+ * @param assetPath - Base asset path (e.g., "docs/claude")
31
+ * @param directory - Repository directory (optional)
32
+ * @returns Full asset path
33
+ */
34
+ export declare const buildAssetPath: (assetPath: string, directory?: string) => string;
35
+ /**
36
+ * Find git repository root directory
37
+ * @param cwd - Current working directory
38
+ * @returns Git root path or null if not in a git repository
39
+ */
40
+ export declare const findGitRoot: (cwd?: string) => string | null;
41
+ /**
42
+ * Find the workspace root directory by looking for package.json with workspaces
43
+ * @param startDir - Directory to start searching from
44
+ * @returns Workspace root path or null if not found
45
+ */
46
+ export declare const findWorkspaceRoot: (startDir?: string) => string | null;
47
+ /**
48
+ * Get list of workspaces using yarn workspaces list
49
+ * @param workspaceRoot - Workspace root directory
50
+ * @returns Array of WorkspaceInfo
51
+ */
52
+ export declare const getWorkspaceList: (workspaceRoot: string) => WorkspaceInfo[];
53
+ /**
54
+ * Find workspace location by package name
55
+ * @param packageName - Package name to find
56
+ * @param workspaceRoot - Workspace root directory
57
+ * @returns Workspace location path or null if not found
58
+ */
59
+ export declare const findWorkspaceLocation: (packageName: string, workspaceRoot: string) => string | null;
60
+ /**
61
+ * Read package.json from local workspace
62
+ * @param packageName - Package name (e.g., "@canard/schema-form")
63
+ * @param cwd - Current working directory
64
+ * @returns PackageInfo or null if not found
65
+ */
66
+ export declare const readLocalPackageJson: (packageName: string, cwd?: string) => PackageInfo | null;
@@ -0,0 +1,133 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ import { join, dirname } from 'node:path';
4
+
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
+ const readPackageJson = (packageName, cwd = process.cwd()) => {
9
+ try {
10
+ const packageJsonPath = join(cwd, 'node_modules', packageName, 'package.json');
11
+ const content = readFileSync(packageJsonPath, 'utf-8');
12
+ const json = JSON.parse(content);
13
+ if (!json.name || !json.version || !json.repository)
14
+ return null;
15
+ return {
16
+ name: json.name,
17
+ version: json.version,
18
+ repository: json.repository,
19
+ claude: json.claude,
20
+ };
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ };
26
+ const parseGitHubRepo = (repository) => {
27
+ if (!repository || typeof repository.url !== 'string')
28
+ return null;
29
+ const url = repository.url;
30
+ const httpsMatch = url.match(GITHUB_HTTPS_URL_PATTERN);
31
+ if (httpsMatch)
32
+ return {
33
+ owner: httpsMatch[1],
34
+ repo: httpsMatch[2],
35
+ directory: repository.directory,
36
+ };
37
+ const sshMatch = url.match(GITHUB_SSH_URL_PATTERN);
38
+ if (sshMatch)
39
+ return {
40
+ owner: sshMatch[1],
41
+ repo: sshMatch[2],
42
+ directory: repository.directory,
43
+ };
44
+ const shorthandMatch = url.match(GITHUB_SHORTHAND_PATTERN);
45
+ if (shorthandMatch)
46
+ return {
47
+ owner: shorthandMatch[1],
48
+ repo: shorthandMatch[2],
49
+ directory: repository.directory,
50
+ };
51
+ return null;
52
+ };
53
+ const buildVersionTag = (packageName, version) => `${packageName}@${version}`;
54
+ const buildAssetPath = (assetPath, directory) => (assetPath);
55
+ const findGitRoot = (cwd = process.cwd()) => {
56
+ try {
57
+ const gitRoot = execSync('git rev-parse --show-toplevel', {
58
+ cwd,
59
+ encoding: 'utf-8',
60
+ stdio: ['pipe', 'pipe', 'pipe'],
61
+ }).trim();
62
+ return gitRoot;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ };
68
+ const findWorkspaceRoot = (startDir = process.cwd()) => {
69
+ let currentDir = startDir;
70
+ while (currentDir !== '/') {
71
+ const packageJsonPath = join(currentDir, 'package.json');
72
+ if (existsSync(packageJsonPath)) {
73
+ try {
74
+ const content = readFileSync(packageJsonPath, 'utf-8');
75
+ const json = JSON.parse(content);
76
+ if (json.workspaces)
77
+ return currentDir;
78
+ }
79
+ catch {
80
+ }
81
+ }
82
+ currentDir = dirname(currentDir);
83
+ }
84
+ return null;
85
+ };
86
+ const getWorkspaceList = (workspaceRoot) => {
87
+ try {
88
+ const output = execSync('yarn workspaces list --json', {
89
+ cwd: workspaceRoot,
90
+ encoding: 'utf-8',
91
+ stdio: ['pipe', 'pipe', 'pipe'],
92
+ });
93
+ return output
94
+ .trim()
95
+ .split('\n')
96
+ .filter(Boolean)
97
+ .map((line) => JSON.parse(line));
98
+ }
99
+ catch {
100
+ return [];
101
+ }
102
+ };
103
+ const findWorkspaceLocation = (packageName, workspaceRoot) => {
104
+ const workspaces = getWorkspaceList(workspaceRoot);
105
+ const workspace = workspaces.find((ws) => ws.name === packageName);
106
+ return workspace ? join(workspaceRoot, workspace.location) : null;
107
+ };
108
+ const readLocalPackageJson = (packageName, cwd = process.cwd()) => {
109
+ try {
110
+ const workspaceRoot = findWorkspaceRoot(cwd);
111
+ if (!workspaceRoot)
112
+ return null;
113
+ const packageLocation = findWorkspaceLocation(packageName, workspaceRoot);
114
+ if (!packageLocation)
115
+ return null;
116
+ const packageJsonPath = join(packageLocation, 'package.json');
117
+ const content = readFileSync(packageJsonPath, 'utf-8');
118
+ const json = JSON.parse(content);
119
+ if (!json.name || !json.version || !json.repository)
120
+ return null;
121
+ return {
122
+ name: json.name,
123
+ version: json.version,
124
+ repository: json.repository,
125
+ claude: json.claude,
126
+ };
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ };
132
+
133
+ export { buildAssetPath, buildVersionTag, findGitRoot, findWorkspaceLocation, findWorkspaceRoot, getWorkspaceList, parseGitHubRepo, readLocalPackageJson, readPackageJson };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Claude configuration in package.json
3
+ */
4
+ export interface ClaudeConfig {
5
+ /** Path to Claude assets directory (e.g., "docs/claude") */
6
+ assetPath: string;
7
+ }
8
+ /**
9
+ * Repository information from package.json
10
+ */
11
+ export interface RepositoryInfo {
12
+ type: string;
13
+ url: string;
14
+ directory?: string;
15
+ }
16
+ /**
17
+ * Parsed package information
18
+ */
19
+ export interface PackageInfo {
20
+ name: string;
21
+ version: string;
22
+ repository: RepositoryInfo;
23
+ claude?: ClaudeConfig;
24
+ }
25
+ /**
26
+ * Parsed GitHub repository details
27
+ */
28
+ export interface GitHubRepoInfo {
29
+ owner: string;
30
+ repo: string;
31
+ directory?: string;
32
+ }
33
+ /**
34
+ * GitHub API file/directory entry
35
+ */
36
+ export interface GitHubEntry {
37
+ name: string;
38
+ path: string;
39
+ type: 'file' | 'dir';
40
+ download_url: string | null;
41
+ sha: string;
42
+ }
43
+ /**
44
+ * Sync metadata stored in .sync-meta.json
45
+ */
46
+ export interface SyncMeta {
47
+ /** Package version at sync time */
48
+ version: string;
49
+ /** ISO timestamp of last sync */
50
+ syncedAt: string;
51
+ /** List of synced file names */
52
+ files: string[];
53
+ }
54
+ /**
55
+ * Asset type (commands or skills)
56
+ */
57
+ export type AssetType = 'commands' | 'skills';
58
+ /**
59
+ * Sync result for a single package
60
+ */
61
+ export interface SyncResult {
62
+ packageName: string;
63
+ success: boolean;
64
+ skipped: boolean;
65
+ reason?: string;
66
+ syncedFiles?: {
67
+ commands: string[];
68
+ skills: string[];
69
+ };
70
+ }
71
+ /**
72
+ * CLI options
73
+ */
74
+ export interface CliOptions {
75
+ package: string[];
76
+ force: boolean;
77
+ dryRun: boolean;
78
+ local: boolean;
79
+ /** Custom git ref (branch, tag, or commit) to fetch from */
80
+ ref?: string;
81
+ }
82
+ /**
83
+ * Workspace info from yarn workspaces
84
+ */
85
+ export interface WorkspaceInfo {
86
+ name: string;
87
+ location: string;
88
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@slats/claude-assets-sync",
3
+ "version": "0.0.1",
4
+ "description": "CLI tool to sync Claude commands and skills from npm packages to your project's .claude directory",
5
+ "keywords": [
6
+ "claude",
7
+ "claude-code",
8
+ "cli",
9
+ "sync",
10
+ "commands",
11
+ "skills",
12
+ "assets"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/vincent-kk/albatrion.git",
17
+ "directory": "packages/slats/claude-assets-sync"
18
+ },
19
+ "license": "MIT",
20
+ "author": {
21
+ "name": "Vincent K. Kelvin",
22
+ "email": "lunox273@gmail.com"
23
+ },
24
+ "sideEffects": false,
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "source": "./src/index.ts",
30
+ "import": "./dist/index.mjs",
31
+ "require": "./dist/index.cjs"
32
+ }
33
+ },
34
+ "main": "dist/index.cjs",
35
+ "module": "dist/index.mjs",
36
+ "types": "dist/index.d.ts",
37
+ "bin": "./dist/index.mjs",
38
+ "files": [
39
+ "dist",
40
+ "README.md"
41
+ ],
42
+ "scripts": {
43
+ "build": "rollup -c && yarn build:types",
44
+ "build:publish:npm": "yarn build && yarn publish:npm",
45
+ "build:types": "tsc -p ./tsconfig.declarations.json && tsc-alias -p ./tsconfig.declarations.json",
46
+ "dev": "tsx src/index.ts",
47
+ "format": "prettier --write \"src/**/*.ts\"",
48
+ "lint": "eslint \"src/**/*.ts\"",
49
+ "publish:npm": "npm publish --access public",
50
+ "test": "vitest",
51
+ "version:major": "yarn version major",
52
+ "version:minor": "yarn version minor",
53
+ "version:patch": "yarn version patch"
54
+ },
55
+ "dependencies": {
56
+ "commander": "^12.1.0",
57
+ "picocolors": "^1.1.1"
58
+ }
59
+ }