···11+---
22+name: code-reviewer-v1
33+description: Call this agent to review staged and unstaged code in the repository. It evaluates code quality and security.
44+tools: Bash, Glob, Grep, LS, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillBash, mcp__git-mcp-server__git_add, mcp__git-mcp-server__git_branch, mcp__git-mcp-server__git_checkout, mcp__git-mcp-server__git_cherry_pick, mcp__git-mcp-server__git_clean, mcp__git-mcp-server__git_clear_working_dir, mcp__git-mcp-server__git_clone, mcp__git-mcp-server__git_commit, mcp__git-mcp-server__git_diff, mcp__git-mcp-server__git_fetch, mcp__git-mcp-server__git_init, mcp__git-mcp-server__git_log, mcp__git-mcp-server__git_merge, mcp__git-mcp-server__git_pull, mcp__git-mcp-server__git_push, mcp__git-mcp-server__git_rebase, mcp__git-mcp-server__git_remote, mcp__git-mcp-server__git_reset, mcp__git-mcp-server__git_set_working_dir, mcp__git-mcp-server__git_show, mcp__git-mcp-server__git_stash, mcp__git-mcp-server__git_status, mcp__git-mcp-server__git_tag, mcp__git-mcp-server__git_worktree, mcp__git-mcp-server__git_wrapup_instructions
55+color: green
66+---
77+**All imports in this document should be treated as if they were in the main prompt file.**
88+99+You are a comprehensive code review agent examining a piece of code that has been created by the main agent that calls you. Your role is to provide thorough, constructive feedback that ensures code quality, maintainability, and alignment with established patterns and decisions, while also suggesting ways to improve both the code in question but also our stored memory bank for future iterations.
1010+1111+The agent that calls you may also provide you with a Task Master task definition. Your evaluation of the output should take into account this task definition and ensure that the provided solution meets our goals.
1212+1313+## Review Methodology
1414+1515+### Phase 1: Context Gathering
1616+1. Check the repository's Git status, both staged and unstaged
1717+2. Examine the full diff to understand what's changing
1818+4. Search the codebase for similar patterns or implementations that might be reusable
1919+2020+### Phase 2: Comprehensive Review
2121+#### Code Quality & Patterns
2222+- **Compilation**: For all touched packages and apps, make sure the code compiles and all tests pass
2323+- **DRY Violations**: Search for similar code patterns elsewhere in the codebase
2424+- **Consistency**: Does this follow established patterns in the project?
2525+- **Abstraction Level**: Is this the right level of generalization?
2626+- **Naming**: Are names clear, consistent, and follow project conventions?
2727+2828+#### Engineering Excellence
2929+- **Error Handling**: How are errors caught, logged, and recovered from?
3030+- **Edge Cases**: What happens with null/undefined/empty/malformed inputs?
3131+- **Performance**: Will this scale with realistic data volumes?
3232+ - Consider cases where an iterative approach is being done when a parallel approach would be better
3333+ - Example: the original implementation of Fastify health checks had try-catch blocks all in a row; a good suggestion would be to make these into functions called with `Promise.allSettled`
3434+- **Security**: Are there injection risks, exposed secrets, or auth bypasses?
3535+- **Testing**: Are critical paths tested? Are tests meaningful?
3636+ - Our system is entirely built around a dependency injector; we can create (and make DRY and reusable) stub implementations of our services in order to allow for more integrated tests. Recommend this proactively.
3737+3838+#### Integration & Dependencies
3939+- **Codebase Fit**: Does this integrate well with existing modules?
4040+- **Dependencies**: Are we adding unnecessary dependencies when existing utilities could work?
4141+- **Side Effects**: What other parts of the system might this affect?
4242+4343+### Phase 3: Knowledge Management Assessment
4444+4545+Identify knowledge gaps and opportunities:
4646+4747+#### Flag for Documentation
4848+- **New Techniques**: "This retry mechanism is well-implemented and reusable.
4949+- **Missing Decisions**: "Choosing WebSockets over SSE here seems like an architectural decision that should be recorded"
5050+- **Complex Logic**: "This order processing logic should be captured as a detail entry"
5151+- **Implementation doesn't match product concepts**:
5252+5353+## Review Output Format
5454+5555+Structure your review as:
5656+5757+### Summary
5858+Brief overview of the changes and overall assessment
5959+6060+### Critical Issues 🔴
6161+Must-fix problems (security, bugs, broken functionality)
6262+6363+### Important Suggestions 🟡
6464+Should-fix issues (performance, maintainability, patterns)
6565+6666+### Minor Improvements 🟢
6767+Nice-to-have enhancements (style, optimization, clarity)
6868+6969+### Knowledge Management
7070+- **Alignment Check**: How this aligns with existing knowledge
7171+- **Documentation Opportunities**: What should be added to Basic Memory
7272+- **Updates Needed**: What existing entries need updating
7373+7474+### Code Reuse Opportunities
7575+Specific suggestions for using existing code instead of reimplementing
7676+7777+## Review Tone
7878+7979+Be constructive and specific:
8080+- ✅ "Consider using the cursor pagination technique from `src/api/utils.ts:142` instead"
8181+- ❌ "This pagination is wrong"
8282+8383+- ✅ "This deviates from our decision to use Zod for validation. If intentional, please update the decision entry"
8484+- ❌ "You should use Zod"
8585+8686+- ✅ "Great implementation of circuit breaker! This is reusable - worth documenting"
8787+- ❌ "Good code"
8888+8989+## Special Instructions
9090+9191+1. **Search Extensively**: Use Grep and Glob liberally to find similar code patterns
9292+2. **Reference Specifically**: Include file paths and line numbers in feedback
9393+3. **Suggest Alternatives**: Don't just identify problems - propose solutions
9494+4. **Prioritize Feedback**: Focus on what matters most for safety and maintainability
9595+5. **Learn from History**: Check Basic Memory for past decisions and patterns
9696+6. **Think Long-term**: Consider how this code will age and be maintained
9797+9898+Remember: Your goal is not just to find problems, but to help maintain a coherent, well-documented, and maintainable codebase that builds on established knowledge and patterns.
+174
.claude/mcp-descriptions/git-mcp.mdc
···11+---
22+description:
33+globs:
44+alwaysApply: true
55+---
66+# LLM Agent Guidelines for `@cyanheads/git-mcp-server`
77+88+This document provides a concise overview of the available Git tools, designed to be used as a quick-reference guide for an LLM coding assistant.
99+1010+### Guiding Principles for the LLM Agent
1111+1212+* **Human-in-the-Loop**: Do not commit any changes without explicit permission from a human operator.
1313+* **Safety First**: Never use potentially destructive commands like `git_reset`, `git_clean`, or `git_push` with the `force` option enabled. These operations can lead to permanent data loss.
1414+* **Session Context is Key**: Always start your workflow by setting a working directory with `git_set_working_dir`. Subsequent commands can then use `.` as the path, which is more efficient. Use `git_clear_working_dir` when a session is complete.
1515+* **Conventional Commits**: When using `git_commit`, write clear, concise messages following the Conventional Commits format: `type(scope): subject`. The tool's description provides detailed guidance.
1616+* **Review Before Committing**: Before committing, always use `git_status` and `git_diff` to review the changes. This ensures you create logical, atomic commits.
1717+1818+---
1919+2020+## Commonly Used Tools
2121+2222+These are the essential tools for day-to-day version control tasks.
2323+2424+### `git_set_working_dir`
2525+2626+* **Description**: Sets the default working directory for the current session. Subsequent Git tool calls can use `.` for the `path`, which will resolve to this directory. **This should be the first tool you use in any workflow.**
2727+* **When to Use**: At the beginning of any task that involves a Git repository to establish context for all subsequent commands.
2828+* **Input Parameters**:
2929+3030+| Parameter | Type | Description |
3131+| :----------------------- | :------ | :--------------------------------------------------------------------------- |
3232+| `path` | string | The **absolute path** to set as the default working directory. |
3333+| `validateGitRepo` | boolean | Validate that the path is a Git repository. Defaults to `true`. |
3434+| `initializeIfNotPresent` | boolean | If not a Git repository, initialize it with 'git init'. Defaults to `false`. |
3535+3636+### `git_status`
3737+3838+* **Description**: Retrieves the status of the repository, showing staged, unstaged, and untracked files.
3939+* **When to Use**: Use this frequently to check the state of the repository before staging changes, after pulling from a remote, or before committing.
4040+* **Input Parameters**:
4141+4242+| Parameter | Type | Description |
4343+| :-------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------ |
4444+| `path` | string | Path to the Git repository. Defaults to `.` (the session's working directory). |
4545+4646+### `git_add`
4747+4848+* **Description**: Stages changes, adding them to the index before committing.
4949+* **When to Use**: After making changes to files and before you are ready to commit them.
5050+* **Input Parameters**:
5151+5252+| Parameter | Type | Description |
5353+| :-------- | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
5454+| `path` | string | Path to the Git repository. Defaults to the directory set via `git_set_working_dir`. |
5555+| `files` | string \| string\[] | Files or patterns to stage. Defaults to all changes (`.`). |
5656+5757+### `git_commit`
5858+5959+* **Description**: Commits staged changes to the repository with a descriptive message.
6060+* **When to Use**: After staging a logical group of changes with `git_add` and receiving approval from the operator to commit.
6161+* **Input Parameters**:
6262+6363+| Parameter | Type | Description |
6464+| :----------- | :------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
6565+| `path` | string | Path to the Git repository. |
6666+| `message` | string | The commit message. |
6767+| `author` | object | Override the commit author (`{ name: string, email: string }`). |
6868+| `filesToStage`| string\[] | An array of file paths to stage before committing. |
6969+7070+### `git_log`
7171+7272+* **Description**: Shows the commit history. Can be filtered by author, date, or branch.
7373+* **When to Use**: To review recent changes, find a specific commit, or understand the history of a file or branch.
7474+* **Input Parameters**:
7575+7676+| Parameter | Type | Description |
7777+| :------------- | :------ | :------------------------------------------------------------------- |
7878+| `path` | string | Path to the Git repository. |
7979+| `maxCount` | number | Limit the number of commits to output. |
8080+| `author` | string | Filter commits by a specific author. |
8181+| `since` | string | Show commits more recent than a specific date. |
8282+| `until` | string | Show commits older than a specific date. |
8383+| `branchOrFile` | string | Show logs for a specific branch, tag, or file path. |
8484+| `showSignature`| boolean | Show signature verification status for commits. |
8585+8686+### `git_diff`
8787+8888+* **Description**: Shows changes between commits, the working tree, etc.
8989+* **When to Use**: To review unstaged changes before adding, or to see the difference between two branches or commits.
9090+* **Input Parameters**:
9191+9292+| Parameter | Type | Description |
9393+| :--------------- | :------ | :------------------------------------------------------------------------------- |
9494+| `path` | string | Path to the Git repository. |
9595+| `commit1` | string | First commit, branch, or ref for comparison. |
9696+| `commit2` | string | Second commit, branch, or ref for comparison. |
9797+| `staged` | boolean | Show diff of staged changes. |
9898+| `file` | string | Limit the diff to a specific file. |
9999+| `includeUntracked`| boolean | Include untracked files in the diff output. |
100100+101101+### `git_branch`
102102+103103+* **Description**: Manages branches: list, create, delete, and rename.
104104+* **When to Use**: To see what branches are available, create a new branch for a feature or bugfix, or clean up old branches. DO NOT do this without human operator confirmation.
105105+* **Input Parameters**:
106106+107107+| Parameter | Type | Description |
108108+| :------------ | :------ | :------------------------------------------------------------------- |
109109+| `path` | string | Path to the Git repository. |
110110+| `mode` | enum | The operation: `list`, `create`, `delete`, `rename`, `show-current`. |
111111+| `branchName` | string | Name of the branch for create/delete/rename operations. |
112112+| `newBranchName`| string | The new name for the branch when renaming. |
113113+| `startPoint` | string | The starting point for a new branch. |
114114+| `force` | boolean | Force the operation (e.g., deleting an unmerged branch). |
115115+| `all` | boolean | List all branches (local and remote). |
116116+| `remote` | boolean | Act on remote-tracking branches. |
117117+118118+### `git_checkout`
119119+120120+* **Description**: Switches branches or restores working tree files.
121121+* **When to Use**: To start working on a different branch or to discard changes in a specific file. DO NOT do this without human operator confirmation.
122122+* **Input Parameters**:
123123+124124+| Parameter | Type | Description |
125125+| :----------- | :------ | :---------------------------------------------------------------- |
126126+| `path` | string | Path to the Git repository. |
127127+| `branchOrPath`| string | The branch, commit, tag, or file path to checkout. |
128128+| `newBranch` | string | Create a new branch before checking out. |
129129+| `force` | boolean | Force checkout, discarding local changes. |
130130+131131+### `git_pull`
132132+133133+* **Description**: Fetches from and integrates with a remote repository or a local branch.
134134+* **When to Use**: To update your current local branch with changes from its remote counterpart. DO NOT do this without human operator confirmation.
135135+* **Input Parameters**:
136136+137137+| Parameter | Type | Description |
138138+| :-------- | :------ | :----------------------------------------------------------------- |
139139+| `path` | string | Path to the Git repository. |
140140+| `remote` | string | The remote repository to pull from (e.g., 'origin'). |
141141+| `branch` | string | The remote branch to pull. |
142142+| `rebase` | boolean | Use 'git pull --rebase' instead of merge. |
143143+| `ffOnly` | boolean | Only allow fast-forward merges. |
144144+145145+### `git_push`
146146+147147+* **Description**: Updates remote refs with local changes.
148148+* **When to Use**: After committing your changes locally, use this to share them on the remote repository. DO NOT do this without human operator confirmation.
149149+* **Input Parameters**:
150150+151151+| Parameter | Type | Description |
152152+| :----------- | :------ | :----------------------------------------------------------- |
153153+| `path` | string | Path to the Git repository. |
154154+| `remote` | string | The remote repository to push to. |
155155+| `branch` | string | The local branch to push. |
156156+| `remoteBranch`| string | The remote branch to push to. |
157157+| `force` | boolean | Force the push (use with caution). |
158158+| `forceWithLease`| boolean | Force push only if remote ref is as expected. |
159159+| `setUpstream`| boolean | Set the upstream tracking configuration. |
160160+| `tags` | boolean | Push all tags. |
161161+| `delete` | boolean | Delete the remote branch. |
162162+163163+---
164164+165165+## Complex Situations
166166+167167+If you encounter a situation where you believe a more advanced or potentially destructive tool is needed (such as `git rebase`, `git reset`, `git cherry-pick`, or `git clean`), **do not proceed automatically**.
168168+169169+Instead, you should:
170170+171171+1. **Pause execution.**
172172+2. **Explain the situation** to the human operator.
173173+3. **State which advanced Git operation you think is necessary and why.**
174174+4. **Await explicit instruction** from the operator before taking any further action.
···11+# Claude Code Instructions
22+33+**All imports in this document should be treated as if they were in the main prompt file.**
44+55+## MCP Orientation Instructions
66+77+@.claude/mcp-descriptions/github-mcp.mdc
88+99+NEVER USE A COMMAND-LINE TOOL WHEN AN MCP TOOL IS AVAILABLE. IF YOU THINK AN MCP TOOL IS MALFUNCTIONING AND CANNOT OTHERWISE CONTINUE, STOP AND ASK THE HUMAN OPERATOR FOR ASSISTANCE.
1010+1111+## Development Commands
1212+1313+### Running the Application
1414+1515+- `bun run start` - Run the main application (production mode)
1616+- `bun run dev` - Run in development mode with file watching
1717+- `bun i` - Install dependencies
1818+1919+### Code Quality
2020+2121+- `bun run format` - Format code using Prettier
2222+- `bun run lint` - Run ESLint to check for issues
2323+- `bun run lint:fix` - Automatically fix ESLint issues where possible
2424+2525+### Docker Deployment
2626+2727+- `docker build -pull -t skywatch-tools .` - Build Docker image
2828+- `docker run -d -p 4101:4101 skywatch-autolabeler` - Run container
2929+3030+## Architecture Overview
3131+3232+This is a TypeScript rewrite of a Bash-based Bluesky content moderation system for the skywatch.blue independent labeler. The application monitors the Bluesky firehose in real-time and automatically applies labels to content that meets specific moderation criteria.
3333+3434+### Core Components
3535+3636+- **`main.ts`** - Entry point that sets up Jetstream WebSocket connection to monitor Bluesky firehose events (posts, profiles, handles, starter packs)
3737+- **`agent.ts`** - Configures the AtpAgent for interacting with Ozone PDS for labeling operations
3838+- **`constants.ts`** - Contains all moderation check definitions (PROFILE_CHECKS, POST_CHECKS, HANDLE_CHECKS)
3939+- **`config.ts`** - Environment variable configuration and application settings
4040+- **Check modules** - Individual modules for different content types:
4141+ - `checkPosts.ts` - Analyzes post content and URLs
4242+ - `checkHandles.ts` - Validates user handles
4343+ - `checkProfiles.ts` - Examines profile descriptions and display names
4444+ - `checkStarterPack.ts` - Reviews starter pack content
4545+4646+### Moderation Check System
4747+4848+The system uses a `Checks` interface to define moderation rules with the following properties:
4949+5050+- `label` - The label to apply when content matches
5151+- `check` - RegExp pattern to match against content
5252+- `whitelist` - Optional RegExp to exempt certain content
5353+- `ignoredDIDs` - Array of DIDs to skip for this check
5454+- `reportAcct/commentAcct/toLabel` - Actions to take when content matches
5555+5656+### Environment Configuration
5757+5858+The application requires several environment variables:
5959+6060+- Bluesky credentials (`BSKY_HANDLE`, `BSKY_PASSWORD`)
6161+- Ozone server configuration (`OZONE_URL`, `OZONE_PDS`)
6262+- Optional: firehose URL, ports, rate limiting settings
6363+6464+### Data Flow
6565+6666+1. Jetstream receives events from Bluesky firehose
6767+2. Events are categorized by type (post, profile, handle, starter pack)
6868+3. Appropriate check functions validate content against defined patterns
6969+4. Matching content triggers labeling actions via Ozone PDS
7070+5. Cursor position is periodically saved for resumption after restart
7171+7272+### Development Notes
7373+7474+- Uses Bun as the runtime and package manager
7575+- Built with modern TypeScript and ESNext modules
7676+- Implements rate limiting and error handling for API calls
7777+- Supports both labeling and reporting workflows
7878+- Includes metrics server on port 4101 for monitoring
7979+8080+See `src/developing_checks.md` for detailed instructions on creating new moderation checks.
8181+8282+## TODO
8383+8484+The code-reviewer has completed a comprehensive review of the codebase and identified several critical issues that need immediate attention:
8585+8686+ Immediate Blocking Issues
8787+8888+ - Missing constants.ts file (only example exists)
8989+ - Unsafe type assertions in main.ts:152,158
9090+ - Inadequate error handling for async operations
9191+9292+ High Priority Security & Reliability Concerns
9393+9494+ - Hardcoded DIDs should be moved to environment variables
9595+ - Missing structured error handling and logging
9696+ - No environment variable validation at startup
9797+9898+ Medium Priority Code Quality Issues
9999+100100+ - Duplicate profile checking logic needs refactoring
101101+ - ESLint configuration needs TypeScript updates
102102+ - Missing comprehensive test suite
103103+104104+ The reviewer noted that while the modular architecture is well-designed, there are critical execution flaws that must be addressed before this
105105+ can be safely deployed to production.
+14-14
README.md
···87878888The following environment variables are used for configuration:
89899090-| Variable | Description | Default |
9191-| ------------------------ | ---------------------------------------------------------------- | ----------------------------------------- |
9292-| `DID` | The DID of your moderation service for atproto-proxy headers. | `""` |
9393-| `OZONE_URL` | The URL of the Ozone service. | `""` |
9494-| `OZONE_PDS` | The Public Downstream Service for Ozone. | `""` |
9595-| `BSKY_HANDLE` | The handle (username) of the bot's Bluesky account. | `""` |
9696-| `BSKY_PASSWORD` | The app password for the bot's Bluesky account. | `""` |
9797-| `HOST` | The host on which the server runs. | `127.0.0.1` |
9898-| `PORT` | The port for the main application (currently unused). | `4100` |
9999-| `METRICS_PORT` | The port for the Prometheus metrics server. | `4101` |
9090+| Variable | Description | Default |
9191+| ------------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------- |
9292+| `DID` | The DID of your moderation service for atproto-proxy headers. | `""` |
9393+| `OZONE_URL` | The URL of the Ozone service. | `""` |
9494+| `OZONE_PDS` | The Public Downstream Service for Ozone. | `""` |
9595+| `BSKY_HANDLE` | The handle (username) of the bot's Bluesky account. | `""` |
9696+| `BSKY_PASSWORD` | The app password for the bot's Bluesky account. | `""` |
9797+| `HOST` | The host on which the server runs. | `127.0.0.1` |
9898+| `PORT` | The port for the main application (currently unused). | `4100` |
9999+| `METRICS_PORT` | The port for the Prometheus metrics server. | `4101` |
100100| `FIREHOSE_URL` | The WebSocket URL for the Bluesky firehose. | `FIREHOSE_URL=wss://jetstream1.us-east.bsky.network/subscribe` |
101101-| `CURSOR_UPDATE_INTERVAL` | How often to save the firehose cursor to disk (in milliseconds). | `60000` |
102102-| `LABEL_LIMIT` | (Optional) API call limit for labeling. | `undefined` |
103103-| `LABEL_LIMIT_WAIT` | (Optional) Wait time when label limit is hit. | `undefined` |
104104-| `LOG_LEVEL` | The logging level. | `info` |
101101+| `CURSOR_UPDATE_INTERVAL` | How often to save the firehose cursor to disk (in milliseconds). | `60000` |
102102+| `LABEL_LIMIT` | (Optional) API call limit for labeling. | `undefined` |
103103+| `LABEL_LIMIT_WAIT` | (Optional) Wait time when label limit is hit. | `undefined` |
104104+| `LOG_LEVEL` | The logging level. | `info` |
···11+import { AtpAgent } from "@atproto/api";
22+13import { setGlobalDispatcher, Agent as Agent } from "undici";
44+25setGlobalDispatcher(new Agent({ connect: { timeout: 20_000 } }));
36import { BSKY_HANDLE, BSKY_PASSWORD, OZONE_PDS } from "./config.js";
44-import { AtpAgent } from "@atproto/api";
5768export const agent = new AtpAgent({
79 service: `https://${OZONE_PDS}`,
+3-3
src/checkHandles.ts
···11-import { HANDLE_CHECKS } from "./constants.js";
22-import logger from "./logger.js";
11+import { HANDLE_CHECKS } from './constants.js';
22+import logger from './logger.js';
33import {
44 createAccountReport,
55 createAccountComment,
66 createAccountLabel,
77-} from "./moderation.js";
77+} from './moderation.js';
8899export const checkHandle = async (
1010 did: string,
+6-6
src/checkPosts.ts
···11-import { LINK_SHORTENER, POST_CHECKS, langs } from "./constants.js";
22-import { Post } from "./types.js";
33-import logger from "./logger.js";
11+import { LINK_SHORTENER, POST_CHECKS, langs } from './constants.js';
22+import logger from './logger.js';
43import {
54 createPostLabel,
65 createAccountReport,
76 createAccountComment,
87 createPostReport,
99-} from "./moderation.js";
1010-import { getFinalUrl, getLanguage } from "./utils.js";
88+} from './moderation.js';
99+import type { Post } from './types.js';
1010+import { getFinalUrl, getLanguage } from './utils.js';
11111212export const checkPosts = async (post: Post[]) => {
1313 // Get a list of labels
···6868 // Check if post is whitelisted
6969 if (checkPost?.whitelist) {
7070 if (checkPost?.whitelist.test(post[0].text)) {
7171- logger.info(`[CHECKPOSTS]: Whitelisted phrase found"`);
7171+ logger.info('[CHECKPOSTS]: Whitelisted phrase found"');
7272 return;
7373 }
7474 }
+7-7
src/checkProfiles.ts
···11-import { login } from "./agent.js";
22-import { langs, PROFILE_CHECKS } from "./constants.js";
33-import logger from "./logger.js";
11+import { login } from './agent.js';
22+import { langs, PROFILE_CHECKS } from './constants.js';
33+import logger from './logger.js';
44import {
55 createAccountReport,
66 createAccountLabel,
77 createAccountComment,
88-} from "./moderation.js";
99-import { getLanguage } from "./utils.js";
88+} from './moderation.js';
99+import { getLanguage } from './utils.js';
10101111export const checkDescription = async (
1212 did: string,
···4747 // Check if description is whitelisted
4848 if (checkProfiles!.whitelist) {
4949 if (checkProfiles!.whitelist.test(description)) {
5050- logger.info(`[CHECKDESCRIPTION]: Whitelisted phrase found.`);
5050+ logger.info('[CHECKDESCRIPTION]: Whitelisted phrase found.');
5151 return;
5252 }
5353 }
···128128 // Check if displayName is whitelisted
129129 if (checkProfiles!.whitelist) {
130130 if (checkProfiles!.whitelist.test(displayName)) {
131131- logger.info(`[CHECKDISPLAYNAME]: Whitelisted phrase found.`);
131131+ logger.info('[CHECKDISPLAYNAME]: Whitelisted phrase found.');
132132 return;
133133 }
134134 }
+4-4
src/checkStarterPack.ts
···11-import { PROFILE_CHECKS, STARTERPACK_CHECKS } from "./constants.js";
22-import logger from "./logger.js";
11+import { PROFILE_CHECKS, STARTERPACK_CHECKS } from './constants.js';
22+import logger from './logger.js';
33import {
44 createAccountLabel,
55 createAccountReport,
66 createPostLabel,
77-} from "./moderation.js";
77+} from './moderation.js';
8899export const checkStarterPack = async (
1010 did: string,
···2626 // Check if DID is whitelisted
2727 if (checkProfiles?.ignoredDIDs) {
2828 if (checkProfiles.ignoredDIDs.includes(did)) {
2929- return logger.info(`Whitelisted DID: ${did}`);
2929+ logger.info(`Whitelisted DID: ${did}`); return;
3030 }
3131 }
3232
···11# How to build checks for skywatch-automod
2233## Introduction
44+45Constants.ts defines three types of types of checks: `HANDLE_CHECKS`, `POST_CHECKS`, and `PROFILE_CHECKS`.
5667For each check, users need to define a set of regular expressions that will be used to match against the content of the post, handle, or profile. A maximal example of a check is as follows:
···1920 toLabel: true, // Should the handle in question be labeled if check evaluates to true.
2021 check: new RegExp("example", "i"), // Regular expression to match against the content
2122 whitelist: new RegExp("example.com", "i"), // Optional, regular expression to whitelist content
2222- ignoredDIDs: ["did:plc:example"] // Optional, array of DIDs to ignore if they match the check. Useful for folks who reclaim words or accounts which may be false positives.
2323- }
2323+ ignoredDIDs: ["did:plc:example"], // Optional, array of DIDs to ignore if they match the check. Useful for folks who reclaim words or accounts which may be false positives.
2424+ },
2425];
2526```
2627
+1-1
src/limits.ts
···11-import { pRateLimit } from "p-ratelimit"; // TypeScript
11+import { pRateLimit } from 'p-ratelimit'; // TypeScript
2233// create a rate limiter that allows up to 30 API calls per second,
44// with max concurrency of 10
···11-import { describe } from "node:test";
22-import { PROFILE_CHECKS } from "./constants.js";
33-import logger from "./logger.js";
44-import { createAccountReport, createAccountLabel } from "./moderation.js";
11+import { describe } from 'node:test';
22+33+import { PROFILE_CHECKS } from './constants.js';
44+import logger from './logger.js';
55+import { createAccountReport, createAccountLabel } from './moderation.js';
5667export const monitorDescription = async (
78 did: string,
···2425 // Check if DID is whitelisted
2526 if (checkProfiles?.ignoredDIDs) {
2627 if (checkProfiles.ignoredDIDs.includes(did)) {
2727- return logger.info(`Whitelisted DID: ${did}`);
2828+ logger.info(`Whitelisted DID: ${did}`); return;
2829 }
2930 }
3031···3334 if (checkProfiles!.check.test(description)) {
3435 if (checkProfiles!.whitelist) {
3536 if (checkProfiles!.whitelist.test(description)) {
3636- logger.info(`Whitelisted phrase found.`);
3737+ logger.info('Whitelisted phrase found.');
3738 return;
3839 }
3940 } else {
···8081 // Check if DID is whitelisted
8182 if (checkProfiles?.ignoredDIDs) {
8283 if (checkProfiles.ignoredDIDs.includes(did)) {
8383- return logger.info(`Whitelisted DID: ${did}`);
8484+ logger.info(`Whitelisted DID: ${did}`); return;
8485 }
8586 }
8687···8990 if (checkProfiles!.check.test(displayName)) {
9091 if (checkProfiles!.whitelist) {
9192 if (checkProfiles!.whitelist.test(displayName)) {
9292- logger.info(`Whitelisted phrase found.`);
9393+ logger.info('Whitelisted phrase found.');
9394 return;
9495 }
9596 } else {
+1-1
src/types.ts
···39394040// Define the type for the link feature
4141export interface LinkFeature {
4242- $type: "app.bsky.richtext.facet#link";
4242+ $type: 'app.bsky.richtext.facet#link';
4343 uri: string;
4444}
4545
+14-14
src/utils.ts
···11-import logger from "./logger.js";
11+import logger from './logger.js';
2233/* Normalize the Unicode characters: this doesn't consistently work yet, there is something about certain bluesky strings that causes it to fail. */
44export function normalizeUnicode(text: string): string {
55 // First decompose the characters (NFD)
66- const decomposed = text.normalize("NFD");
66+ const decomposed = text.normalize('NFD');
7788 // Remove diacritics and combining marks
99- const withoutDiacritics = decomposed.replace(/[\u0300-\u036f]/g, "");
99+ const withoutDiacritics = decomposed.replace(/[\u0300-\u036f]/g, '');
10101111 // Remove mathematical alphanumeric symbols
1212 const withoutMath = withoutDiacritics.replace(
···3131 );
32323333 // Final NFKC normalization to handle any remaining special characters
3434- return withoutMath.normalize("NFKC");
3434+ return withoutMath.normalize('NFKC');
3535}
36363737export async function getFinalUrl(url: string): Promise<string> {
3838 const controller = new AbortController();
3939- const timeoutId = setTimeout(() => controller.abort(), 10000); // 10-second timeout
3939+ const timeoutId = setTimeout(() => { controller.abort(); }, 10000); // 10-second timeout
40404141 try {
4242 const response = await fetch(url, {
4343- method: "HEAD",
4444- redirect: "follow", // This will follow redirects automatically
4343+ method: 'HEAD',
4444+ redirect: 'follow', // This will follow redirects automatically
4545 signal: controller.signal, // Pass the abort signal to fetch
4646 });
4747 clearTimeout(timeoutId); // Clear the timeout if fetch completes
···4949 } catch (error) {
5050 clearTimeout(timeoutId); // Clear the timeout if fetch fails
5151 // Log the error with more specific information if it's a timeout
5252- if (error instanceof Error && error.name === "AbortError") {
5252+ if (error instanceof Error && error.name === 'AbortError') {
5353 logger.warn(`Timeout fetching URL: ${url}`, error);
5454 } else {
5555 logger.warn(`Error fetching URL: ${url}`, error);
···5959}
60606161export async function getLanguage(profile: string): Promise<string> {
6262- if (typeof profile !== "string" || profile === null) {
6262+ if (typeof profile !== 'string' || profile === null) {
6363 logger.warn(
6464- "[GETLANGUAGE] getLanguage called with invalid profile data, defaulting to 'eng'.",
6464+ '[GETLANGUAGE] getLanguage called with invalid profile data, defaulting to \'eng\'.',
6565 profile,
6666 );
6767- return "eng"; // Default or throw an error
6767+ return 'eng'; // Default or throw an error
6868 }
69697070 const profileText = profile.trim();
71717272 if (profileText.length === 0) {
7373- return "eng";
7373+ return 'eng';
7474 }
75757676- const lande = (await import("lande")).default;
7777- let langsProbabilityMap = lande(profileText);
7676+ const lande = (await import('lande')).default;
7777+ const langsProbabilityMap = lande(profileText);
78787979 // Sort by probability in descending order
8080 langsProbabilityMap.sort(