MANDATORY: Act as principal-level engineer. Follow these guidelines exactly.
This is a reference to shared Socket standards. See ../socket-registry/CLAUDE.md for canonical source.
- Identify users by git credentials: Extract name from git commit author, GitHub account, or context
- π¨ When identity is verified: ALWAYS use their actual name - NEVER use "the user" or "user"
- Direct communication: Use "you/your" when speaking directly to the verified user
- Discussing their work: Use their actual name when referencing their commits/contributions
- Example: If git shows "John-David Dalton jdalton@example.com", refer to them as "John-David"
- Other contributors: Use their actual names from commit history/context
MANDATORY: Review CLAUDE.md before any action. No exceptions.
MANDATORY: Before claiming any task is complete:
- Test the solution end-to-end
- Verify all changes work as expected
- Run the actual commands to confirm functionality
- Never claim "Done" without verification
- Fix ALL issues when asked - Never dismiss issues as "pre-existing" or "not caused by my changes"
- When asked to fix, lint, or check: fix everything found, regardless of who introduced it
- Always address all issues found during lint/check operations
- Never create files unless necessary
- Always prefer editing existing files
- Forbidden to create docs unless requested
- Required to do exactly what was asked
Principal Software Engineer: production code, architecture, reliability, ownership.
If user repeats instruction 2+ times, ask: "Should I add this to CLAUDE.md?"
Canonical reference: ../socket-registry/CLAUDE.md
All shared standards (git, testing, code style, cross-platform, CI) defined in socket-registry/CLAUDE.md.
Quick references:
- Commits: Conventional Commits
<type>(<scope>): <description>- NO AI attribution - Scripts: Prefer
pnpm run foo --flagoverfoo:barscripts - Dependencies: After
package.jsonedits, runpnpm installto updatepnpm-lock.yaml - Backward Compatibility: π¨ FORBIDDEN to maintain - actively remove when encountered (see canonical CLAUDE.md)
- Work Safeguards: MANDATORY commit + backup branch before bulk changes
- Safe Deletion: Use
safeDelete()from@socketsecurity/lib/fs(NEVERfs.rm/rmSyncorrm -rf)
π¨ CRITICAL: Do NOT litter the repository with documentation files.
Allowed documentation locations:
docs/- Primary documentation folder (lowercase-with-hyphens.md filenames, pithy writing with visuals)README.md- Root project README onlyCHANGELOG.md- Root changelog onlySECURITY.md- Root security policy onlyCLAUDE.md- Root project instructions onlypackages/*/README.md- Package-specific READMEs onlytest/fixtures/*/README.md- Test fixture explanations only (when fixtures are complex)test/*/README.md- Test suite architecture docs only (e.g.,test/e2e/README.md,test/helpers/README.md)
Forbidden documentation locations:
- β
.claude/*.md- No temporary analysis docs, architectural notes, coverage reports - β Root directory clutter - No ad-hoc markdown files
- β Scattered docs - No documentation outside approved locations
When creating documentation:
- Ask: Does this belong in
docs/? (99% of the time, yes) - Use descriptive lowercase-with-hyphens.md filenames
- Keep writing pithy and focused
- Include visuals where helpful
- Never create temporary/scratch documentation files
- Build:
pnpm run build(smart build, skips unchanged) - Build force:
pnpm run build --force(force rebuild CLI + SEA for current platform) - Build SEA:
pnpm run build:sea(build SEA binaries for all platforms) - Build CLI:
pnpm run build:cli(CLI package only) - Test:
pnpm test(runs check + all tests from monorepo root) - Test unit only:
pnpm --filter @socketsecurity/cli run test:unit - Lint:
pnpm run lint(uses oxlint) - Type check:
pnpm run type(uses tsc) - Check all:
pnpm run check(lint + typecheck) - Fix all issues:
pnpm run fix(auto-fix linting and formatting) - Commit without tests:
git commit --no-verify(skips pre-commit hooks including tests)
- Node-smol binaries: Downloaded from socket-btm releases (not built locally)
- Yoga WASM: Downloaded from socket-btm releases (not built locally)
- SEA binaries: Built by injecting CLI blob into downloaded node-smol binaries
- Output location:
packages/package-builder/build/{dev|prod}/out/socketbin-cli-<platform>-<arch>/socket - Cache location: Build assets in
packages/build-infra/build/downloaded/, DLX packages and VFS-extracted tools in~/.socket/_dlx/
- π¨ NEVER USE
--BEFORE TEST FILE PATHS - This runs ALL tests, not just your specified files! - Always build before testing: Run
pnpm run build:clibefore running tests to ensure dist files are up to date. - Test all: β
CORRECT:
pnpm test(from monorepo root) - Test single file: β
CORRECT:
pnpm --filter @socketsecurity/cli run test:unit src/commands/specific/cmd-file.test.mts- β WRONG:
pnpm test:unit src/commands/specific/cmd-file.test.mts(command not found at root!) - β WRONG:
pnpm --filter @socketsecurity/cli run test:unit -- src/commands/specific/cmd-file.test.mts(runs ALL tests!)
- β WRONG:
- Test multiple files: β
CORRECT:
pnpm --filter @socketsecurity/cli run test:unit file1.test.mts file2.test.mts - Test with pattern: β
CORRECT:
pnpm --filter @socketsecurity/cli run test:unit src/commands/specific/cmd-file.test.mts -t "pattern"- β WRONG:
pnpm --filter @socketsecurity/cli run test:unit -- src/commands/specific/cmd-file.test.mts -t "pattern"
- β WRONG:
- Update snapshots:
- All tests:
pnpm testu(builds first, then updates all snapshots) - Single file: β
CORRECT:
pnpm testu src/commands/specific/cmd-file.test.mts - β WRONG:
pnpm testu -- src/commands/specific/cmd-file.test.mts(updates ALL snapshots!)
- All tests:
- Update with --update flag:
pnpm --filter @socketsecurity/cli run test:unit src/commands/specific/cmd-file.test.mts --update - Timeout for long tests: Use
timeoutcommand or specify in test file.
- Follow Conventional Commits style
- π¨ FORBIDDEN: NO AI attribution in commits (see SHARED STANDARDS)
- Watch mode:
pnpm dev(auto-rebuilds on file changes) - Build and run:
pnpm build:cli && node packages/cli/dist/index.js - Run built version:
node packages/cli/dist/index.js <args>(requires prior build)
Note: Avoid pnpm exec socket if you have a global socket installation, as it may conflict with the local package.
- Package Manager: This project uses pnpm (v10.22+)
- Install dependencies:
pnpm install - Add dependency:
pnpm add <package> - Add dev dependency:
pnpm add -D <package> - Update dependencies:
pnpm update - Override behavior: pnpm.overrides in package.json controls dependency versions across the entire project
- Using $ syntax:
"$package-name"in overrides means "use the version specified in dependencies"
This is a CLI tool for Socket.dev security analysis, built with TypeScript using .mts extensions.
- Entry point:
src/cli.mts- Main CLI entry with meow subcommands - Commands:
src/commands.mts- Exports all command definitions - Command modules:
src/commands/*/- Each feature has its own directory with cmd-, handle-, and output-* files - Utilities:
src/utils/- Shared utilities for API, config, formatting, etc. - Constants:
src/constants.mts- Application constants - Types:
src/types.mts- TypeScript type definitions
β PREFERRED: Consolidated Pattern for Simple Commands
For commands with straightforward logic (no subcommands, < 200 lines total), consolidate into a single cmd-*.mts file:
// Single cmd-*.mts file structure:
import { getDefaultLogger } from '@socketsecurity/lib/logger'
// ... other imports
const logger = getDefaultLogger()
export const CMD_NAME = 'command-name'
const description = 'Command description'
const hidden = false
// Types.
interface CommandResult {
// Type definitions here.
}
// Helper functions.
function helperFunction(): void {
// Helper logic here.
}
// Command handler.
async function run(
argv: string[] | readonly string[],
importMeta: ImportMeta,
{ parentName }: CliCommandContext,
): Promise<void> {
const config: CliCommandConfig = {
/* ... */
}
const cli = meowOrExit({ argv, config, importMeta, parentName })
// Command logic here.
}
// Exported command.
export const cmdCommandName = {
description,
hidden,
run,
}Benefits:
- All command logic in one file for easy navigation
- Clear sections: imports β constants β types β helpers β handler β export
- Reduced file count (3 files β 1 file)
- Maintained compatibility with existing meow-based CLI architecture
Examples: whoami, logout (consolidated)
Complex commands with subcommands or > 200 lines should keep the modular pattern:
cmd-*.mts- Command definition and CLI interfacehandle-*.mts- Business logic and processingoutput-*.mts- Output formatting (JSON, markdown, etc.)fetch-*.mts- API calls (where applicable)
Examples: scan, organization, repository (keep modular)
- npm/npx wrapping:
socket npm,socket npx- Wraps npm/npx with security scanning - Scanning:
socket scan- Create and manage security scans - Organization management:
socket organization- Manage org settings and policies - Package analysis:
socket package- Analyze package scores - Optimization:
socket optimize- Apply Socket registry overrides - Configuration:
socket config- Manage CLI configuration
Socket CLI has different update mechanisms depending on installation method:
- Update checking: Handled by node-smol C stub via embedded
--update-config - Configuration: Embedded during build in
packages/cli/scripts/sea-build-utils/builder.mjs - GitHub releases: Checks
https://api.github.com/repos/SocketDev/socket-cli/releases - Tag pattern: Matches
socket-cli-*(e.g.,socket-cli-20260127-abc1234) - Notification: Shown on CLI exit (non-blocking)
- Update command:
socket self-update(handled by node-smol, not TypeScript CLI) - Environment variable:
SOCKET_CLI_SKIP_UPDATE_CHECK=1to disable
- Update checking: TypeScript-based in
src/utils/update/manager.mts - Registry: Checks npm registry for
socketpackage - Notification: Shown on CLI exit (non-blocking)
- Update command: Use package manager (e.g.,
npm update -g socket) - Environment variable:
SOCKET_CLI_SKIP_UPDATE_CHECK=1to disable
scheduleUpdateCheck()inmanager.mtsskips whenisSeaBinary()returns true- SEA binaries use embedded update-config.json (1112 bytes)
- node-smol handles HTTP requests via embedded libcurl
- Update checks respect CI/TTY detection and rate limiting
- Uses esbuild for building distribution files
- TypeScript compilation with tsgo
- Environment config (.env.test for testing)
- Linting with Oxlint
- Formatting with Oxfmt
- Vitest for unit testing
- Test files use
.test.mtsextension - Fixtures in
test/fixtures/ - Coverage reporting available
- Vendored modules in
src/external/(e.g., ink-table) - Dependencies bundled into
dist/cli.jsvia esbuild - Uses Socket registry overrides for security
- Custom patches applied to dependencies in
patches/
.env.test- Test environment configuration
.oxfmtrc.json- Oxfmt formatter configuration.oxlintrc.json- Oxlint linter configurationvitest.config.mts- Vitest test runner configurationtsconfig.json- Main TypeScript configurationtsconfig.dts.json- TypeScript configuration for type definitions
- Binary entries:
socket,socket-npm,socket-npx(defined in package.jsonbinfield, pointing todist/index.js) - Distribution: Built files go to
dist/directory - External dependencies: Bundled into
dist/cli.jsvia esbuild - Test fixtures: Located in
test/fixtures/
- Uses Socket registry overrides for enhanced alternatives
- Custom patches applied to dependencies via
custompatch - Overrides specified in package.json for enhanced alternatives
Follow Keep a Changelog format. Include user-facing changes only: Added, Changed, Fixed, Removed. Exclude: dependency updates, refactoring, tests, CI/CD, formatting. Marketing voice, stay concise.
Socket CLI integrates with various third-party tools and services:
- @coana-tech/cli: Static analysis tool for reachability analysis and vulnerability detection
- cdxgen: CycloneDX BOM generator for creating software bill of materials
- synp: Tool for converting between yarn.lock and package-lock.json formats
- File extensions: Use
.mtsfor TypeScript module files - Import order: Node.js built-ins first, then third-party packages, then local imports
- Import grouping: Group imports by source (Node.js, external packages, local modules)
- Type imports: π¨ ALWAYS use separate
import typestatements for TypeScript types, NEVER mix runtime imports with type imports in the same statement- β
CORRECT:
import { readPackageJson } from '@socketsecurity/registry/lib/packages'followed byimport type { PackageJson } from '@socketsecurity/registry/lib/packages' - β FORBIDDEN:
import { readPackageJson, type PackageJson } from '@socketsecurity/registry/lib/packages'
- β
CORRECT:
- Constants: Use
UPPER_SNAKE_CASEfor constants (e.g.,CMD_NAME,REPORT_LEVEL) - Files: Use kebab-case for filenames (e.g.,
cmd-scan-create.mts,handle-create-new-scan.mts) - Variables: Use camelCase for variables and functions
- Command pattern: Complex commands use modular pattern (
cmd-*.mts,handle-*.mts,output-*.mts); simple commands use consolidated singlecmd-*.mtsfile - Type definitions: π¨ ALWAYS use
import typefor better tree-shaking - Flags: π¨ MUST use
MeowFlagstype with descriptive help text - Error handling: π¨ REQUIRED - Use custom error types
AuthErrorandInputError - Array destructuring: Use object notation
{ 0: key, 1: data }instead of array destructuring[key, data] - Dynamic imports: π¨ FORBIDDEN - Never use dynamic imports (
await import()). Always use static imports at the top of the file - Sorting: π¨ MANDATORY - Always sort lists, exports, and items in documentation headers alphabetically/alphanumerically for consistency
- Comment placement: Place comments on their own line, not to the right of code
- Comment formatting: Use fewer hyphens/dashes and prefer commas, colons, or semicolons for better readability
- Await in loops: When using
awaitinside for-loops, sequential processing is acceptable when iterations depend on each other - If statement returns: Never use single-line return if statements; always use proper block syntax with braces
- List formatting: Use
-for bullet points in text output, notβ’or other Unicode characters, for better terminal compatibility - Existence checks: Perform simple existence checks first before complex operations
- Destructuring order: Sort destructured properties alphabetically in const declarations
- Function ordering: Place functions in alphabetical order, with private functions first, then exported functions
- GitHub API calls: Use Octokit instances from
src/utils/github.mts(getOctokit(),getOctokitGraphql()) instead of raw fetch calls for GitHub API interactions - Object mappings: Use objects with
__proto__: null(notundefined) for static string-to-string mappings and lookup tables to prevent prototype pollution; useMapfor dynamic collections that will be mutated - Mapping constants: Move static mapping objects outside functions as module-level constants with descriptive UPPER_SNAKE_CASE names
- Array length checks: Use
!array.lengthinstead ofarray.length === 0. Forarray.length > 0, use!!array.lengthwhen function must return boolean, orarray.lengthwhen used in conditional contexts - Catch parameter naming: Use
catch (e)instead ofcatch (error)for consistency across the codebase - Node.js fs imports: π¨ MANDATORY pattern -
import { someSyncThing, promises as fs } from 'node:fs' - Process spawning: π¨ FORBIDDEN to use Node.js built-in
child_process.spawn- MUST usespawnfrom@socketsecurity/registry/lib/spawn - Number formatting: π¨ REQUIRED - Use underscore separators (e.g.,
20_000) for large numeric literals. π¨ FORBIDDEN - Do NOT modify number values inside strings
- Input validation errors: Use
InputErrorfromsrc/utils/errors.mtsfor user input validation failures (missing files, invalid arguments, etc.) - Authentication errors: Use
AuthErrorfromsrc/utils/errors.mtsfor API authentication issues - CResult pattern: Use
CResult<T>type for functions that can fail, following the Result/Either pattern withok: true/false - Process exit: Avoid
process.exit(1)unless absolutely necessary; prefer throwing appropriate error types that the CLI framework handles - Error messages: Write clear, actionable error messages that help users understand what went wrong and how to fix it
- Examples:
- β
throw new InputError('No .socket directory found in current directory') - β
throw new AuthError('Invalid API token') - β
logger.error('Error occurred'); return(doesn't set proper exit code) - β
process.exit(1)(bypasses error handling framework)
- β
- File deletion: See SHARED STANDARDS section
- π¨ Use
safeDelete()from@socketsecurity/lib/fs(NEVERfs.rm/rmSyncorrm -rf)
- CI vs Local Differences: CI uses published npm packages, not local versions. Be defensive when using @socketsecurity/registry features
- File Existence Checks: ALWAYS use
existsSync()fromnode:fs, NEVER usefs.access()orfs.promises.access()for file/directory existence checks.existsSync()is synchronous, more direct, and the established pattern in this codebase for consistency
- Linting: Uses Oxlint with TypeScript and import plugins
- Formatting: Uses Oxfmt for code formatting with 2-space indentation
- Line length: Target 80 character line width where practical
- Code MUST pass all existing lints and type checks
- All patterns MUST follow established codebase conventions
- Error handling MUST be robust and user-friendly
- Performance considerations MUST be evaluated for any changes