npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

skymarshal

v2.2.0

Published

Comprehensive Bluesky/AT Protocol toolkit - authentication, content management, network/graph analysis, thread caching, engagement tracking, deletion workflows, media uploads, chat, analytics, backup, vision AI, and sentiment analysis

Readme

skymarshal

Comprehensive Bluesky/AT Protocol toolkit for TypeScript/JavaScript

skymarshal provides everything you need to build Bluesky applications: authentication, content management, network analysis, chat/DMs, notifications, profile management, posts, lists, feeds, media uploads, bot detection, backup, alt text generation, and sentiment analysis.

npm version License: MIT

Installation

npm install skymarshal @atproto/api

Quick Start

import {
  AuthManager,
  ContentManager,
  NetworkManager,
  ChatManager,
  NotificationManager,
  ProfileManager,
  PostManager,
  ListsManager,
  FeedsManager,
  MediaManager,
  AnalyticsManager,
  SearchManager,
} from 'skymarshal';

// Authenticate
const auth = new AuthManager();
await auth.login('myhandle.bsky.social', 'my-app-password');

// Content management
const content = new ContentManager(auth.agent);
const posts = await content.getPosts(auth.did!);
const likes = await content.getLikes(auth.did!);
const reposts = await content.getReposts(auth.did!);

// Create posts with rich text, images, and threads
const post = new PostManager(auth.agent);
await post.createPost({ text: 'Hello from skymarshal!' });
await post.createThread([
  { text: 'Thread part 1' },
  { text: 'Thread part 2' },
]);

// Upload media
const media = new MediaManager(auth.agent);
const blob = await media.uploadImage({
  data: imageBuffer,
  mimeType: 'image/jpeg',
  altText: 'A beautiful sunset',
});

// Network analysis
const network = new NetworkManager(auth.agent);
const mutuals = await network.getMutuals(auth.did!);
const nonFollowers = await network.getNonFollowers(auth.did!);
const followers = await network.getFollowers(auth.did!);
const following = await network.getFollowing(auth.did!);

// Graph operations
await network.follow('did:plc:example');
await network.unfollow('did:plc:example');
await network.block('did:plc:example');
await network.mute('did:plc:example');

// Chat/DMs
const chat = new ChatManager(auth.agent);
const convos = await chat.listConvos();
const messages = await chat.getMessages(convos[0].id);
await chat.sendMessage(convos[0].id, 'Hello!');
await chat.muteConvo(convos[0].id);

// Notifications
const notifications = new NotificationManager(auth.agent);
const { notifications: notifs } = await notifications.listNotifications();
const unread = await notifications.getUnreadCount();
await notifications.markAllRead();

// Filter notifications by type
const likesOnly = await notifications.listNotifications({ 
  filter: ['like'] 
});

// Profile management
const profile = new ProfileManager(auth.agent);
const myProfile = await profile.getProfile(auth.did!);
await profile.updateProfile({ 
  displayName: 'New Name', 
  description: 'Updated bio' 
});

// Lists
const lists = new ListsManager(auth.agent);
const myLists = await lists.getLists(auth.did!);
await lists.createList({ 
  name: 'My List', 
  purpose: 'curatelist',
  description: 'People I follow closely'
});
await lists.addMember(listUri, 'did:plc:example');

// Feeds
const feeds = new FeedsManager(auth.agent);
const timeline = await feeds.getTimeline();
const savedFeeds = await feeds.getSavedFeeds();
const popularFeeds = await feeds.searchFeeds('science');
await feeds.saveFeed(feedUri);
await feeds.pinFeed(feedUri);

// Search with advanced operators
const search = new SearchManager(auth.agent);
const results = await search.searchPosts({
  query: '"exact phrase" -exclude +required',
  sort: 'engagement',
});

// Bot detection & analytics
const analytics = new AnalyticsManager();
const analysis = analytics.analyzeAccount(profile);
console.log(`Bot score: ${analysis.botScore}`);
console.log(`Signals: ${analysis.signals.map(s => s.type).join(', ')}`);

// Batch analysis
const batchResults = analytics.batchAnalyze(mutuals);
const suspicious = batchResults.filter(a => a.botScore > 0.5);

Managers

| Manager | Description | |---------|-------------| | AuthManager | Session management, login/logout, token refresh, profile fetch | | ContentManager | Get posts, likes, reposts with engagement scoring and presets | | NetworkManager | Following/followers, mutuals, non-followers, follow/unfollow/block/mute | | ChatManager | List conversations, get/send messages, reactions, mute/unmute, leave | | NotificationManager | List notifications, mark read, unread count, filter by type | | ProfileManager | Get/update profile, avatar, banner, search profiles | | PostManager | Create posts/threads, delete, like/unlike, repost/unrepost | | ListsManager | Create/delete lists, add/remove members, mute/block lists | | FeedsManager | Timeline, custom feeds, save/pin/unpin feeds, search feeds | | MediaManager | Upload images/videos, create embeds, validate dimensions | | AnalyticsManager | Bot detection, engagement analysis, dead thread detection | | SearchManager | Keyword search with operators ("exact", -exclude, +required) |

Services

| Service | Description | |---------|-------------| | BackupService | CAR file export for account backup | | VisionService | Alt text generation (Ollama, OpenAI, Anthropic, xAI/Grok) | | SentimentService | VADER-based text sentiment analysis |

VisionService Providers

import { VisionService } from 'skymarshal';

// Local with Ollama (no API key needed)
const vision = new VisionService({ provider: 'ollama' });

// Cloud providers
const vision = new VisionService([
  { provider: 'openai', apiKey: process.env.OPENAI_API_KEY },
  { provider: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY },
  { provider: 'xai', apiKey: process.env.XAI_API_KEY },
]);

// Generate alt text
const result = await vision.generateAltText(imageUrl);
console.log(result.text);

// Detailed analysis
const analysis = await vision.analyzeImage(imageUrl, { detailed: true });
console.log(analysis.description);
console.log(analysis.objects);
console.log(analysis.accessibility.altText);

| Provider | Default Model | Notes | |----------|---------------|-------| | Ollama | llava-phi3 | Local/self-hosted | | OpenAI | gpt-4o-mini | Cloud API | | Anthropic | claude-3-haiku | Cloud API | | xAI/Grok | grok-2-vision-1212 | Cloud API |

Utilities

| Utility | Description | |---------|-------------| | EngagementCache | Age-based TTL caching (1hr recent, 6hr medium, 24hr old) | | PaginationHelper | Cursor-based pagination with batch fetching | | ExportHelper | CSV/JSON export for posts, followers, analytics | | DateUtils | Date formatting, relative time ("2 hours ago") | | BatchUtils | Batch processing with concurrency control | | UriUtils | AT URI parsing (extract DID, collection, rkey) |

Engagement Scoring

import { calculateEngagementScore, getEngagementPreset } from 'skymarshal';

// Score formula: likes + (2 × reposts) + (2.5 × replies)
const score = calculateEngagementScore(post);

// Presets based on user's average engagement
const preset = getEngagementPreset(score, userAverageScore);
// Returns: 'dead' | 'low' | 'mid' | 'banger' | 'viral'

Subpath Imports

Import only what you need for smaller bundle sizes:

import { AuthManager } from 'skymarshal/managers';
import { VisionService } from 'skymarshal/services';
import { EngagementCache } from 'skymarshal/utils';

TypeScript Support

Full TypeScript support with exported types:

import type {
  Profile,
  Post,
  Notification,
  Conversation,
  Message,
  List,
  FeedGenerator,
  AccountAnalysis,
  BotSignal,
  EngagementPreset,
} from 'skymarshal';

Related Projects

License

MIT © Luke Steuber