From ns-tools
Guide for contributing to ns-tools-atlas. Use when the user wants to add features, fix bugs, understand the codebase, or contribute to the Network School tool Atlas project. Triggers on 'contribute', 'how does this work', 'project structure', 'add a feature', 'add an endpoint', 'add a page', or general onboarding questions about this repo.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ns-tools:contributingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are helping a developer contribute to **ns-tools-atlas**, an interactive ecosystem visualization of tools in the Network School community. Use this guide to understand the codebase and give accurate, contextual answers.
You are helping a developer contribute to ns-tools-atlas, an interactive ecosystem visualization of tools in the Network School community. Use this guide to understand the codebase and give accurate, contextual answers.
pnpm install
pnpm dev:vercel # Local dev with Vercel serverless functions (recommended)
pnpm dev # Vite-only dev server (no API routes)
Requires a .env file with: DATABASE_URL, ADMIN_PASSWORD, ADMIN_TOKEN, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID. See .env.example.
api/)src/
├── pages/ # Route pages
│ ├── Index.tsx # Main ecosystem visualization
│ ├── Admin.tsx # Admin panel (password/token protected)
│ ├── Pending.tsx # Pending project approvals
│ ├── Requests.tsx # Community project requests + voting
│ ├── Graveyard.tsx # Dead/shutdown projects
│ ├── Data.tsx # Data/analytics view
│ └── NotFound.tsx
├── components/
│ ├── ecosystem/ # Core visualization components
│ │ ├── FullCanvas.tsx # D3 force-directed canvas (main viz)
│ │ ├── MobileProjectList.tsx # Masonry grid for mobile
│ │ └── AddProjectForm.tsx # Project submission form
│ └── ui/ # shadcn/ui primitives (do not edit directly)
├── hooks/
│ └── useProjects.ts # ALL React Query hooks + mutations
├── lib/
│ └── api.ts # API client — every fetch call lives here
├── types/
│ └── ecosystem.ts # Core types: EcosystemProject, ProjectRequest, Category
└── data/
├── categories.json # 10 predefined categories with colors
└── ecosystemData.ts # Category utilities, color/slug helpers
api/ # Vercel serverless functions (each file = one endpoint)
├── _db.ts # Drizzle schema + DB connection (shared)
├── projects.ts # GET approved active projects
├── graveyard.ts # GET dead projects
├── requests.ts # GET project requests
├── submit-project.ts # POST new project (goes to pending)
├── submit-request.ts # POST new project request
├── upvote.ts # POST upvote a request
├── validate-profile.ts # POST validate NS profile URL
├── pending-projects.ts # GET pending projects (admin, auth required)
├── approve-project.ts # POST approve/reject project (admin)
├── admin-data.ts # GET all projects for admin table (admin)
└── admin-update.ts # POST update project fields (admin)
All data flows through this pipeline — follow this pattern for new features:
api/_db.ts (Drizzle ORM)api/ that queries the DBsrc/lib/api.tssrc/hooks/useProjects.ts// 1. api/my-endpoint.ts
import type { VercelRequest, VercelResponse } from "@vercel/node";
import { getDb, projects } from "./_db";
import { eq } from "drizzle-orm";
export default async function handler(req: VercelRequest, res: VercelResponse) {
const db = getDb();
const result = await db
.select()
.from(projects)
.where(eq(projects.status, "active"));
return res.json(result);
}
// 2. src/lib/api.ts — add fetch function
export async function fetchMyData(): Promise<MyType[]> {
const response = await fetch("/api/my-endpoint");
if (!response.ok) throw new Error(`Failed: ${response.status}`);
return response.json();
}
// 3. src/hooks/useProjects.ts — add hook
export function useMyData() {
return useQuery({
queryKey: ["my-data"],
queryFn: fetchMyData,
staleTime: 1000 * 60 * 2,
});
}
Don't forget to register the new endpoint in vercel.json under functions if it needs custom memory/duration config.
Three tables defined in api/_db.ts:
projects — Main entities
id (text PK), name, category, description, url, guideUrl, imageUrl, emojistatus: 'active' | 'dead'approvalStatus: 'approved' | 'pending' | 'rejected'tags (text array): 'nsOfficial', 'free', 'paid'productImages (text array, max 3), nsProfileUrls (text array)customCategoryId, customCategoryName, customCategoryColor for user-defined categoriespostMortem (for graveyard projects), addedAtproject_requests — Community suggestions
id, name, description, category, submittedBy, emoji, upvotes, submittedAtrequest_upvotes — Vote tracking
requestId, voterId)To modify the schema:
# Edit api/_db.ts, then:
pnpm db:generate # Generate migration
pnpm db:migrate # Apply migration
# OR for quick iteration:
pnpm db:push # Push schema directly (no migration file)
10 predefined categories in src/data/categories.json: Networks, Coworking, Events, Media, Education, Local VCs, Global VCs, Accelerators, Corporate, Transport.
Custom categories are supported per-project via customCategoryId/customCategoryName/customCategoryColor fields. Category matching is case-insensitive.
Admin endpoints require one of these headers:
x-admin-password — the ADMIN_PASSWORD env varx-admin-token — the ADMIN_TOKEN env varPublic endpoints (projects, graveyard, requests, submit-project, submit-request, upvote) have no auth.
Auto-login via ?token= URL parameter is supported on the admin page.
@/* maps to src/*. Use @/components/ui/button not ../../components/ui/button.
pnpm dev:vercel.src/components/ui/). Don't edit these directly — use npx shadcn-ui@latest add <component> to add new ones.src/lib/api.ts. Don't fetch directly in components.src/hooks/useProjects.ts.|) in API submissions but native arrays in the DB and types.src/pages/MyPage.tsxsrc/App.tsx (or wherever routes are defined)vercel.json already catches all non-API routes to index.htmlnpx shadcn-ui@latest add <component-name>
The force simulation lives in src/components/ecosystem/FullCanvas.tsx. It uses D3 force simulation with canvas rendering. This is the most complex component — read it carefully before modifying.
The admin panel (src/pages/Admin.tsx) uses a data table for bulk project management. It connects to api/admin-data.ts (read) and api/admin-update.ts (write).
Push to main triggers Vercel deployment. The build command is:
vite build && cd docs && npm run build && cp -r build ../dist/docs
This builds both the main app and the Docusaurus docs site.
$ARGUMENTS
npx claudepluginhub 0xvitae/ns-tools-atlas --plugin ns-toolsGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.