🚨 MANDATORY: Act as principal-level engineer with deep expertise in TypeScript, Node.js, and SDK development.
- Identify users by git credentials; use their actual name, never "the user"
- Use "you/your" when speaking directly; use names when referencing contributions
This repo may have multiple Claude sessions running concurrently against the same checkout, against parallel git worktrees, or against sibling clones. Several common git operations are hostile to that and silently destroy or hijack the other session's work.
-
FORBIDDEN in the primary checkout (the one another Claude may be editing):
git stash— shared stash store; another session canpopyours.git add -A/git add .— sweeps files belonging to other sessions.git checkout <branch>/git switch <branch>— yanks the working tree out from under another session.git reset --hardagainst a non-HEAD ref — discards another session's commits.
-
REQUIRED for branch work: spawn a worktree instead of switching branches in place. Each worktree has its own HEAD, so branch operations inside it are safe.
# From the primary checkout — does NOT touch the working tree here. git worktree add -b <task-branch> ../<repo>-<task> main cd ../<repo>-<task> # edit, commit, push from here; the primary checkout is untouched. cd - git worktree remove ../<repo>-<task>
-
REQUIRED for staging: surgical
git add <specific-file> [<file>…]with explicit paths. Never-A/.. -
If you need a quick WIP save: commit on a new branch from inside a worktree, not a stash.
The umbrella rule: never run a git command that mutates state belonging to a path other than the file you just edited.
- Commits: Conventional Commits
<type>(<scope>): <description>— NO AI attribution - Scripts: Prefer
pnpm run foo --flagoverfoo:barscripts - Dependencies: After
package.jsonedits, runpnpm install - Backward Compatibility: 🚨 FORBIDDEN to maintain — actively remove when encountered
- 🚨 NEVER use
npx,pnpm dlx, oryarn dlx— usepnpm exec <package>orpnpm run <script>. Add tools as pinned devDependencies first. - minimumReleaseAge: NEVER add packages to
minimumReleaseAgeExcludein CI. Locally, ASK before adding — the age threshold is a security control. - File existence: ALWAYS
existsSyncfromnode:fs. NEVERfs.access,fs.stat-for-existence, or an asyncfileExistswrapper. Import form:import { existsSync, promises as fs } from 'node:fs'. - Null-prototype objects: ALWAYS use
{ __proto__: null, ...rest }for config, return, and internal-state objects. Prevents prototype pollution and accidental inheritance. Seesrc/socket-sdk-class.tsandsrc/file-upload.tsfor examples. - Linear references: NEVER reference Linear issues (e.g.
SOC-123,ENG-456, Linear URLs) in code, code comments, or PR titles/descriptions/review comments. Keep the codebase and PR history tool-agnostic — tracking lives in Linear.
NEVER re-race the same pool of promises across loop iterations. Each call to Promise.race([A, B, ...]) attaches fresh .then handlers to every arm; a promise that survives N iterations accumulates N handler sets. See nodejs/node#17469 and @watchable/unpromise.
- Safe:
Promise.race([fresh1, fresh2])where both arms are created per call (e.g. one-shotwithTimeoutwrappers). - Leaky:
Promise.race(pool)inside a loop wherepoolpersists across iterations (the classic concurrency-limiter bug) — also applies toPromise.anyand long-lived arms like interrupt signals. - Fix: single-waiter "slot available" signal — each task's
.thenresolves a one-shotpromiseWithResolversthat the loop awaits, then replaces. No persistent pool, nothing to stack.
Terminal symbols (from @socketsecurity/lib/logger LOG_SYMBOLS): ✓ (green), ✗ (red), ⚠ (yellow), ℹ (blue), → (cyan). Use logger methods, never manually apply colors.
import { getDefaultLogger } from '@socketsecurity/lib/logger'
const logger = getDefaultLogger()
logger.success(msg) // Green ✓
logger.fail(msg) // Red ✗
logger.warn(msg) // Yellow ⚠
logger.info(msg) // Blue ℹ
logger.step(msg) // Cyan →Emojis allowed sparingly: 📦 💡 🚀 🎉. Prefer text-based symbols for terminal compatibility.
Socket SDK for JavaScript/TypeScript — programmatic access to Socket.dev security analysis.
src/index.ts— entrysrc/socket-sdk-class.ts— SDK class with all API methodssrc/http-client.ts— request/response handlingsrc/types.ts— TypeScript definitionssrc/utils.ts— shared utilitiessrc/constants.ts— constants
Features: TypeScript support, API client, package analysis, security scanning, org/repo management, SBOM, batch operations, file uploads.
- Build:
pnpm build(pnpm build --watchfor dev — 68% faster rebuilds) - Test:
pnpm test - Type check:
pnpm run type - Lint:
pnpm run lint - Check all:
pnpm check - Coverage:
pnpm run cover
/security-scan— AgentShield + zizmor security audit/quality-scan— comprehensive code quality analysis/quality-loop— scan and fix iteratively- Agents:
code-reviewer,security-reviewer,refactor-cleaner(in.claude/agents/) - Shared subskills in
.claude/skills/_shared/ - Pipeline state tracked in
.claude/ops/queue.yaml
Configs live in .config/:
| File | Purpose |
|---|---|
tsconfig.json |
Main TS config (extends base) |
.config/tsconfig.base.json |
Base TS settings |
.config/tsconfig.check.json |
Type checking for type command |
.config/tsconfig.dts.json |
Declaration file generation |
.config/esbuild.config.mts |
Build orchestration (ESM, node18+) |
.oxlintrc.json |
oxlint rules |
.oxfmtrc.json |
oxfmt formatting |
.config/vitest.config.mts |
Main test config |
.config/vitest.config.isolated.mts |
Isolated tests (for vi.doMock()) |
.config/vitest.coverage.config.mts |
Shared coverage thresholds (≥99%) |
.config/isolated-tests.json |
List of tests requiring isolation |
.config/taze.config.mts |
Dependency update policies |
Logger calls: logger.error()/logger.log() must include empty string: logger.error(''), logger.log('').
File structure:
- Extensions:
.mtsfor TypeScript modules - 🚨 MANDATORY
@fileoverviewheaders - ❌ FORBIDDEN
"use strict"in.mjs/.mts(ES modules are strict)
TypeScript:
- Semicolons: Use them (unlike other Socket projects)
- ❌ FORBIDDEN
any; useunknownor specific types - Type imports: Always
import type(separate statements, never inlinetypein value imports) - Prefer
undefinedovernull
HTTP requests in SDK:
- 🚨 NEVER use
fetch()— usecreateGetRequest/createRequestWithJsonfromsrc/http-client.tsfetch()bypasses the SDK's HTTP stack (retries, timeouts, hooks, agent config)fetch()cannot be intercepted by nock in tests, forcing c8 ignore blocks- For external URLs (e.g., firewall API), pass a different
baseUrltocreateGetRequest
Working directory:
- 🚨 NEVER use
process.chdir()— pass{ cwd }options with absolute paths instead
Sorting (MANDATORY):
- Type properties: required first, then optional; alphabetical within groups
- Class members: 1) private properties, 2) private methods, 3) public methods (alphabetical)
- Object properties & destructuring: alphabetical (except semantic ordering)
Two vitest configs:
.config/vitest.config.mts— default.config/vitest.config.isolated.mts— full process isolation forvi.doMock()
Structure: test/ for tests, test/utils/ for shared helpers. Use descriptive names like socket-sdk-upload-manifest.test.mts.
Helpers (test/utils/environment.mts):
// Recommended: combined nock setup + client creation
import { setupTestClient } from './utils/environment.mts'
describe('My tests', () => {
const getClient = setupTestClient('test-api-token', { retries: 0 })
it('should work', async () => {
const client = getClient()
})
})Also available: setupTestEnvironment() (nock only), createTestClient() (client only), isCoverageMode (flag).
Running:
- All:
pnpm test - Specific:
pnpm test <file>(glob support) - 🚨 NEVER use
--before test paths — runs ALL tests - Coverage:
pnpm run cover
Best practices:
- Use
setupTestClient()+getClient()pattern - Mock HTTP with nock (cleaned automatically in beforeEach/afterEach)
- Test success + error paths
- Test cross-platform path handling
- See
test/unit/getapi-sendapi-methods.test.mtsfor examples
Test style — functional over source scanning: NEVER read source files and assert on their contents (.toContain('pattern')). Write functional behavior tests.
- 🚨 MANDATORY:
SocketDev/socket-registry/.github/workflows/ci.yml@<full-sha> # main - Custom runner:
scripts/test.mtswith glob expansion - Memory: auto heap (CI 8GB, local 4GB)
🚨 MANDATORY for version bumps:
- Check OpenAPI updates:
git diff v{prev}..HEAD -- types/ - Document user-facing changes: new endpoints, parameter changes, new enum values, breaking contracts
- Focus on user impact, not internal infrastructure
- CI uses published npm packages, not local
- Package detection: use
existsSync()notfs.access() - Test failures: check unused nock mocks and cleanup
- If the user's request is based on a misconception, say so before executing
- If you spot a bug adjacent to what was asked, flag it: "I also noticed X — want me to fix it?"
- You are a collaborator, not just an executor
- When a warning (lint, type-check, build, runtime) surfaces in code you're already editing, fix it in the same change — don't leave it for later. For warnings in unrelated files, flag them instead of drive-by fixing.
- Default to perfectionist mindset: when you have latitude to choose, pick the maximally correct option — no shortcuts, no cosmetic deferrals. Fix state that looks stale even if not load-bearing. If pragmatism is the right call, the user will ask for it explicitly. "Works now" ≠ "right."
- Before calling done: present two views — perfectionist reject vs. pragmatist ship — and let the user decide. If the user gives no signal, default to perfectionist: do the fuller fix.
- If a fix fails twice: stop, re-read top-down, state where the mental model was wrong, try something fundamentally different
- NEVER claim done at 80% — finish 100% before reporting
- Fix forward, don't revert; reverting requires explicit user approval
- After EVERY code change: build, test, verify, commit — one atomic unit
Use .claude/ (gitignored) for plans, intermediate analysis, logs, and cross-session context. Don't hold large analysis in context.
- After ANY user correction: log the pattern so the mistake isn't repeated
- Convert mistakes into strict rules
- After fixing a bug: explain why it happened and what category it represents
- After 10+ messages: re-read files before editing
- Before/after every edit: re-read to confirm
- Tool results over 50K chars are silently truncated — narrow scope and re-run if sparse
- Tasks touching >5 files: use sub-agents with worktree isolation
- Windows compatibility matters — test path handling
- Use utilities from
@socketsecurity/registrywhere available - Maintain consistency with surrounding code