feat: add project-level Claude Code skills for dev workflow

Copy dev workflow skills (dev, dev1-start through dev7-pr, dev-debug,
ts-style-review) to project .claude/skills/ so they're available to all
contributors. Excludes twitter agent and browseros browser skills.

Update .gitignore to track .claude/skills/ and .claude/commands/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Felarof
2026-02-20 08:56:48 -08:00
parent 1204697873
commit 1f5d9ec55c
12 changed files with 1434 additions and 1 deletions

View File

@@ -0,0 +1,69 @@
---
name: dev-debug
description: Debug an issue by identifying root causes, fixing the most probable one, testing, and committing. Use with "/dev-debug <description of the issue>".
disable-model-invocation: true
argument-hint: [issue description]
---
# Debug Workflow
You are debugging an issue. Be methodical — understand before you fix.
## Step 1: Understand the issue
1. Derive a short slug from the issue description (e.g., "login fails after redirect" → `login_redirect_fail`)
2. Create `.llm/debug_<slug>/` directory
3. Read the relevant code paths. Trace the logic that relates to the issue described in `$ARGUMENTS`.
4. Identify **2-5 possible root causes**, ranked by probability.
Write your analysis to `.llm/debug_<slug>/tmp_root_causes.md`:
```
## Issue
<restate the issue clearly>
## Root Causes (ranked by probability)
### 1. [Most likely] — <short title>
- **Why**: Explanation of why this could be the cause
- **Evidence**: What in the code supports this theory
- **Files**: Relevant file paths
### 2. <short title>
...
```
Present the root causes to the user. Ask if they agree with the ranking or want to override which one to fix first.
## Step 2: Fix the most probable root cause
After user confirms (or you proceed with #1 by default):
1. Implement the fix — keep it minimal and focused. Only change what's needed to address the root cause.
2. Follow existing code patterns and conventions.
3. No drive-by refactors — fix the bug, nothing else.
## Step 3: Test
1. Run existing tests if they cover the affected code path.
2. If no tests exist or tests don't cover this case, tell the user what to test manually and what the expected behavior should be.
3. If tests fail, fix and re-test in a loop.
## Step 4: Commit
Stage and commit the fix:
```bash
git add -A && git commit -m "fix: <concise description of what was fixed and why>"
```
## Step 5: Check for remaining issues
If the fix didn't fully resolve the issue (or if there are related problems):
1. Update `.llm/debug_<slug>/tmp_root_causes.md` — mark #1 as addressed, re-rank remaining causes
2. Go back to Step 2 with the next root cause
3. Commit after each fix
When the issue is resolved, tell the user:
> Debug complete. Fixed in N commit(s). See `.llm/debug_<slug>/tmp_root_causes.md` for the full analysis.

View File

@@ -0,0 +1,26 @@
---
name: dev
description: Full feature development workflow. Explores codebase, designs, writes PRD, implements, reviews, fixes, and creates PR. Use with "/dev <feature description>".
disable-model-invocation: true
argument-hint: [feature description]
---
# Dev Workflow — Orchestrator
Run the full feature development pipeline by invoking sub-skills sequentially. Start by invoking `/dev1-start $ARGUMENTS`. Each sub-skill will automatically chain to the next one.
## Pipeline
1. `/dev1-start` — High-level code exploration
2. `/dev2-design` — Design options (user picks one)
3. `/dev3-prd` — Questions + PRD
4. `/dev4-implement` — Implementation
5. `/dev5-review` — Code review + commit
6. `/dev6-review-fix` — Apply review fixes + commit (skipped if review is clean)
7. `/dev7-pr` — Create PR, wait for Greptile, address comments, push
## Instructions
Invoke `/dev1-start $ARGUMENTS` now. It will chain through the rest of the pipeline automatically. Each skill writes artifacts to `.llm/<feature_name>/` and hands off to the next skill.
Skills that need user input (design choice, question answers, PR approval) will pause and wait before continuing.

View File

@@ -0,0 +1,40 @@
---
name: dev1-start
description: Start a new feature development. Does a high-level exploration of the codebase to understand the stack and project layout, then kicks off the design phase. Sub-skill of the /dev workflow.
argument-hint: [feature description]
---
# Dev Workflow — Step 1: Start & Explore
You are beginning a new feature development workflow. Do a quick, high-level exploration to understand the stack, project layout, and what already exists. This is not a deep dive — just orient yourself.
## Step 1: Set up the feature workspace
1. Derive a short snake_case feature name from the user's description (e.g., "add user auth" → `add_user_auth`)
2. Create the directory `.llm/<feature_name>/`
3. Write `.llm/<feature_name>/tmp_context.md` with:
- The original feature request (verbatim from `$ARGUMENTS`)
- Timestamp
- Current working directory
## Step 2: High-level code exploration
Get a quick lay of the land:
1. **Read CLAUDE.md** — If there is a `CLAUDE.md` (or `.claude/CLAUDE.md`) in the project root, read it first. It contains project-specific instructions and context.
2. **Understand the stack** — What language(s), frameworks, and key dependencies does this project use? Check `package.json`, `Cargo.toml`, `go.mod`, or equivalent.
3. **Map the project structure** — List top-level directories and what each one is for. If it's a monorepo, identify the different packages/apps.
4. **Identify high-level features** — What does this project do? What are its main features or entry points?
Write your learnings to `.llm/<feature_name>/tmp_exploration.md`. Keep it concise — bullet points are fine.
## Step 3: Summarize for the user
Present a brief summary to the user:
- The stack and project structure
- Key projects/packages if it's a monorepo
- Where the new feature likely fits in
## Step 4: Hand off
Tell the user the exploration is complete, then immediately invoke `/dev2-design <feature_name>` (where `<feature_name>` is the slug you created in Step 1).

View File

@@ -0,0 +1,50 @@
---
name: dev2-design
description: Generate high-level design options for a feature. Presents 2-4 design alternatives with pros and cons. Sub-skill of the /dev workflow.
argument-hint: [feature_name]
---
# Dev Workflow — Step 2: Design Options
You are generating design options for a feature. Think like a staff software engineer at Google presenting options in a design review.
## Input
1. Read `.llm/$ARGUMENTS/tmp_context.md` for the original feature request
2. Read `.llm/$ARGUMENTS/tmp_exploration.md` for codebase exploration findings
## Step 1: Generate design options
Present **2 to 4 high-level design options**. For each option:
- **Name** — A short descriptive title (e.g., "Option A: Event-driven with pub/sub")
- **Overview** — 2-3 paragraphs explaining the approach at a high level. NO code snippets. Describe the architecture, data flow, and key decisions in plain language.
- **Advantages** — Bullet list of pros
- **Disadvantages** — Bullet list of cons
- **Complexity** — Low / Medium / High
- **Risk** — Low / Medium / High
If there is genuinely only one reasonable way to implement the feature, present that single option and explain why alternatives don't make sense.
## Rules
- **No code snippets** in design options. This is a high-level architectural discussion.
- Design options need not be very futureproof; prioritize solving the immediate need over speculative extensibility.
- Focus on trade-offs: maintainability vs performance, simplicity vs flexibility, etc.
- Ground each option in the actual codebase — reference real modules, patterns, and conventions found during exploration.
- Be honest about disadvantages. Don't present a straw man option just to fill the count.
## Step 2: Present and get user's choice
Present all options to the user in a clear format. Ask the user which design option they prefer (or if they want to combine aspects of multiple options).
## Step 3: Save the chosen design
After the user picks an option, write `.llm/$ARGUMENTS/design.md` with:
- The chosen design option (full description)
- Any user modifications or clarifications
- Key decisions and rationale
## Step 4: Hand off
Tell the user the design is locked in, then immediately invoke `/dev3-prd $ARGUMENTS`.

View File

@@ -0,0 +1,60 @@
---
name: dev3-prd
description: Generate questions, clarify unknowns, then write a PRD using pyramid principles. Sub-skill of the /dev workflow.
argument-hint: [feature_name]
---
# Dev Workflow — Step 3: Questions & PRD
You are writing a Product Requirements Document (PRD) / design spec. Before writing, you must clarify all unknowns.
## Input
1. Read `.llm/$ARGUMENTS/tmp_context.md` for the original feature request
2. Read `.llm/$ARGUMENTS/tmp_exploration.md` for codebase exploration findings
3. Read `.llm/$ARGUMENTS/design.md` for the chosen design
## Step 1: Generate questions
Think about what a staff software engineer would need clarified before writing a design spec. Write your questions to `.llm/$ARGUMENTS/tmp_questions.md`.
For each question:
- Write the question clearly
- Attempt to answer it yourself by reading the codebase
- Mark each question as either `[RESOLVED]` (you found the answer in the code) or `[NEEDS INPUT]` (requires human clarification)
## Step 2: Ask the human
If there are any `[NEEDS INPUT]` questions remaining, present ONLY those to the user. Wait for their answers. Update `.llm/$ARGUMENTS/tmp_questions.md` with the answers.
If all questions were self-resolved, tell the user: "No open questions — all clarified from the codebase. Proceeding to PRD."
## Step 3: Write the PRD
Write the PRD to `.llm/$ARGUMENTS/prd.md` following the **pyramid principle** in three levels:
### Level 1 — Executive Summary
1. **Requirements** — Clear bullet points listing what the feature must do. Just the requirements, nothing else.
2. **Background** — What are we building and why? Context and motivation in 2-3 paragraphs.
3. **Design Overview** — High-level overview of the chosen design. How the pieces fit together. No code.
### Level 2 — Component Details
For each major component or module in the design:
- **One paragraph** explaining what this component does, its responsibilities, and how it interacts with other components.
### Level 3 — Implementation Details
For each component from Level 2:
- Code snippets showing key interfaces, data structures, or function signatures
- Specific file paths where changes will be made
- Any migration or configuration changes needed
## Step 4: Present and confirm
Show the user a summary of the PRD (Level 1 only) and ask if they want to review the full document or proceed.
## Step 5: Hand off
Tell the user the PRD is complete, then immediately invoke `/dev4-implement $ARGUMENTS`.

View File

@@ -0,0 +1,65 @@
---
name: dev4-implement
description: Implement a feature from its PRD. Creates a work tree if needed, writes clean code following Google-level standards, and tests iteratively. Sub-skill of the /dev workflow.
argument-hint: [feature_name]
---
# Dev Workflow — Step 4: Implement
You are implementing a feature from its PRD. Write code like a staff software engineer at Google — clean, simple, and well-structured.
## Input
1. Read `.llm/$ARGUMENTS/prd.md` for the full PRD
2. Read `.llm/$ARGUMENTS/design.md` for design decisions
3. Read `.llm/$ARGUMENTS/tmp_exploration.md` for codebase context
## Step 1: Set up work tree
Check if you are already in a git work tree (not the main tree):
```bash
git rev-parse --is-inside-work-tree && git worktree list
```
If you are in the **main work tree** (not a feature worktree), create one:
```bash
wt switch -c feat/$ARGUMENTS
```
If already in a feature worktree, continue in place.
## Step 2: Plan implementation order
Break the PRD into small, testable implementation steps. Write the plan to `.llm/$ARGUMENTS/tmp_impl_plan.md`. Each step should be:
- Small enough to verify independently
- Ordered so that dependencies come first
- Testable (you can run something to verify it works)
## Step 3: Implement step by step
For each step in the plan:
1. Write the code
2. Test it (run existing tests, or manually verify)
3. Fix any failures before moving to the next step
## Code Style Guide
Follow these rules strictly:
- **No excessive console.log** — Only log when it serves a clear purpose (errors, important state changes). Remove debug logs.
- **Self-contained functions** — Each function should do one thing. No function should exceed 20-30 lines.
- **Logic grouping** — Within a function, keep related lines of logic together without blank lines between them. Use a blank line only to separate distinct logical blocks.
- **Comments** — Only add a comment when the logic is not self-evident. The comment should explain *why*, not *what*. Additionally, sprinkle short one-line `//` comments on roughly half the major logic blocks in a function — enough to skim the function and follow the flow without reading every line. Keep these brief (e.g., `// validate input`, `// build response payload`). Not every block needs one, but the big chunks should be signposted.
- **Simple and direct** — No premature abstractions. No over-engineering. Write the simplest code that solves the problem.
- **Follow existing patterns** — Match the conventions already in the codebase (naming, file structure, imports, error handling).
## Step 4: Verify
After all steps are implemented:
1. Run the full test suite
2. Manually verify the feature works as described in the PRD
3. Fix anything that fails — loop back to implementation until it passes
## Step 5: Hand off
Tell the user implementation is complete, then immediately invoke `/dev5-review $ARGUMENTS`.

View File

@@ -0,0 +1,80 @@
---
name: dev5-review
description: Review implemented code for quality, correctness, and style. Produces review comments and creates a commit. Sub-skill of the /dev workflow.
argument-hint: [feature_name]
---
# Dev Workflow — Step 5: Code Review
You are reviewing code like a senior engineer doing a thorough code review. Be constructive but rigorous.
## Input
1. Read `.llm/$ARGUMENTS/prd.md` for what the feature should do
2. Read `.llm/$ARGUMENTS/design.md` for the chosen design
3. Run `git diff` to see all changes made during implementation
## Step 1: Style guide review
If the project uses TypeScript, invoke `/ts-style-review` to check all changed files against the Google TypeScript Style Guide and team conventions. Incorporate its findings into your review comments.
## Step 2: Review the code
Review every changed file. Check for:
### Correctness
- Does the implementation match the PRD requirements?
- Are there edge cases not handled?
- Are there logical errors?
### Code Quality
- Functions under 20-30 lines?
- Logic grouped without unnecessary blank lines?
- No excessive console.log statements?
- Comments only where logic is non-obvious (explaining *why*, not *what*)?
- Self-contained functions that do one thing?
### Architecture
- Does it follow existing codebase patterns?
- Are there unnecessary abstractions or over-engineering?
- Is the code simple and direct?
### Safety
- Any security issues (injection, XSS, etc.)?
- Proper error handling at system boundaries?
- No leaked secrets or credentials?
## Step 3: Write review comments
Write review comments to `.llm/$ARGUMENTS/tmp_review.md` in this format:
```
## Review Comments
### [file_path:line_number] — severity (critical/suggestion/nit)
Description of the issue and suggested fix.
### [file_path:line_number] — severity
...
```
## Step 4: Present review to user
Show the user a summary:
- Total files reviewed
- Number of critical / suggestion / nit comments
- Top 3 most important issues (if any)
## Step 5: Commit
Stage all changes and create a commit with a clear, descriptive commit message that summarizes the feature:
```bash
git add -A && git commit -m "feat: <concise description of what was built>"
```
## Step 6: Hand off
Tell the user the review summary, then:
- If there are critical or suggestion comments: immediately invoke `/dev6-review-fix $ARGUMENTS`
- If there are zero actionable comments (only nits or clean code): skip dev6 and immediately invoke `/dev7-pr $ARGUMENTS`

View File

@@ -0,0 +1,44 @@
---
name: dev6-review-fix
description: Apply fixes for review comments from dev5-review and commit. Sub-skill of the /dev workflow.
argument-hint: [feature_name]
---
# Dev Workflow — Step 6: Fix Review Comments
You are applying fixes for the code review comments.
## Input
1. Read `.llm/$ARGUMENTS/tmp_review.md` for the review comments
2. Read `.llm/$ARGUMENTS/prd.md` for context on what the feature should do
## Step 1: Triage comments
Read all review comments. Group them:
- **Critical** — Must fix. Bugs, security issues, correctness problems.
- **Suggestions** — Should fix. Code quality, readability improvements.
- **Nits** — Fix if quick. Minor style preferences.
## Step 2: Apply fixes
For each comment (starting with critical, then suggestions, then nits):
1. Read the referenced file and line
2. Apply the fix
3. Verify the fix doesn't break anything (run tests if applicable)
## Step 3: Verify
Run the test suite to make sure fixes didn't introduce regressions.
## Step 4: Commit
Stage and commit the review fixes:
```bash
git add -A && git commit -m "fix: address review comments for $ARGUMENTS"
```
## Step 5: Hand off
Tell the user review fixes are applied and committed, then immediately invoke `/dev7-pr $ARGUMENTS`.

View File

@@ -0,0 +1,88 @@
---
name: dev7-pr
description: Create a PR, push to GitHub, wait for Greptile review, address comments, and push final. Sub-skill of the /dev workflow.
argument-hint: [feature_name]
---
# Dev Workflow — Step 7: PR & External Review
You are creating a pull request, waiting for automated review (Greptile), and addressing any comments.
## Input
1. Read `.llm/$ARGUMENTS/prd.md` for the feature summary
2. Read `.llm/$ARGUMENTS/design.md` for design context
3. Run `git log --oneline -10` to see recent commits
## Step 1: Push and create PR
Push the current branch and create a PR:
```bash
git push -u origin HEAD
```
Then create the PR using the PRD Level 1 summary as the body:
```bash
gh pr create --title "feat: <concise feature title>" --body "<PR body from PRD Level 1>"
```
The PR body should include:
- **Summary** — 2-3 bullet points from the PRD requirements
- **Design** — One paragraph on the chosen approach
- **Test plan** — How to verify the feature works
## Step 2: Wait for Greptile review
Tell the user: "Waiting 10 minutes for Greptile to review the PR..."
Start a timer:
```bash
echo "Waiting for Greptile review..." && sleep 600 && echo "Timer complete — checking for PR comments."
```
## Step 3: Pull PR comments
After the timer, fetch PR comments:
```bash
gh pr view --comments
```
Also check the review comments:
```bash
gh api repos/{owner}/{repo}/pulls/{pr_number}/comments
```
## Step 4: Address PR comments
If there are review comments from Greptile or other reviewers:
1. Read each comment
2. Apply the fix to the relevant file
3. Keep fixes focused — only change what the comment asks for
If there are no comments, skip to Step 5.
## Step 5: Commit and push
If fixes were made:
```bash
git add -A && git commit -m "fix: address PR review comments for $ARGUMENTS" && git push
```
## Step 6: Done
Tell the user:
> PR created and review comments addressed. The feature is ready for human review.
> PR URL: <url from gh pr create>
Write a final summary to `.llm/$ARGUMENTS/tmp_done.md` with:
- PR URL
- Total commits
- Summary of what was built
- Any follow-up items or tech debt noted during review

View File

@@ -0,0 +1,87 @@
---
name: ts-style-review
description: Review TypeScript code against the Google TypeScript Style Guide plus team conventions. Use when reviewing TS code for style, naming, imports, type usage, or quality.
---
# TypeScript Style Guide Review
When reviewing TypeScript code, check against these sections. For full rules and examples, see [google-ts-styleguide.md](google-ts-styleguide.md).
## 1. Source File Structure
- File order: copyright, `@fileoverview`, imports, implementation
- Use named exports only — no default exports
- Use ES6 modules — no `namespace`, no `require()`
- Prefer namespace imports for large APIs, named imports for frequently used symbols
- No mutable exports (`export let`) — use getter functions instead
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 3. Source file structure
## 2. Variables & Literals
- `const` by default, `let` when reassigned, never `var`
- One variable per declaration
- Single quotes for strings, template literals for concatenation
- No `Array()` or `Object()` constructors
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 4. Language features
## 3. Classes
- Use `readonly` for properties never reassigned after constructor
- Use parameter properties (`constructor(private readonly foo: Foo)`)
- No `public` keyword — TypeScript is public by default
- No `#private` fields — use `private` modifier
- No prototype manipulation
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 4. Classes
## 4. Functions
- Prefer function declarations for named functions
- Arrow functions for callbacks and nested functions — never function expressions
- Use rest parameters, not `arguments`
- Use spread syntax, not `Function.prototype.apply`
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 4. Functions
## 5. Control Flow & Error Handling
- Always use braces for control flow blocks
- Always `===` and `!==` (exception: `== null` for null/undefined check)
- `for...of` for arrays, never `for...in`
- Only throw `Error` instances (or subclasses), never strings
- All `switch` must have `default`, no fall-through
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 4. Control structures
## 6. Naming
- `UpperCamelCase`: classes, interfaces, types, enums, decorators, TSX components
- `lowerCamelCase`: variables, parameters, functions, methods, properties
- `CONSTANT_CASE`: global constants, enum values
- Descriptive names — no ambiguous abbreviations
- Treat acronyms as words (`loadHttpUrl`, not `loadHTTPURL`)
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 5. Naming
## 7. Type System
- Prefer `interface` over `type` alias for object shapes
- Use `unknown` over `any` — narrow with type guards
- Use optional (`?`) over `| undefined`
- `T[]` for simple types, `Array<T>` for complex
- No wrapper types (`String`, `Boolean`, `Number`) — use primitives
- No `@ts-ignore` or `@ts-nocheck`
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 6. Type system
## 8. Schema & Type Definitions (Team Convention)
- Use **Zod** for defining schemas, then derive TypeScript types with `z.infer<typeof schema>`
- If types are **not shared** across files, define the Zod schema and inferred type at the **top of the same file** that uses them
- If types **are shared**, place them in a shared types/schemas module
- Example:
```ts
import { z } from 'zod';
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
```
## 9. Comments & Documentation
- `/** JSDoc */` for public API documentation
- `//` for implementation comments
- No type annotations in JSDoc — TypeScript handles types
- Mark deprecated code with `@deprecated`
- See [google-ts-styleguide.md](google-ts-styleguide.md) § 8. Comments and documentation

View File

@@ -0,0 +1,821 @@
Here is the Google TypeScript Style Guide converted to Markdown.
# Google TypeScript Style Guide
This guide is based on the internal Google TypeScript style guide, but it has been slightly adjusted to remove Google-internal sections. Google's internal environment has different constraints on TypeScript than you might find outside of Google. The advice here is specifically useful for people authoring code they intend to import into Google, but otherwise may not apply in your external environment.
There is no automatic deployment process for this version as it's pushed on-demand by volunteers.
---
## 1. Introduction
### Terminology notes
This Style Guide uses [RFC 2119](https://tools.ietf.org/html/rfc2119) terminology when using the phrases *must*, *must not*, *should*, *should not*, and *may*. The terms *prefer* and *avoid* correspond to *should* and *should not*, respectively. Imperative and declarative statements are prescriptive and correspond to *must*.
### Guide notes
All examples given are **non-normative** and serve only to illustrate the normative language of the style guide. That is, while the examples are in Google Style, they may not illustrate the *only* stylish way to represent the code. Optional formatting choices made in examples must not be enforced as rules.
## 2. Source file basics
### File encoding: UTF-8
Source files are encoded in **UTF-8**.
#### Whitespace characters
Aside from the line terminator sequence, the ASCII horizontal space character (0x20) is the only whitespace character that appears anywhere in a source file. This implies that all other whitespace characters in string literals are escaped.
#### Special escape sequences
For any character that has a special escape sequence (`\'`, `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`), that sequence is used rather than the corresponding numeric escape (e.g `\x0a`, `\u000a`, or `\u{a}`). Legacy octal escapes are never used.
#### Non-ASCII characters
For the remaining non-ASCII characters, use the actual Unicode character (e.g. `∞`). For non-printable characters, the equivalent hex or Unicode escapes (e.g. `\u221e`) can be used along with an explanatory comment.
**Good:**
```ts
// Perfectly clear, even without a comment.
const units = 'μs';
// Use escapes for non-printable characters.
const output = '\ufeff' + content; // byte order mark
```
**Bad:**
```ts
// Hard to read and prone to mistakes, even with the comment.
const units = '\u03bcs'; // Greek letter mu, 's'
// The reader has no idea what this is.
const output = '\ufeff' + content;
```
## 3. Source file structure
Files consist of the following, **in order**:
1. Copyright information, if present
2. JSDoc with `@fileoverview`, if present
3. Imports, if present
4. The files implementation
**Exactly one blank line** separates each section that is present.
### Copyright information
If license or copyright information is necessary in a file, add it in a JSDoc at the top of the file.
### `@fileoverview` JSDoc
A file may have a top-level `@fileoverview` JSDoc. If present, it may provide a description of the file's content, its uses, or information about its dependencies. Wrapped lines are not indented.
**Example:**
```ts
/**
* @fileoverview Description of file. Lorem ipsum dolor sit amet, consectetur
* adipiscing elit, sed do eiusmod tempor incididunt.
*/
```
### Imports
There are four variants of import statements in ES6 and TypeScript:
| Import type | Example | Use for |
| :--- | :--- | :--- |
| module | `import * as foo from '...';` | TypeScript imports |
| named | `import {SomeThing} from '...';` | TypeScript imports |
| default | `import SomeThing from '...';` | Only for other external code that requires them |
| side-effect | `import '...';` | Only to import libraries for their side-effects on load (such as custom elements) |
```ts
// Good: choose between two options as appropriate (see below).
import * as ng from '@angular/core';
import {Foo} from './foo';
// Only when needed: default imports.
import Button from 'Button';
// Sometimes needed to import libraries for their side effects:
import 'jasmine';
import '@polymer/paper-button';
```
#### Import paths
TypeScript code *must* use paths to import other TypeScript code. Paths *may* be relative, i.e. starting with `.` or `..`, or rooted at the base directory, e.g. `root/path/to/file`.
Code *should* use relative imports (`./foo`) rather than absolute imports `path/to/foo` when referring to files within the same (logical) project as this allows to move the project around without introducing changes in these imports.
Consider limiting the number of parent steps (`../../../`) as those can make module and path structures hard to understand.
```ts
import {Symbol1} from 'path/from/root';
import {Symbol2} from '../parent/file';
import {Symbol3} from './sibling';
```
#### Namespace versus named imports
Both namespace and named imports can be used.
Prefer named imports for symbols used frequently in a file or for symbols that have clear names, for example Jasmine's `describe` and `it`. Named imports can be aliased to clearer names as needed with `as`.
Prefer namespace imports when using many different symbols from large APIs. A namespace import, despite using the `*` character, is not comparable to a "wildcard" import as seen in other languages. Instead, namespace imports give a name to all the exports of a module, and each exported symbol from the module becomes a property on the module name. Namespace imports can aid readability for exported symbols that have common names like `Model` or `Controller` without the need to declare aliases.
**Bad:**
```ts
// Bad: overlong import statement of needlessly namespaced names.
import {Item as TableviewItem, Header as TableviewHeader, Row as TableviewRow,
Model as TableviewModel, Renderer as TableviewRenderer} from './tableview';
let item: TableviewItem|undefined;
```
**Good:**
```ts
// Better: use the module for namespacing.
import * as tableview from './tableview';
let item: tableview.Item|undefined;
```
**Bad:**
```ts
import * as testing from './testing';
// Bad: The module name does not improve readability.
testing.describe('foo', () => {
testing.it('bar', () => {
testing.expect(null).toBeNull();
testing.expect(undefined).toBeUndefined();
});
});
```
**Good:**
```ts
// Better: give local names for these common functions.
import {describe, it, expect} from './testing';
describe('foo', () => {
it('bar', () => {
expect(null).toBeNull();
expect(undefined).toBeUndefined();
});
});
```
**Special case: Apps JSPB protos**
Apps JSPB protos must use named imports, even when it leads to long import lines. This rule exists to aid in build performance and dead code elimination.
```ts
// Good: import the exact set of symbols you need from the proto file.
import {Foo, Bar} from './foo.proto';
function copyFooBar(foo: Foo, bar: Bar) {...}
```
#### Renaming imports
Code *should* fix name collisions by using a namespace import or renaming the exports themselves. Code *may* rename imports (`import {SomeThing as SomeOtherThing}`) if needed.
Three examples where renaming can be helpful:
1. If it's necessary to avoid collisions with other imported symbols.
2. If the imported symbol name is generated.
3. If importing symbols whose names are unclear by themselves, renaming can improve code clarity.
### Exports
Use named exports in all code:
```ts
// Use named exports:
export class Foo { ... }
```
Do not use default exports. This ensures that all imports follow a uniform pattern.
**Bad:**
```ts
// Do not use default exports:
export default class Foo { ... } // BAD!
```
> **Why?** Default exports provide no canonical name, which makes central maintenance difficult with relatively little benefit to code owners. Named exports have the benefit of erroring when import statements try to import something that hasn't been declared.
#### Export visibility
TypeScript does not support restricting the visibility for exported symbols. Only export symbols that are used outside of the module. Generally minimize the exported API surface of modules.
#### Mutable exports
Regardless of technical support, mutable exports can create hard to understand and debug code. One way to paraphrase this style point is that `export let` is not allowed.
**Bad:**
```ts
export let foo = 3;
// In pure ES6, foo is mutable and importers will observe the value change after a second.
// In TS, if foo is re-exported by a second file, importers will not see the value change.
window.setTimeout(() => {
foo = 4;
}, 1000 /* ms */);
```
If one needs to support externally accessible and mutable bindings, they *should* instead use explicit getter functions.
**Good:**
```ts
let foo = 3;
window.setTimeout(() => {
foo = 4;
}, 1000 /* ms */);
// Use an explicit getter to access the mutable export.
export function getFoo() { return foo; };
```
#### Container classes
Do not create container classes with static methods or properties for the sake of namespacing.
**Bad:**
```ts
export class Container {
static FOO = 1;
static bar() { return 1; }
}
```
Instead, export individual constants and functions:
**Good:**
```ts
export const FOO = 1;
export function bar() { return 1; }
```
### Import and export type
#### Import type
You may use `import type {...}` when you use the imported symbol only as a type. Use regular imports for values:
```ts
import type {Foo} from './foo';
import {Bar} from './foo';
import {type Foo, Bar} from './foo';
```
> **Why?** The TypeScript compiler automatically handles the distinction and does not insert runtime loads for type references. This distinction is useful for compiler modes (`isolatedModules`) and build tools.
#### Export type
Use `export type` when re-exporting a type, e.g.:
```ts
export type {AnInterface} from './foo';
```
### Use modules not namespaces
TypeScript supports two methods to organize code: *namespaces* and *modules*, but namespaces are disallowed. Your code *must* refer to code in other files using imports and exports.
Your code *must not* use the `namespace Foo { ... }` construct. `namespace`s *may* only be used when required to interface with external, third party code.
Code *must not* use `require` (as in `import x = require('...');`) for imports. Use ES6 module syntax.
**Bad:**
```ts
// Bad: do not use namespaces:
namespace Rocket {
function launch() { ... }
}
// Bad: do not use <reference>
/// <reference path="..."/>
// Bad: do not use require()
import x = require('mydep');
```
## 4. Language features
### Local variable declarations
#### Use const and let
Always use `const` or `let` to declare variables. Use `const` by default, unless a variable needs to be reassigned. Never use `var`.
```ts
const foo = otherValue; // Use if "foo" never changes.
let bar = someValue; // Use if "bar" is ever assigned into later on.
```
**Bad:**
```ts
var foo = someValue; // Don't use - var scoping is complex and causes bugs.
```
#### One variable per declaration
Every local variable declaration declares only one variable: declarations such as `let a = 1, b = 2;` are not used.
### Array literals
#### Do not use the `Array` constructor
*Do not* use the `Array()` constructor, with or without `new`. It has confusing and contradictory usage.
**Bad:**
```ts
const a = new Array(2); // [undefined, undefined]
const b = new Array(2, 3); // [2, 3];
```
**Good:**
```ts
const a = [2];
const b = [2, 3];
// Equivalent to Array(2):
const c = [];
c.length = 2;
// [0, 0, 0, 0, 0]
Array.from<number>({length: 5}).fill(0);
```
#### Do not define properties on arrays
Do not define or use non-numeric properties on an array (other than `length`). Use a `Map` (or `Object`) instead.
#### Using spread syntax
Using spread syntax `[...foo];` is a convenient shorthand for shallow-copying or concatenating iterables.
When using spread syntax, the value being spread *must* match what is being created. When creating an array, only spread iterables. Primitives (including `null` and `undefined`) *must not* be spread.
#### Array destructuring
Array literals may be used on the left-hand side of an assignment to perform destructuring.
```ts
const [a, b, c, ...rest] = generateResults();
let [, b,, d] = someArray;
```
Destructuring may also be used for function parameters. Always specify `[]` as the default value if a destructured array parameter is optional.
**Good:**
```ts
function destructured([a = 4, b = 2] = []) { }
```
**Disallowed:**
```ts
function badDestructuring([a, b] = [4, 2]) { }
```
### Object literals
#### Do not use the `Object` constructor
The `Object` constructor is disallowed. Use an object literal (`{}` or `{a: 0, b: 1, c: 2}`) instead.
#### Iterating objects
Iterating objects with `for (... in ...)` is error prone. It will include enumerable properties from the prototype chain. Either filter values explicitly with an `if` statement, or use `for (... of Object.keys(...))`.
**Bad:**
```ts
for (const x in someObj) {
// x could come from some parent prototype!
}
```
**Good:**
```ts
for (const x in someObj) {
if (!someObj.hasOwnProperty(x)) continue;
// now x was definitely defined on someObj
}
for (const x of Object.keys(someObj)) { // note: for _of_!
// now x was definitely defined on someObj
}
for (const [key, value] of Object.entries(someObj)) { // note: for _of_!
// now key was definitely defined on someObj
}
```
#### Using spread syntax
Using spread syntax `[...foo]` is a convenient shorthand for creating a shallow copy of an object. When creating an object, only objects may be spread; arrays and primitives *must not* be spread.
#### Computed property names
Computed property names (e.g. `{['key' + foo()]: 42}`) are allowed.
#### Object destructuring
Object destructuring patterns may be used on the left-hand side of an assignment.
Destructured objects may also be used as function parameters, but should be kept as simple as possible: a single level of unquoted shorthand properties.
**Good:**
```ts
interface Options {
num?: number;
str?: string;
}
function destructured({num, str = 'default'}: Options = {}) {}
```
**Disallowed:**
```ts
function nestedTooDeeply({x: {num, str}}: {x: Options}) {}
function nontrivialDefault({num, str}: Options = {num: 42, str: 'default'}) {}
```
### Classes
#### Class declarations
Class declarations *must not* be terminated with semicolons.
**Good:**
```ts
class Foo {
}
```
**Bad:**
```ts
class Foo {
}; // Unnecessary semicolon
```
Statements that contain class expressions *must* be terminated with a semicolon.
#### Class method declarations
Class method declarations *must not* use a semicolon to separate individual method declarations. Method declarations should be separated from surrounding code by a single blank line.
**Good:**
```ts
class Foo {
doThing() {
console.log("A");
}
getOtherThing(): number {
return 4;
}
}
```
##### Overriding toString
The `toString` method may be overridden, but must always succeed and never have visible side effects.
#### Static methods
**Avoid private static methods:** Prefer module-local functions over private static methods.
**Do not rely on dynamic dispatch:** Code *should not* rely on dynamic dispatch of static methods.
**Avoid static `this` references:** Code *must not* use `this` in a static context.
#### Constructors
Constructor calls *must* use parentheses, even when no arguments are passed: `const x = new Foo();`.
It is unnecessary to provide an empty constructor or one that simply delegates into its parent class because ES2015 provides a default class constructor. However constructors with parameter properties, visibility modifiers or parameter decorators *should not* be omitted.
The constructor should be separated from surrounding code both above and below by a single blank line.
#### Class members
**No #private fields:** Do not use private fields (also known as private identifiers like `#ident`). Instead, use TypeScript's visibility annotations (`private ident`).
**Use readonly:** Mark properties that are never reassigned outside of the constructor with the `readonly` modifier.
**Parameter properties:** Use TypeScript parameter properties rather than plumbing an obvious initializer through to a class member.
**Good:**
```ts
class Foo {
constructor(private readonly barService: BarService) {}
}
```
**Field initializers:** If a class member is not a parameter, initialize it where it's declared.
**Properties used outside of class lexical scope:** Properties used from outside the lexical scope (e.g. Angular templates) *must not* use `private` visibility. Use `protected` or `public`.
##### Getters and setters
Getters and setters *may* be used. The getter method *must* be a pure function.
##### Computed properties
Computed properties may only be used in classes when the property is a symbol.
#### Visibility
Restricting visibility helps with keeping code decoupled.
* Limit symbol visibility as much as possible.
* TypeScript symbols are public by default. Never use the `public` modifier except when declaring non-readonly public parameter properties.
#### Disallowed class patterns
**Class prototypes:** Do not manipulate `prototype`s directly.
### Functions
#### Prefer function declarations for named functions
Prefer function declarations over arrow functions or function expressions when defining named functions.
**Good:**
```ts
function foo() {
return 42;
}
```
**Bad:**
```ts
const foo = () => 42;
```
#### Nested functions
Functions nested within other methods or functions *may* use function declarations or arrow functions. In method bodies, arrow functions are preferred because they have access to the outer `this`.
#### Do not use function expressions
Do not use function expressions. Use arrow functions instead.
#### Arrow function bodies
Use arrow functions with concise bodies (expressions) or block bodies as appropriate. Only use a concise body if the return value of the function is actually used.
**Good:**
```ts
// GOOD: return value is unused, use a block body.
myPromise.then(v => {
console.log(v);
});
// GOOD: explicit `void` ensures no leaked return value
myPromise.then(v => void console.log(v));
```
#### Rebinding `this`
Function expressions and function declarations *must not* use `this` unless they specifically exist to rebind the `this` pointer. Prefer arrow functions.
#### Prefer passing arrow functions as callbacks
Callbacks can be invoked with unexpected arguments. Prefer passing an arrow-function that explicitly forwards parameters to the named callback.
```ts
// GOOD: Arguments are explicitly passed to the callback
const numbers = ['11', '5', '3'].map((n) => parseInt(n));
```
#### Arrow functions as properties
Classes usually *should not* contain properties initialized to arrow functions. Code *should* always use arrow functions to call instance methods.
#### Event handlers
Event handlers *may* use arrow functions when there is no need to uninstall the handler. If the handler requires uninstallation, arrow function properties are the right approach.
#### Parameter initializers
Optional function parameters *may* be given a default initializer. Initializers *must not* have any observable side effects.
#### Rest and spread
Use a *rest* parameter instead of accessing `arguments`. Use function spread syntax instead of `Function.prototype.apply`.
### this
Only use `this` in class constructors and methods, functions that have an explicit `this` type declared, or in arrow functions defined in a scope where `this` may be used. Never use `this` to refer to the global object.
### Primitive literals
#### String literals
**Use single quotes:** Ordinary string literals are delimited with single quotes (`'`), rather than double quotes (`"`).
**No line continuations:** Do not use line continuations (`\`) in string literals.
**Template literals:** Use template literals (delimited with `` ` ``) over complex string concatenation.
#### Number literals
Numbers may be specified in decimal, hex, octal, or binary. Use exactly `0x`, `0o`, and `0b` prefixes. Never include a leading zero unless it is immediately followed by `x`, `o`, or `b`.
#### Type coercion
TypeScript code *may* use the `String()` and `Boolean()` functions, string template literals, or `!!` to coerce types.
Values of enum types *must not* be converted to booleans with `Boolean()` or `!!`, and must instead be compared explicitly.
Code *must* use `Number()` to parse numeric values, and *must* check its return for `NaN` values explicitly. Code *must not* use unary plus (`+`) to coerce strings to numbers. Code *must not* use `parseInt` or `parseFloat` to parse numbers, except for non-base-10 strings.
### Control structures
#### Control flow statements and blocks
Control flow statements (`if`, `else`, `for`, `do`, `while`, etc) always use braced blocks, even if the body contains only a single statement.
**Exception:** `if` statements fitting on one line *may* elide the block.
**Assignment in control statements:** Prefer to avoid assignment of variables inside control statements.
#### Iterating containers
Prefer `for (... of someArr)` to iterate over arrays. Do not use `for (... in ...)` to iterate over arrays.
#### Grouping parentheses
Optional grouping parentheses are omitted only when the author and reviewer agree that there is no reasonable chance that the code will be misinterpreted without them.
#### Exception handling
**Instantiate errors using `new`:** Always use `new Error()` when instantiating exceptions.
**Only throw errors:** Only throw (subclasses of) `Error`. Do not throw strings or raw objects.
**Catching and rethrowing:** When catching errors, code *should* assume that all thrown errors are instances of `Error`.
**Empty catch blocks:** It is very rarely correct to do nothing in response to a caught exception. Add a comment if it is intentional.
#### Switch statements
All `switch` statements *must* contain a `default` statement group. Non-empty statement groups *must not* fall through.
#### Equality checks
Always use triple equals (`===`) and not equals (`!==`).
**Exception:** Comparisons to the literal `null` value *may* use the `==` and `!=` operators to cover both `null` and `undefined`.
#### Type and non-nullability assertions
Type assertions (`x as SomeType`) and non-nullability assertions (`y!`) are unsafe. You *should not* use them without an obvious or explicit reason.
**Syntax:** Type assertions *must* use the `as` syntax (as opposed to `<Foo>`).
### Decorators
Do not define new decorators. Only use the decorators defined by frameworks (Angular, Polymer).
When using decorators, the decorator *must* immediately precede the symbol it decorates, with no empty lines between.
### Disallowed features
* **Wrapper objects:** TypeScript code *must not* instantiate the wrapper classes for the primitive types `String`, `Boolean`, and `Number`.
* **Automatic Semicolon Insertion:** Explicitly end all statements using a semicolon.
* **Const enums:** Code *must not* use `const enum`; use plain `enum` instead.
* **Debugger statements:** *Must not* be included in production code.
* **`with`:** Do not use the `with` keyword.
* **Dynamic code evaluation:** Do not use `eval` or the `Function(...string)` constructor.
* **Non-standard features:** Do not use non-standard ECMAScript or Web Platform features.
* **Modifying builtin objects:** Never modify builtin types.
## 5. Naming
### Identifiers
Identifiers *must* use only ASCII letters, digits, underscores, and (rarely) the '$' sign.
**Descriptive names:** Names *must* be descriptive and clear. Do not use abbreviations that are ambiguous.
**Camel case:** Treat abbreviations like acronyms as whole words (e.g., `loadHttpUrl`, not `loadHTTPURL`).
### Rules by identifier type
| Style | Category |
| :--- | :--- |
| `UpperCamelCase` | class / interface / type / enum / decorator / type parameters / component functions in TSX |
| `lowerCamelCase` | variable / parameter / function / method / property / module alias |
| `CONSTANT_CASE` | global constant values, including enum values |
| `#ident` | private identifiers are never used |
**Type parameters:** Like in `Array<T>`, *may* use a single upper case character (`T`) or `UpperCamelCase`.
**Private properties:** Do not use trailing or leading underscores for private properties.
**Constants:** `CONSTANT_CASE` indicates that a value is *intended* to not be changed.
**Imports:** Module namespace imports are `lowerCamelCase` (e.g. `import * as fooBar`).
## 6. Type system
### Type inference
Code *may* rely on type inference. Leave out type annotations for trivially inferred types. Explicitly specifying types may be required to prevent generic type parameters from being inferred as `unknown`.
### Undefined and null
TypeScript supports `undefined` and `null` types. TypeScript code can use either.
**Nullable/undefined type aliases:** Type aliases *must not* include `|null` or `|undefined` in a union type. Code *must* only add them when the alias is actually used.
**Prefer optional over `|undefined`:** Use optional fields (`?`) rather than a `|undefined` type.
### Use structural types
Use interfaces to define structural types, not classes.
### Prefer interfaces over type literal aliases
When declaring types for objects, use interfaces instead of a type alias (`type Foo = {...}`) for the object literal expression.
### `Array<T>` Type
For simple types, use the syntax sugar `T[]`. For anything more complex, use the longer form `Array<T>`.
### Indexable types
In TypeScript, provide a meaningful label for the key in index signatures (e.g. `{[userName: string]: number}`).
### Mapped and conditional types
Always use the simplest type construct that can possibly express your code. Mapped & conditional types may be used, but avoid complexity.
### `any` Type
**Consider *not* to use `any`.** In circumstances where you want to use `any`, consider:
* Providing a more specific type (interfaces, inline types).
* Using `unknown`.
* Suppressing the lint warning and documenting why.
### `unknown` over `any`
Use `unknown` when a type is truly unknown. Narrow the type using a type guard to use it safely.
### `{}` Type
Google3 code **should not** use `{}` for most use cases. Use `unknown`, `Record<string, T>`, or `object` instead.
### Tuple types
Use tuple types `[string, string]` instead of creating specific "Pair" interfaces if appropriate.
### Wrapper types
Never use `String`, `Boolean`, `Number`, or `Object` types. Use `string`, `boolean`, `number`, `{}`, or `object`.
## 7. Toolchain requirements
### TypeScript compiler
All TypeScript files must pass type checking using the standard tool chain.
**`@ts-ignore`:** Do not use `@ts-ignore`, `@ts-expect-error`, or `@ts-nocheck`. You *may* use `@ts-expect-error` in unit tests, though generally *should not*.
## 8. Comments and documentation
### JSDoc versus comments
* Use `/** JSDoc */` comments for documentation (users of the code).
* Use `// line comments` for implementation comments.
### JSDoc general form
```ts
/**
* Multiple lines of JSDoc text are written here,
* wrapped normally.
* @param arg A number to do something to.
*/
function doSomething(arg: number) { … }
```
### JSDoc tags
Most tags must occupy their own line.
### Document all top-level exports of modules
Use `/** JSDoc */` comments to communicate information to the users of your code.
### Class comments
JSDoc comments for classes should provide the reader with enough information to know how and when to use the class.
### Parameter property comments
To document parameter properties (e.g. `constructor(private readonly foo: Foo)`), use JSDoc's `@param` annotation on the constructor.
### JSDoc type annotations
JSDoc type annotations are redundant in TypeScript. Do not declare types in `@param` or `@return` blocks.
## 9. Policies
### Consistency
For any style question that isn't settled definitively by this specification, do what the other code in the same file is already doing ("be consistent").
### Deprecation
Mark deprecated methods, classes or interfaces with an `@deprecated` JSDoc annotation.

5
.gitignore vendored
View File

@@ -58,7 +58,9 @@ jspm_packages/
web_modules/
# Claude
.claude
.claude/*
!.claude/skills/
!.claude/commands/
# Build unpublished docs
# docs/
@@ -184,3 +186,4 @@ tmp/
# Coding agent artifacts
.agent/
.llm/