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.3.1

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, sentiment analysis, real-time streaming (Je

Downloads

447

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, 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);

v2.2.0 Features

Thread Management

Fetch and analyze post threads with built-in caching:

import { fetchThread, flattenThread, PostCache, countThreadPosts } from 'skymarshal/threads';

// Thread fetching with cache (5-minute TTL)
const thread = await fetchThread(auth.agent, 'at://did:plc:xyz/app.bsky.feed.post/abc');

// Flatten nested thread for analysis
const allPosts = flattenThread(thread);
console.log(`Thread contains ${countThreadPosts(thread)} posts`);

// Get unique participants
import { getThreadAuthors } from 'skymarshal/threads';
const participants = getThreadAuthors(thread);

// Parse Bluesky URLs
import { parsePostUrl, resolvePostUrl } from 'skymarshal/threads';
const { handle, postId } = parsePostUrl('https://bsky.app/profile/user.bsky.social/post/abc123');
const atUri = await resolvePostUrl(auth.agent, 'https://bsky.app/profile/user.bsky.social/post/abc123');

Analytics & Scoring

Ported from Python (bluebeam, blueye, bluefry) with zero dependencies:

import {
  calculateEngagementScore,
  calculateEngagementRate,
  calculatePopularityScore,
  calculateCleanupScore,
  isLikelyBot,
  getCleanupPriority,
  classifyPostType,
} from 'skymarshal/analytics-utils';

// Engagement scoring (weighted: likes 1×, reposts 3×, replies 2×, quotes 4×)
const score = calculateEngagementScore({
  likes: 50, reposts: 8, replies: 10, quotes: 3
}); // = 50 + 24 + 20 + 12 = 106

// Engagement rate as percentage of followers
const rate = calculateEngagementRate(score, 1000); // 10.6%

// Popularity scoring (50% followers, 30% ratio, 20% activity)
const popularity = calculatePopularityScore({
  followers: 5000, following: 200, postsCount: 500
});

// Bot detection (9 heuristic signals, 0-100+ scale)
const botScore = calculateCleanupScore(profile);
const isBot = isLikelyBot(profile); // threshold: 80
const priority = getCleanupPriority(profile); // 'high' | 'medium' | 'low' | 'none'

// Post type classification
const type = classifyPostType(post); // 'photo' | 'video' | 'link' | 'long_text' | 'question' | 'text'

Graph Analysis

Pure TypeScript social graph analysis (no NetworkX dependency):

import {
  degreeCentrality,
  betweennessCentrality,
  calculatePageRank,
  detectCommunities,
  calculateModularity,
  networkDensity,
  averageClustering,
  computeGraphMetrics,
  orbitTier,
} from 'skymarshal/graph';

// Build graph from followers/following
const nodes = followers.map(f => ({ id: f.did, handle: f.handle }));
const edges = relationships.map(r => ({ source: r.from, target: r.to }));

// Centrality metrics
const degree = degreeCentrality(nodes, edges);
const betweenness = betweennessCentrality(nodes, edges);
const pagerank = calculatePageRank(nodes, edges, { damping: 0.85, iterations: 20 });

// Community detection (label propagation)
const communities = detectCommunities(nodes, edges);
const quality = calculateModularity(nodes, edges, communities);

// All-in-one analysis
const metrics = computeGraphMetrics(nodes, edges);
console.log(`Density: ${metrics.density}, Clustering: ${metrics.clustering}`);

// Orbit classification (0: >20 connections, 1: 5-20, 2: <5)
const tier = orbitTier(connectionCount);

Engagement Manager

TTL-based caching with intelligent refresh:

import { EngagementManager } from 'skymarshal/engagement';

const em = new EngagementManager(auth.agent, {
  // Dynamic TTL based on content age:
  // - Recent (<1 day): 1 hour cache
  // - Medium (1-7 days): 6 hour cache
  // - Old (>7 days): 24 hour cache
});

// Hydrate posts with fresh engagement metrics
const posts = await content.getPosts(auth.did!);
await em.hydrateItems(posts);

// Batch update with concurrency control (10 parallel max)
await em.batchUpdateEngagement(postUris);

// Check cache statistics
const stats = em.getStats();
console.log(`Cache hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);

Deletion Manager

Safe deletion workflows with backup support:

import { DeletionManager } from 'skymarshal/deletion';

const dm = new DeletionManager(auth.agent);

// Preview deletion (dry run)
const preview = await dm.previewDeletion(postUri);
console.log(`Will delete: ${preview.uri}`);

// Safe delete with options
await dm.safeDelete(postUri, {
  confirmationRequired: true,
  deleteFromRemote: true,
  createBackup: true,
});

// Batch delete with progress tracking
const result = await dm.batchDelete(postUris, {
  onProgress: (completed, total) => console.log(`${completed}/${total}`)
});
console.log(`Deleted: ${result.success}, Failed: ${result.failed}`);

// Parse AT-URIs
import { parseAtUri } from 'skymarshal/deletion';
const { repo, collection, rkey } = parseAtUri('at://did:plc:xyz/app.bsky.feed.post/abc');

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) | | EngagementManager | TTL-based engagement caching with age-aware expiry (v2.2.0) | | DeletionManager | Safe deletion workflows with backup and confirmation (v2.2.0) |

Services

| Service | Description | |---------|-------------| | BackupService | CAR file export for account backup | | VisionService | Alt text generation (Ollama, OpenAI, Anthropic) | | SentimentService | VADER-based text sentiment analysis | | JetstreamService | Real-time Bluesky firehose streaming via WebSocket |

VisionService

The VisionService provides image analysis and alt text generation using vision-capable language models. See documentation for full details.

JetstreamService - Real-time Firehose

Stream real-time posts, likes, follows, and other events from the Bluesky network:

import { JetstreamService } from 'skymarshal/jetstream';

const jetstream = new JetstreamService({
  wantedCollections: ['app.bsky.feed.post', 'app.bsky.feed.like'],
  reconnectOnError: true,
  maxReconnectAttempts: 5
});

// Connect to Jetstream
await jetstream.connect();

// Event listener pattern
jetstream.on('post', (post) => {
  console.log(`${post.authorHandle}: ${post.text}`);
});

jetstream.on('commit', (event) => {
  console.log('Commit:', event.commit.collection, event.commit.operation);
});

// Async iterator pattern
for await (const post of jetstream.streamPosts()) {
  console.log('New post:', post.text);
  if (shouldStop) break;
}

// Stream from specific user
for await (const event of jetstream.streamFromDid('did:plc:...')) {
  console.log('User action:', event.kind);
}

// Stream mentions of a handle
for await (const post of jetstream.streamMentions('alice.bsky.social')) {
  console.log('Mention:', post.text);
}

// Disconnect when done
await jetstream.disconnect();

Features:

  • Browser-compatible WebSocket (native API)
  • Auto-reconnect with exponential backoff
  • Filter by collections and DIDs at source
  • EventEmitter-style API with on/off methods
  • Async iterator support for streaming
  • DID → handle cache from identity events
  • Full TypeScript types

Available Collections:

  • app.bsky.feed.post - Posts
  • app.bsky.feed.like - Likes
  • app.bsky.feed.repost - Reposts
  • app.bsky.graph.follow - Follows
  • app.bsky.graph.block - Blocks
  • app.bsky.actor.profile - Profile updates

See JETSTREAM_USAGE.md for complete documentation and examples.

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) | | PostCache | Thread caching with 5-minute TTL (v2.2.0) | | GraphMetrics | Centrality, PageRank, community detection (v2.2.0) | | AnalyticsUtils | Engagement/popularity scoring, bot detection (v2.2.0) |

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:

// Managers
import { AuthManager } from 'skymarshal/auth';
import { ContentManager } from 'skymarshal/content';
import { NetworkManager } from 'skymarshal/network';
import { EngagementManager } from 'skymarshal/engagement';
import { DeletionManager } from 'skymarshal/deletion';

// Services
import { VisionService } from 'skymarshal/vision';
import { BackupService } from 'skymarshal/backup';
import { SentimentService } from 'skymarshal/sentiment';
import { JetstreamService } from 'skymarshal/jetstream';

// Utilities (v2.2.0)
import { fetchThread, PostCache, flattenThread } from 'skymarshal/threads';
import { degreeCentrality, calculatePageRank, detectCommunities } from 'skymarshal/graph';
import { calculateEngagementScore, isLikelyBot, classifyPostType } from 'skymarshal/analytics-utils';

// Database (browser IndexedDB)
import { IndexedDBProvider } from 'skymarshal/database';

// Validation
import { isValidAtUri, isValidHandle, validatePostText } from 'skymarshal/validation';

// Image processing (browser)
import { processImage, optimizeForBluesky } from 'skymarshal/image';

TypeScript Support

Full TypeScript support with exported types:

import type {
  // Core types
  Profile,
  Post,
  Notification,
  Conversation,
  Message,
  List,
  FeedGenerator,
  AccountAnalysis,
  BotSignal,
  EngagementPreset,

  // Thread types (v2.2.0)
  ThreadPost,

  // Graph types (v2.2.0)
  GraphNode,
  GraphEdge,
  Community,
  GraphMetrics,
  OrbitDistribution,

  // Analytics types (v2.2.0)
  PostEngagement,
  AccountMetrics,
  AccountProfile,
  CleanupResult,
  PostType,

  // Manager types (v2.2.0)
  EngagementMetrics,
  DeletionOptions,
  DeletionResult,
  ParsedAtUri,
} from 'skymarshal';

Related Projects

License

MIT © Luke Steuber