mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 23:53:25 +00:00
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:
69
.claude/skills/dev-debug/SKILL.md
Normal file
69
.claude/skills/dev-debug/SKILL.md
Normal 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.
|
||||
26
.claude/skills/dev/SKILL.md
Normal file
26
.claude/skills/dev/SKILL.md
Normal 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.
|
||||
40
.claude/skills/dev1-start/SKILL.md
Normal file
40
.claude/skills/dev1-start/SKILL.md
Normal 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).
|
||||
50
.claude/skills/dev2-design/SKILL.md
Normal file
50
.claude/skills/dev2-design/SKILL.md
Normal 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`.
|
||||
60
.claude/skills/dev3-prd/SKILL.md
Normal file
60
.claude/skills/dev3-prd/SKILL.md
Normal 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`.
|
||||
65
.claude/skills/dev4-implement/SKILL.md
Normal file
65
.claude/skills/dev4-implement/SKILL.md
Normal 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`.
|
||||
80
.claude/skills/dev5-review/SKILL.md
Normal file
80
.claude/skills/dev5-review/SKILL.md
Normal 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`
|
||||
44
.claude/skills/dev6-review-fix/SKILL.md
Normal file
44
.claude/skills/dev6-review-fix/SKILL.md
Normal 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`.
|
||||
88
.claude/skills/dev7-pr/SKILL.md
Normal file
88
.claude/skills/dev7-pr/SKILL.md
Normal 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
|
||||
87
.claude/skills/ts-style-review/SKILL.md
Normal file
87
.claude/skills/ts-style-review/SKILL.md
Normal 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
|
||||
821
.claude/skills/ts-style-review/google-ts-styleguide.md
Normal file
821
.claude/skills/ts-style-review/google-ts-styleguide.md
Normal 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 file’s 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
5
.gitignore
vendored
@@ -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/
|
||||
|
||||
Reference in New Issue
Block a user