🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@lucassantana/sharekit

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lucassantana/sharekit - npm Package Compare versions

Comparing version
0.2.0
to
0.2.1
+8
-8
dist/index.js
#!/usr/bin/env node
import kleur from 'kleur';
import { install, preview, rollback, init } from './sharekit.js';
const VERSION = '0.2.0';
const VERSION = '0.2.1';
const USAGE = `${kleur.bold('sharekit')} v${VERSION} — share your AI coding setup

@@ -34,13 +34,13 @@

}
const arg = rest[0];
// Separate flags from positionals so flags work in any position
const flags = rest.filter((x) => x.startsWith('--'));
const arg = rest.find((x) => !x.startsWith('--'));
if (!arg) {
console.error(kleur.red(`usage: sharekit ${cmd} <user>${cmd === 'install' ? '[@<ref>]' : ''}`));
const showRef = cmd === 'install' || cmd === 'preview';
console.error(kleur.red(`usage: sharekit ${cmd} <user>${showRef ? '[@<ref>]' : ''}`));
process.exit(1);
}
// Parse options for install command
const opts = {};
if (cmd === 'install') {
if (rest.includes('--include-hooks')) {
opts.includeHooks = true;
}
if (cmd === 'install' && flags.includes('--include-hooks')) {
opts.includeHooks = true;
}

@@ -47,0 +47,0 @@ await fn(arg, opts);

@@ -9,2 +9,9 @@ type Status = 'new' | 'changed' | 'same';

}
type Dirs = {
home: string;
state: string;
};
export interface InstallOpts {
includeHooks?: boolean;
}
export declare function fetchProfile(user: string, ref?: string, baseUrl?: string, cacheRoot?: string): string;

@@ -18,10 +25,9 @@ export declare function readManifest(profileDir: string): {

export declare function printPlan(files: PlanFile[], manifest: ReturnType<typeof readManifest>): void;
export declare function applyProfile(files: PlanFile[], user: string, includeHooks?: boolean): {
export declare function pruneBackups(user: string, state?: string): void;
export declare function applyProfile(files: PlanFile[], user: string, includeHooks?: boolean, dirs?: Dirs): {
backupDir: string;
filesWritten: number;
};
export declare function restoreBackup(user: string): void;
export declare function install(user: string, opts?: {
includeHooks?: boolean;
}): Promise<void>;
export declare function restoreBackup(user: string, dirs?: Dirs): void;
export declare function install(user: string, opts?: InstallOpts): Promise<void>;
export declare function preview(user: string): Promise<void>;

@@ -28,0 +34,0 @@ export declare function rollback(user: string): Promise<void>;

@@ -6,3 +6,3 @@ import * as fs from 'node:fs';

import { stdin as input, stdout as output } from 'node:process';
import { execSync } from 'node:child_process';
import { execFileSync } from 'node:child_process';
import TOML from '@iarna/toml';

@@ -18,2 +18,3 @@ import kleur from 'kleur';

};
const DEFAULT_DIRS = { home: HOME, state: STATE };
const tildify = (p) => (p.startsWith(HOME) ? '~' + p.slice(HOME.length) : p);

@@ -42,3 +43,3 @@ function walk(dir) {

try {
execSync(`git -C "${dir}" pull --ff-only`, { stdio: 'pipe' });
execFileSync('git', ['-C', dir, 'pull', '--ff-only'], { stdio: 'pipe' });
}

@@ -55,14 +56,16 @@ catch {

if (ref) {
execSync(`git clone --depth 1 --branch "${ref}" "${url}" "${dir}"`, { stdio: 'pipe' });
execFileSync('git', ['clone', '--depth', '1', '--branch', ref, '--', url, dir], {
stdio: 'pipe',
});
}
else {
execSync(`git clone --depth 1 "${url}" "${dir}"`, { stdio: 'pipe' });
execFileSync('git', ['clone', '--depth', '1', '--', url, dir], { stdio: 'pipe' });
}
}
catch (e) {
if (e.status === 127) {
if (e.code === 'ENOENT') {
throw new Error('git not found — install git to use sharekit (https://git-scm.com)');
}
const errMsg = e.message || '';
if (ref && errMsg.includes('not found in')) {
const errOut = (e.stderr?.toString() ?? '') + (e.message ?? '');
if (ref && errOut.includes('not found')) {
throw new Error(`ref '${ref}' not found in ${user}'s profile`);

@@ -142,4 +145,4 @@ }

// Prune backups, keeping only the most recent 5
function pruneBackups(user) {
const root = path.join(STATE, 'backups');
export function pruneBackups(user, state = STATE) {
const root = path.join(state, 'backups');
if (!fs.existsSync(root))

@@ -158,9 +161,9 @@ return;

}
function backup(files, user, includeHooks = false) {
function backup(files, user, includeHooks = false, dirs = DEFAULT_DIRS) {
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
const dir = path.join(STATE, 'backups', `${user}-${stamp}`);
const dir = path.join(dirs.state, 'backups', `${user}-${stamp}`);
const applied = files.filter((f) => f.status !== 'same' && !isExecutable(f, includeHooks));
fs.mkdirSync(dir, { recursive: true });
for (const f of applied.filter((f) => f.status === 'changed')) {
const t = path.join(dir, path.relative(HOME, f.dest));
const t = path.join(dir, path.relative(dirs.home, f.dest));
fs.mkdirSync(path.dirname(t), { recursive: true });

@@ -184,10 +187,10 @@ cp(f.dest, t);

// Exported pure functions for testability
export function applyProfile(files, user, includeHooks = false) {
const backupDir = backup(files, user, includeHooks);
export function applyProfile(files, user, includeHooks = false, dirs = DEFAULT_DIRS) {
const backupDir = backup(files, user, includeHooks, dirs);
const filesWritten = write(files, includeHooks);
pruneBackups(user);
pruneBackups(user, dirs.state);
return { backupDir, filesWritten };
}
export function restoreBackup(user) {
const root = path.join(STATE, 'backups');
export function restoreBackup(user, dirs = DEFAULT_DIRS) {
const root = path.join(dirs.state, 'backups');
const last = fs.existsSync(root)

@@ -208,3 +211,3 @@ ? fs

else {
const src = path.join(dir, path.relative(HOME, a.dest));
const src = path.join(dir, path.relative(dirs.home, a.dest));
if (fs.existsSync(src)) {

@@ -211,0 +214,0 @@ fs.mkdirSync(path.dirname(a.dest), { recursive: true }); // dest dir may have been removed since install

{
"name": "@lucassantana/sharekit",
"version": "0.2.0",
"version": "0.2.1",
"description": "Share your AI coding setup with anyone — one command to install, one to rollback.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -18,2 +18,21 @@ # sharekit

### Pin to a version
Install a specific tag or branch of a profile instead of the latest:
```bash
npx @lucassantana/sharekit install <github-user>@v1.0 # a tag
npx @lucassantana/sharekit install <github-user>@stable # a branch
```
Plain `install <github-user>` always tracks the profile's default branch (HEAD).
### Hooks
A profile's `.claude/settings.json` can define hooks that run shell commands, so it is **never installed by default** — it's flagged in the preview and skipped. To opt in after reviewing it, pass `--include-hooks` (you'll get a second explicit confirmation before it's written):
```bash
npx @lucassantana/sharekit install <github-user> --include-hooks
```
## Publish your own profile

@@ -20,0 +39,0 @@