Essential Deno TypeScript practices for ALL Deno development: configuration, imports, testing, permissions, and anti-patterns. Read this skill for any Deno project setup, dependency management, or core development work.
Provides essential Deno TypeScript best practices for configuration, imports, testing, and security. Use this skill for ALL Deno project setup, dependency management, or development work to ensure proper practices.
/plugin marketplace add jahanson/cc-plugins/plugin install deno-lsp@local-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Use this skill for ALL Deno TypeScript development:
node_modules at all costs - reduce supply chain attack surfacetsconfig.json - Deno uses deno.json(c) as the single source of truthdeno check / deno test - do not rely on external tscDeno.*, Web APIs, Fetch, URLPattern) over polyfillsAlways use the strictest possible settings in deno.json:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true
}
}
Use deno.json or deno.jsonc as the single configuration file for:
Complete Configuration Example:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"tasks": {
"dev": "deno run --watch --allow-net --allow-read --allow-env src/main.ts",
"test": "deno test --allow-net --allow-read --allow-env --coverage=coverage src/",
"test:unit": "deno test --allow-net --allow-read --allow-env --coverage=coverage src/",
"test:e2e": "deno test --allow-net --allow-read --allow-env tests/e2e/",
"test:watch": "deno test --allow-net --allow-read --allow-env --watch --fail-fast",
"coverage": "deno coverage coverage --html",
"check": "deno check $(find src -name '*.ts' -not -name '*.sql')",
"lint": "deno lint",
"fmt": "deno fmt"
},
"imports": {
"@/": "./src/",
"@/domain/": "./src/domain/",
"@/infrastructure/": "./src/infrastructure/",
"@/application/": "./src/application/",
"@std/assert": "jsr:@std/assert@^1.0.14",
"@std/fs": "jsr:@std/fs@^1.0.19",
"@std/testing": "jsr:@std/testing@^1.0.15",
"@std/ulid": "jsr:@std/ulid@1",
"zod": "npm:zod@^3.23.8"
},
"exclude": [
"coverage/",
"node_modules/"
],
"lock": true
}
Define these deno task aliases at minimum:
dev - Development with watch modetest, test:watch - Testingcoverage - Generate coverage reportscheck - Type-check all fileslint, fmt - Code qualitydeno.lock to version control--lock=deno.lock --lock-write=false in CIdeno cache --lock=deno.lock --lock-writeCRITICAL: Never use direct JSR/npm imports in source files. All external dependencies MUST be declared in deno.json import map.
Import Order in Source Files:
// 1. Standard library imports (via import map)
import { assertEquals } from "@std/assert";
// 2. Third-party imports (via import map)
import { z } from "zod";
// 3. Internal imports (absolute paths using import map)
import { Agent } from "@/domain/agent.ts";
// 4. Relative imports (only within same module/context)
import { validatePrompt } from "./validation.ts";
Use sources in this order:
jsr: registry (first choice for TypeScript modules)
"@std/assert": "jsr:@std/assert@^1.0.14"
npm: specifier (when needed; prefer ESM-compatible)
"zod": "npm:zod@^3.23.8"
URL imports (rarely needed with import maps)
CRITICAL: Version pin all external imports. No floating @latest in committed code.
{
"imports": {
"zod": "npm:zod@^3.23.8", // GOOD - pinned
"zod": "npm:zod", // BAD - no version
"@std/assert": "jsr:@std/assert@1" // GOOD - pinned
}
}
Use import map aliases for clean internal imports:
{
"imports": {
"@/": "./src/",
"@/domain/": "./src/domain/",
"@/infrastructure/": "./src/infrastructure/"
}
}
// GOOD - Clean, refactor-safe
import { Agent } from "@/domain/agent.ts";
// BAD - Brittle relative paths
import { Agent } from "../../../domain/agent.ts";
Use type-only imports when importing types:
import type { Agent } from "@/domain/agent.ts";
import type { z } from "zod";
Unit Tests - Co-located with Source:
src/
└── domain/
├── agent.ts
└── agent.test.ts # Unit tests next to code
Integration & E2E Tests - Separate Directory:
tests/
├── integration/
│ └── openai_provider.test.ts
└── e2e/
└── workflow.test.ts
Why Co-location:
deno test discoveryNon-Negotiable:
deno test --coverage=coverage
deno coverage coverage --html
Always use explicit AAA (Arrange-Act-Assert):
import { assertEquals } from "@std/assert";
Deno.test("agent should process valid input", () => {
// Arrange
const agent = new Agent({ name: "TestAgent" });
const input = "Hello, world!";
// Act
const result = agent.process(input);
// Assert
assertEquals(result.status, "success");
});
Red-Green-Refactor with fast feedback:
# Watch mode with fail-fast
deno test --watch --fail-fast
# Run specific file
deno test src/domain/agent.test.ts --watch
CRITICAL: All tests must be deterministic.
Test Flakiness Policy:
Use stable seeds and fixtures:
import { FakeTime } from "@std/testing/time";
Deno.test("timer test", () => {
using time = new FakeTime();
// Deterministic time control
time.tick(1000);
});
All test files must end with .test.ts:
agent.test.ts # GOOD
agent_test.ts # BAD
agent.spec.ts # BAD
@std/assert for assertions@std/testing/mock for test doubles@std/testing/time for time controlDefault to minimum required permissions:
# BAD
deno run --allow-all script.ts
# GOOD
deno run --allow-read=./data --allow-net=api.example.com script.ts
--allow-read[=<path>] # File system read
--allow-write[=<path>] # File system write
--allow-net[=<domain>] # Network access
--allow-env[=<var>] # Environment variables
--allow-run[=<program>] # Subprocess execution
/**
* Fetches data from API and caches locally.
*
* Required permissions:
* - --allow-net=api.example.com
* - --allow-read=./cache
* - --allow-write=./cache
*/
export async function fetchData(): Promise<Data> {
// ...
}
// BAD - Hardcoded
const apiKey = "sk-1234";
// GOOD - From environment
const apiKey = Deno.env.get("API_KEY");
if (!apiKey) {
throw new Error("API_KEY required");
}
Run with: deno run --allow-env=API_KEY script.ts
// BAD - Direct JSR/npm imports in source
import { z } from "npm:zod@^3.23.8";
// GOOD - Use import map
import { z } from "zod";
// BAD - Floating versions
"zod": "npm:zod"
// GOOD - Pinned versions
"zod": "npm:zod@^3.23.8"
// BAD - Node.js APIs
const fs = require("fs");
import * as fs from "node:fs";
// GOOD - Deno APIs
await Deno.readTextFile("file.txt");
// BAD - Unnecessary delay
await new Promise(r => setTimeout(r, 100));
// GOOD - Deterministic time
import { FakeTime } from "@std/testing/time";
using time = new FakeTime();
time.tick(100);
# BAD - Overly broad
deno run --allow-all script.ts
# GOOD - Specific
deno run --allow-read=./data script.ts
// BAD - Unnecessary async
async function validate(input: string): Promise<boolean> {
return input.length > 0;
}
// GOOD - Remove async if no await
function validate(input: string): boolean {
return input.length > 0;
}
# Run with watch
deno run --watch src/main.ts
# Type-check
deno check src/**/*.ts
# Format
deno fmt
# Lint
deno lint
# Run all tests
deno test
# With coverage
deno test --coverage=coverage
deno coverage coverage --html
# Watch mode
deno test --watch --fail-fast
# Update dependencies
deno cache --reload
# Update lockfile
deno cache --lock=deno.lock --lock-write
# Run tasks from deno.json
deno task dev
deno task test
deno task coverage
deno.json, never direct importsThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.