diff --git a/.claude/settings.json b/.claude/settings.json index 44279c4..34eacd4 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,7 +1,6 @@ { "permissions": { "allow": [ - "WebFetch(domain:gist.github.com)" ] } } diff --git a/claude/CLAUDE.md b/claude/CLAUDE.md index 3808de2..b9e5744 100644 --- a/claude/CLAUDE.md +++ b/claude/CLAUDE.md @@ -5,20 +5,26 @@ - No argument validation at function start - No try-catch unless explicitly asked - Make minimal changes when fixing issues - don't refactor unrelated code +- Don't guard against missing attributes unless explicitly asked. Let the system fail. # Git Workflow - `gh` is aliased to `git hist`, so use `command gh` for GitHub CLI - Prefer rebase over merge when updating feature branches from master/main - Name worktrees as `{repo-name}--{branch-name}` +- When fetching comments with the `gh` utility, use --paginate to make sure you get all of the comments. # Process - Update CLAUDE.md after significant code changes - Write out purpose before running bash scripts - Reflect on tool results before proceeding +- After finishing each round of changes, go click through the application using the Chrome browser plugin. Verify the functionality and any functionality that was impacted by the last round of changes. # Running Bash Commands - When specifying a path, use paths relative to the current working directory, rather than absolute paths. - There is no need to cd into the local directory. Instead, first check your current working directory. If it is the repo's directory, then run the command. - When running commands, unless there is a specific reason to combine stderr and stdout using `2>&1`, don't do it. + +# Maintaining CLAUDE.md +- There should be one place where information is maintained. Rely on the underlying framework to do this. Don't create a separate list. (For example, in Rails, don't list the routes in CLAUDE.md. The routes are already documented in config/rails.rb.) Instead, in the Claude.md file, point to the file where the information is stored. diff --git a/claude/commands/address-pr-comments.md b/claude/commands/address-pr-comments.md new file mode 100644 index 0000000..66035b4 --- /dev/null +++ b/claude/commands/address-pr-comments.md @@ -0,0 +1,13 @@ +User input: $ARGUMENTS + +Take the comments from the user input (it may be blank) and user comments on the corresponding PR to this branch (if it exists). + +The user probably put in two kinds of comments: +1. Questions for discussion or clarification +2. Direct commands to change how code words + +For type #1 comments, respond #1 directly on the relevant comment. Prepend [CLAUDE] to make it clear it's coming from you. + +For type #2 comments, make new branch called updates/pr-123-202601011345 (updates/pr-[pr number]-[timestamp yyyymmddhhmm]) changes in code then create a new PR wherein the new branch is being merged into the underlying feature branch. On the relevant parts of the PR, put a comment that has the full comment history which drove the change. [Mo Zhu] for my comments [Claude] for your comments. + + diff --git a/claude/commands/new-worktree.md b/claude/commands/new-worktree.md index b5515a4..9d9ee09 100644 --- a/claude/commands/new-worktree.md +++ b/claude/commands/new-worktree.md @@ -1,9 +1,18 @@ $ARGUMENTS -Create a new worktree for this feature. Worktree name should be {repo-name}--{worktree-name}. Put the worktree in a folder that a sibling of the current repo. - -After creating the new worktree: -1. use the /add-dir command to add the new worktree into current Claude's permission instance. -2. `cd` into the new worktree as your new working directory +Create a new worktree for this feature. Worktree name should be {repo-name}--{worktree-name}. Put the worktree in a folder that is a sibling of the current repo. Be sure to specify in the git command to create a worktree off of master or main branch, not the current branch. + +After creating the worktree: +1. Run any installation script necessary such as npm install or bundle install +2. Copy any .env or secret credentials files from the master repo to the worktree repo + +Once all of the above is done, output: + +``` +Worktree created. Open a new terminal tab and run: +cd {worktree-path} && claude +``` + +Where {worktree-path} is the relative path to the worktree (e.g., `../myrepo--feature-branch`). diff --git a/claude/commands/refresh-branch.md b/claude/commands/refresh-branch.md new file mode 100644 index 0000000..5358efc --- /dev/null +++ b/claude/commands/refresh-branch.md @@ -0,0 +1,3 @@ +If this current branch has no associated remote branch, do nothing. + +If it does have an associated remote branch, ensure that this branch is up to date with the remote branch, including force pulling and resetting the head of the current branch to match the remote. diff --git a/claude/hooks/.env.example b/claude/hooks/.env.example index 1111d3e..28c4cd7 100644 --- a/claude/hooks/.env.example +++ b/claude/hooks/.env.example @@ -1,2 +1,5 @@ -TOKEN_USAGE_API_HOST="localhost:1234" -TOKEN_USAGE_API_TOKEN="abc123" +TOKEN_USAGE_API_HOST_DEV=https://localhost:3000 +TOKEN_USAGE_API_TOKEN_DEV=abc123 + +TOKEN_USAGE_API_HOST_PROD=example.com +TOKEN_USAGE_API_TOKEN_PROD=abc123 diff --git a/claude/hooks/run-tests.sh b/claude/hooks/run-tests.sh new file mode 100755 index 0000000..3ffe913 --- /dev/null +++ b/claude/hooks/run-tests.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ -x "bin/test.sh" ]; then + bin/test.sh + exit $? +else + echo "bin/test.sh not found or not executable. Each project should have a bin/test.sh script." >&2 + exit 1 +fi diff --git a/claude/hooks/send-usage-data.py b/claude/hooks/send-usage-data.py index ab869c3..5a86ffe 100755 --- a/claude/hooks/send-usage-data.py +++ b/claude/hooks/send-usage-data.py @@ -21,76 +21,112 @@ def load_env(): load_env() -API_HOST = os.environ.get("TOKEN_USAGE_API_HOST", "http://localhost:3000") -API_URL = f"{API_HOST.rstrip('/')}/token_usage" -API_TOKEN = os.environ.get("TOKEN_USAGE_API_TOKEN", "") +def get_last_message_id(session_id, host, api_token): + url = f"{host.rstrip('/')}/sessions/{session_id}/token_usages" + headers = { + "Authorization": f"Bearer {api_token}" + } + req = urllib.request.Request(url, headers=headers, method="GET") + with urllib.request.urlopen(req, timeout=30) as response: + data = json.loads(response.read().decode("utf-8")) + if data and len(data) > 0: + return data[0].get("message_id") + return None -def read_last_jsonl_entry(transcript_path): - last_line = None +def get_usage_data(transcript_path, after_message_id, session_id): + usage_data = {} + after_message_has_been_passed = after_message_id is None with open(transcript_path, "r") as f: for line in f: line = line.strip() - if line: - last_line = line - if last_line: - return json.loads(last_line) - return None + entry = json.loads(line) + msg_id = entry.get("message", {}).get("id") + + if msg_id == after_message_id: + after_message_has_been_passed = True + continue + + if after_message_has_been_passed and entry.get("type") == "assistant": + usage_data[msg_id] = extract_usage_data(entry, session_id) + + return list(usage_data.values()) def extract_usage_data(entry, session_id): message = entry.get("message", {}) - message_uuid = entry.get("uuid") - - if not message_uuid: - return None + message_id = message.get("id") usage = message.get("usage", {}) error = message.get("error", {}) return { "session_id": session_id, - "message_uuid": message_uuid, + "message_id": message_id, "input_tokens": usage.get("input_tokens", 0), "output_tokens": usage.get("output_tokens", 0), "error_type": error.get("type") if error else None, "error_message": error.get("message") if error else None } -def post_to_api(data): - payload = json.dumps({"datum": data}).encode("utf-8") +def post_usage_datum(datum, host, token): + url = f"{host.rstrip('/')}/token_usages" + payload = json.dumps({"token_usage": datum}).encode("utf-8") headers = { "Content-Type": "application/json", - "Authorization": f"Bearer {API_TOKEN}" + "Authorization": f"Bearer {token}" } - req = urllib.request.Request(API_URL, data=payload, headers=headers, method="POST") + req = urllib.request.Request(url, data=payload, headers=headers, method="POST") with urllib.request.urlopen(req, timeout=30) as response: return response.status, response.read().decode("utf-8") -def main(): - input_data = json.loads(sys.stdin.read()) - - transcript_path = input_data.get("transcript_path") - session_id = input_data.get("session_id") - - if not transcript_path or not os.path.exists(transcript_path): +def send_usage_data(usage_data, host, token): + for usage_datum in usage_data: + message_id = usage_datum.get("message_id", "unknown") + try: + post_usage_datum(usage_datum, host, token) + except urllib.error.HTTPError as e: + print(f"Failed: {message_id} - {e.code} {e.reason}", file=sys.stderr) + +def extract_and_send_usage_data_for_env(transcript_path, session_id, host, api_token): + try: + last_message_id = get_last_message_id(session_id, host, api_token) + except urllib.error.HTTPError as e: + print(f"Failed: {e.url} {e.code} - {e.reason}", file=sys.stderr) return - if not session_id: - return + usage_data = get_usage_data(transcript_path, last_message_id, session_id) - if not API_TOKEN: - return + total_input = sum(e.get("input_tokens", 0) for e in usage_data) + total_output = sum(e.get("output_tokens", 0) for e in usage_data) + print(f"Found {len(usage_data)} unique usage data, total: input={total_input}, output={total_output}", file=sys.stderr) + send_usage_data(usage_data, host, api_token) - entry = read_last_jsonl_entry(transcript_path) - if not entry: - return +def main(): + print("Sending usage data...", file=sys.stderr) - usage_data = extract_usage_data(entry, session_id) - if not usage_data: - return + input_data = json.loads(sys.stdin.read()) - post_to_api(usage_data) + hook_event = input_data.get("hook_event_name") + if hook_event == "SubagentStop": + transcript_path = input_data.get("agent_transcript_path") + session_id = input_data.get("agent_id") + else: + transcript_path = input_data.get("transcript_path") + session_id = input_data.get("session_id") + + dev_host = os.environ.get("TOKEN_USAGE_API_HOST_DEV", "") + dev_api_token = os.environ.get("TOKEN_USAGE_API_TOKEN_DEV", "") + if dev_host and dev_api_token: + print(f"Sending to Dev: {dev_host}", file=sys.stderr) + extract_and_send_usage_data_for_env(transcript_path, session_id, dev_host, dev_api_token) + + prod_host = os.environ.get("TOKEN_USAGE_API_HOST_PROD", "") + prod_api_token = os.environ.get("TOKEN_USAGE_API_TOKEN_PROD", "") + print(f"Sending to Prod: {prod_host}", file=sys.stderr) + extract_and_send_usage_data_for_env(transcript_path, session_id, prod_host, prod_api_token) + + sys.exit(1) if __name__ == "__main__": main() diff --git a/claude/settings.json b/claude/settings.json index fe02566..b522cbe 100644 --- a/claude/settings.json +++ b/claude/settings.json @@ -1,14 +1,7 @@ { "permissions": { "allow": [ - "Bash(git push:*)", - "Bash(git fetch:*)", - "Bash(git add:*)", - "Bash(git commit:*)", - "Bash(git checkout:*)", - "Bash(git worktree:*)", - "Bash(git branch:*)", - "Bash(git rebase:*)", + "Bash(git:*)", "Bash(command gh:*)", "Bash(grep:*)", "Bash(cat:*)", @@ -18,12 +11,40 @@ "Bash(cp:*)", "Bash(cd:*)", "WebSearch", + "WebFetch", "Bash(bundle install:*)", - "Bash(rails generate:*)" + "Bash(rails generate:*)", + "Bash(rspec:*)", + "Bash(bundle exec rspec:*)", + "Bash(bundle install)", + "Bash(rails db:*)", + "Bash(rubocop:*)", + "Bash(npm run dev)", + "Bash(npm dev)" + ], + "deny": [ + "Bash(bin/rails credentials/show)" ], - "deny": [], "ask": [ - "Bash(git merge:*)" + "Bash(git merge:*)", + "Bash(git push master)", + "Bash(git push main)", + "Bash(git push --force)", + "Bash(git push -f)", + "Bash(rm:*)", + "Bash(rails db:drop)", + "Bash(rails db:rollback)", + "Bash(rails db:reset)", + "Bash(rails db:migrate:redo)", + "Bash(rails db:migrate:down)", + "Bash(command gh repo archive:*)", + "Bash(command gh repo delete:*)", + "Bash(command gh repo edit:*)", + "Bash(command gh repo rename:*)", + "Bash(command gh pr merge:*)", + "Bash(command gh pr close:*)", + "Edit(Gemfile)", + "Write(Gemfile)" ], "defaultMode": "default" }, @@ -44,8 +65,8 @@ "matcher": "Write|Edit", "hooks": [ { - "type": "prompt", - "prompt": "if any changes were made, run the tests to ensure that everything still works. Fix any thing that is broken." + "type": "command", + "command": "~/.claude/hooks/run-tests.sh" } ] } @@ -53,10 +74,6 @@ "Stop": [ { "hooks": [ - { - "type": "prompt", - "prompt": "If you changed any front end UI, go use Chrome and look at what you change and decide if it looks good. If it does not, update it until you think it looks good to present to the user." - }, { "type": "command", "command": "~/.claude/hooks/send-usage-data.py" @@ -85,9 +102,7 @@ "feature-dev@claude-plugins-official": true, "code-review@claude-plugins-official": true, "commit-commands@claude-plugins-official": true, - "ralph-wiggum@claude-plugins-official": true, "plugin-dev@claude-plugins-official": true, - "hookify@claude-plugins-official": true, "agent-sdk-dev@claude-plugins-official": true, "pr-review-toolkit@claude-plugins-official": true } diff --git a/claude/skills/feature-spec/SKILL.md b/claude/skills/feature-spec/SKILL.md new file mode 100644 index 0000000..6efee84 --- /dev/null +++ b/claude/skills/feature-spec/SKILL.md @@ -0,0 +1,162 @@ +--- +name: Feature Spec Generator +description: Use this skill when the user asks to "create a feature spec", "spec out a feature", "write requirements", "document requirements", "create technical spec", or needs to clarify requirements before implementation. +--- + +# Feature Spec Generator + +Generate comprehensive feature documentation through a requirements interview. Creates three documents: +1. **Requirements Document** - filled via interview +2. **Technical Architecture Document** - generated from requirements +3. **Testing Document** - generated from requirements + architecture + +**Perspective:** Engineering manager peer helping clarify requirements before implementation. Focus on concrete behavior, edge cases, and technical constraints. + +## Prerequisites + +- Feature description provided by user (can be brief) +- Git repository context (uses current branch name for folder naming) + +## Output Location + +Documents are saved to `projects/{feature-branch-name}/` in the repository root: +``` +projects/{branch-name}/ +├── REQUIREMENTS.md +├── ARCHITECTURE.md +└── TESTING.md +``` + +## Workflow Execution + +### Initialization + +1. Capture feature description from $ARGUMENTS or prompt user if not provided +2. Get branch name: `git branch --show-current` +3. Create output directory: `mkdir -p projects/{branch-name}` +4. Announce the three-phase process to user + +**Opening message:** +"I'll help you document this feature in three phases: +1. Requirements (through an interview) +2. Technical Architecture +3. Testing Plan + +All documents will be saved to `projects/{branch-name}/`. Let's start by nailing down the requirements." + +### Phase 1: Requirements Interview + +Conduct an interview to clarify and document requirements. Ask questions in batches of 2-3, waiting for responses before proceeding. + +**Interview sections** (in order): + +1. **Core Behavior** (maps to Functional Requirements) + - Walk through the main flow step by step + - What are the inputs and outputs? + - What triggers this? + - What existing systems does this interact with? + +2. **Edge Cases & Error Handling** (maps to Edge Cases) + - What if input is malformed or missing? + - What if a dependency is unavailable? + - Rate limits, quotas, resource constraints? + - Retry/fallback behavior? + - Concurrency concerns? + +3. **Data & State** (maps to Data Requirements) + - What data is persisted vs ephemeral? + - Expected volume/scale? + - Validation rules? + - Sensitive data considerations? + +4. **Integration Points** (maps to Integration) + - What existing code/systems does this touch? + - APIs consumed or exposed? + - Database changes needed? + - Backwards compatibility concerns? + +5. **Scope Boundaries** (maps to Scope) + - What's the minimum viable version? + - What's explicitly out of scope? + - What can be hardcoded for now? + +6. **Open Questions** (maps to Open Questions) + - Technical unknowns or spikes needed? + - Decisions depending on external factors? + - Assumptions to validate? + +**Interview rules:** +- Present 2-3 questions at a time +- Allow "skip" or "I'll figure it out later" for items +- Dig into edge cases - that's where bugs hide +- Summarize understanding after each section +- At the end, present a summary and ask for confirmation + +**After interview completion:** +1. Load requirements template from `templates/requirements.md` +2. Fill template with interview responses +3. Write to `projects/{branch}/REQUIREMENTS.md` +4. Present summary and ask: "Does this capture the requirements? Reply 'yes' to proceed to Architecture, or tell me what to adjust." + +### Phase 2: Technical Architecture + +Generate architecture document based on the completed requirements. + +1. Read the requirements from `projects/{branch}/REQUIREMENTS.md` +2. Analyze requirements to determine: + - System components needed + - Data flows between components + - APIs and interfaces + - Dependencies (internal and external) + - Implementation approach + - Technical risks +3. Load architecture template from `templates/architecture.md` +4. Generate architecture document +5. Write to `projects/{branch}/ARCHITECTURE.md` +6. Present key highlights: "Architecture document complete. Key components: {list}. Proceeding to Testing document..." + +### Phase 3: Testing Document + +Generate testing document based on requirements and architecture. + +1. Read requirements from `projects/{branch}/REQUIREMENTS.md` +2. Read architecture from `projects/{branch}/ARCHITECTURE.md` +3. Generate test plan covering: + - Unit tests for each component + - Integration tests for component interactions + - Test cases mapped to requirements + - Edge cases and error conditions + - Test data requirements +4. Load testing template from `templates/testing.md` +5. Generate testing document +6. Write to `projects/{branch}/TESTING.md` + +### Completion + +Present final summary: +"Feature specification complete! Documents created: +- `projects/{branch}/REQUIREMENTS.md` +- `projects/{branch}/ARCHITECTURE.md` +- `projects/{branch}/TESTING.md` + +Next steps: +1. Review documents +2. Resolve open questions +3. Begin implementation" + +## Phase Transitions + +| Transition | User Action Required | +|------------|---------------------| +| Init → Phase 1 | None | +| Phase 1 → Phase 2 | Explicit approval of requirements | +| Phase 2 → Phase 3 | Acknowledgment | +| Phase 3 → Complete | None | + +## Important Notes + +- This workflow is single-session and non-resumable +- If user abandons mid-interview, warn them the session cannot be resumed +- All three documents must be generated in sequence +- Templates are in the `templates/` subdirectory +- Interview guide reference is in `references/interview-guide.md` diff --git a/claude/skills/feature-spec/references/interview-guide.md b/claude/skills/feature-spec/references/interview-guide.md new file mode 100644 index 0000000..fe65aac --- /dev/null +++ b/claude/skills/feature-spec/references/interview-guide.md @@ -0,0 +1,120 @@ +# Requirements Interview Guide + +## Interview Principles + +1. Ask 2-3 questions at a time +2. Wait for response before proceeding +3. Allow "skip" or "I'll figure it out later" responses +4. Dig into edge cases and error conditions +5. Get explicit approval before moving to architecture phase + +**Perspective:** Engineering manager peer helping to clarify requirements before implementation. Focus on concrete behavior, edge cases, and technical constraints - not product strategy. + +--- + +## Section 1: Core Behavior + +**Transition:** "Let's nail down exactly what this needs to do." + +### Questions: + +1. "Walk me through the main flow - what happens step by step?" +2. "What are the inputs? What are the outputs?" +3. "What triggers this? (user action, scheduled, event-driven?)" +4. "What existing systems or data does this interact with?" + +**Maps to:** Functional Requirements + +--- + +## Section 2: Edge Cases & Error Handling + +**Transition:** "Now let's think about what could go wrong." + +### Questions: + +1. "What happens if the input is malformed or missing?" +2. "What if a dependency (API, database, service) is unavailable?" +3. "Are there rate limits, quotas, or resource constraints to handle?" +4. "What's the retry/fallback behavior when something fails?" +5. "Any race conditions or concurrency concerns?" + +**Maps to:** Edge Cases, Error Handling + +--- + +## Section 3: Data & State + +**Transition:** "Let's talk about data." + +### Questions: + +1. "What data needs to be persisted vs. ephemeral?" +2. "What's the expected data volume/scale?" +3. "Are there data validation rules or constraints?" +4. "Any data that needs to be kept in sync across systems?" +5. "Sensitive data considerations? (PII, credentials, etc.)" + +**Maps to:** Data Requirements, Non-Functional Requirements + +--- + +## Section 4: Integration Points + +**Transition:** "How does this fit with existing systems?" + +### Questions: + +1. "What existing code/systems does this touch or modify?" +2. "Any APIs being consumed or exposed?" +3. "Database changes needed? (new tables, schema changes)" +4. "Does this need to work with existing auth/permissions?" +5. "Any backwards compatibility concerns?" + +**Maps to:** Dependencies, Integration Requirements + +--- + +## Section 5: Scope Boundaries + +**Transition:** "Let's be explicit about what's in and out of scope." + +### Questions: + +1. "What's the minimum viable version of this?" +2. "What are we explicitly NOT doing in this iteration?" +3. "Any tempting additions we should resist for now?" +4. "What can we hardcode now and make configurable later?" + +**Maps to:** Out of Scope, MVP Definition + +--- + +## Section 6: Open Questions + +**Transition:** "What do we still need to figure out?" + +### Questions: + +1. "Any technical unknowns or spikes needed?" +2. "Decisions that depend on other teams or external factors?" +3. "Assumptions we're making that should be validated?" +4. "Anything blocked on more information?" + +**Maps to:** Open Questions, Technical Risks + +--- + +## Interview Wrap-Up + +**Summary Template:** +"Here's my understanding: + +**Core Flow:** {summary} +**Key Edge Cases:** {summary} +**Data Considerations:** {summary} +**Integration Points:** {summary} +**Scope:** {what's in} / NOT: {what's out} +**Open Items:** {summary} + +Does this capture the requirements? Reply 'yes' to proceed to Technical Architecture, or let me know what to adjust." diff --git a/claude/skills/feature-spec/templates/architecture.md b/claude/skills/feature-spec/templates/architecture.md new file mode 100644 index 0000000..7b26ec6 --- /dev/null +++ b/claude/skills/feature-spec/templates/architecture.md @@ -0,0 +1,170 @@ +# Technical Architecture: {Feature Name} + +**PRD Reference:** projects/{branch}/PRD.md +**Created:** {date} +**Status:** Draft + +--- + +## 1. System Overview + +### Purpose +{Brief description of what this architecture enables} + +### Scope +{Boundaries of this architecture document} + +### High-Level Diagram +``` +{ASCII diagram of system components} +``` + +--- + +## 2. Component Design + +### Component Overview + +| Component | Responsibility | Technology | +|-----------|---------------|------------| +| {name} | {what it does} | {tech stack} | + +### Component Details + +#### Component: {Name} + +**Purpose:** {what this component does} + +**Responsibilities:** +- {responsibility 1} +- {responsibility 2} + +**Interfaces:** +- Input: {interface description} +- Output: {interface description} + +**Error Handling:** +{error handling strategy} + +--- + +## 3. Data Flow + +### Primary Data Flow + +``` +[Input] -> [Component A] -> [Component B] -> [Output] +``` + +### Flow Description + +1. **{Step Name}:** {description} +2. **{Step Name}:** {description} + +### Data Transformations + +| Stage | Input Format | Output Format | Transformation | +|-------|--------------|---------------|----------------| +| {stage} | {format} | {format} | {what changes} | + +--- + +## 4. API/Interface Definitions + +### External APIs + +#### API: {Name} + +**Endpoint:** `{HTTP method} /path` + +**Request:** +```json +{ + "field": "type" +} +``` + +**Response:** +```json +{ + "field": "type" +} +``` + +**Error Codes:** +| Code | Description | +|------|-------------| +| {code} | {description} | + +### Internal Interfaces + +#### Interface: {Name} + +**Methods:** +- `{method signature}` - {description} + +--- + +## 5. Dependencies + +### External Dependencies + +| Dependency | Version | Purpose | Risk Level | +|------------|---------|---------|------------| +| {name} | {version} | {why needed} | Low/Medium/High | + +### Internal Dependencies + +| Module | Dependency Type | Impact if Unavailable | +|--------|-----------------|----------------------| +| {name} | {hard/soft} | {what breaks} | + +--- + +## 6. Implementation Approach + +### Development Phases + +| Phase | Scope | Deliverables | +|-------|-------|--------------| +| 1 | {scope} | {what gets delivered} | + +### Implementation Order + +1. **{Component/Feature}** - {rationale for order} +2. **{Component/Feature}** - {rationale} + +### Key Implementation Decisions + +| Decision | Options Considered | Choice | Rationale | +|----------|-------------------|--------|-----------| +| {decision} | {options} | {chosen} | {why} | + +--- + +## 7. Risk Assessment + +### Technical Risks + +| Risk | Probability | Impact | Mitigation | +|------|-------------|--------|------------| +| {risk} | Low/Med/High | Low/Med/High | {mitigation strategy} | + +### Security Considerations +{security risks and mitigations} + +### Performance Risks +{performance concerns and mitigations} + +--- + +## Appendix + +### Technology Stack Summary +- **Language:** {language} +- **Framework:** {framework} +- **Database:** {if applicable} +- **Infrastructure:** {if applicable} + +### References +- {reference 1} diff --git a/claude/skills/feature-spec/templates/requirements.md b/claude/skills/feature-spec/templates/requirements.md new file mode 100644 index 0000000..03a8e28 --- /dev/null +++ b/claude/skills/feature-spec/templates/requirements.md @@ -0,0 +1,158 @@ +# Requirements: {Feature Name} + +**Branch:** {branch-name} +**Created:** {date} +**Status:** Draft + +--- + +## 1. Overview + +### Summary +{One paragraph describing what this feature does} + +### Trigger +{What initiates this feature - user action, event, schedule, etc.} + +--- + +## 2. Functional Requirements + +### Core Flow + +1. {Step 1} +2. {Step 2} +3. {Step 3} + +### Inputs + +| Input | Type | Required | Description | +|-------|------|----------|-------------| +| {name} | {type} | Yes/No | {description} | + +### Outputs + +| Output | Type | Description | +|--------|------|-------------| +| {name} | {type} | {description} | + +### Detailed Behavior + +#### {Scenario Name} +**Given:** {precondition} +**When:** {action} +**Then:** {expected result} + +--- + +## 3. Edge Cases & Error Handling + +### Input Validation + +| Condition | Expected Behavior | +|-----------|-------------------| +| Missing required field | {behavior} | +| Invalid format | {behavior} | +| {other condition} | {behavior} | + +### Failure Scenarios + +| Failure | Handling | Retry? | +|---------|----------|--------| +| Dependency unavailable | {fallback behavior} | Yes/No | +| Timeout | {behavior} | Yes/No | +| {other failure} | {behavior} | Yes/No | + +### Concurrency Considerations +{Race conditions, locking, idempotency requirements} + +--- + +## 4. Data Requirements + +### Persistence + +| Data | Storage | Retention | +|------|---------|-----------| +| {data item} | {where stored} | {how long} | + +### Data Validation Rules +- {rule 1} +- {rule 2} + +### Sensitive Data +{PII, credentials, or other sensitive data considerations} + +### Scale Expectations +- Expected volume: {volume} +- Growth rate: {rate} + +--- + +## 5. Integration Points + +### Systems Touched + +| System | Interaction | Impact | +|--------|-------------|--------| +| {system} | Read/Write/Both | {what changes} | + +### APIs + +#### Consumed +| API | Purpose | Auth | +|-----|---------|------| +| {api} | {why needed} | {auth method} | + +#### Exposed +| Endpoint | Method | Purpose | +|----------|--------|---------| +| {path} | GET/POST/etc | {description} | + +### Database Changes +{Schema changes, new tables, migrations needed} + +### Backwards Compatibility +{Breaking changes, deprecations, migration path} + +--- + +## 6. Scope + +### In Scope (MVP) +- {item 1} +- {item 2} + +### Explicitly Out of Scope +- {item 1} - {reason/deferral} +- {item 2} - {reason/deferral} + +### Hardcoded for Now +| Item | Current Value | Make Configurable When | +|------|---------------|------------------------| +| {item} | {value} | {trigger} | + +--- + +## 7. Open Questions + +| Question | Blocker? | Owner | Notes | +|----------|----------|-------|-------| +| {question} | Yes/No | {who} | {context} | + +### Assumptions to Validate +- {assumption 1} +- {assumption 2} + +### Technical Unknowns +- {unknown 1} - {spike needed?} + +--- + +## Appendix + +### Glossary +- **{term}:** {definition} + +### References +- {reference 1} diff --git a/claude/skills/feature-spec/templates/testing.md b/claude/skills/feature-spec/templates/testing.md new file mode 100644 index 0000000..1a06435 --- /dev/null +++ b/claude/skills/feature-spec/templates/testing.md @@ -0,0 +1,151 @@ +# Testing Document: {Feature Name} + +**PRD Reference:** projects/{branch}/PRD.md +**Architecture Reference:** projects/{branch}/ARCHITECTURE.md +**Created:** {date} +**Status:** Draft + +--- + +## 1. Unit Test Plan + +### Testing Strategy +{Overall unit testing approach} + +### Coverage Targets +- Line coverage: {target}% +- Branch coverage: {target}% + +### Object-Level Tests + +#### {Component/Class Name} + +**Test File:** `{path/to/test/file}` + +| Test ID | Method/Function | Test Description | Expected Result | +|---------|-----------------|------------------|-----------------| +| UT-1 | `{method}` | {what is being tested} | {expected outcome} | + +**Setup Requirements:** +- {mock/stub requirements} + +--- + +## 2. Integration Test Plan + +### Integration Scope +{What integrations are being tested} + +### Integration Points + +| Integration | Components Involved | Test Approach | +|-------------|--------------------| --------------| +| {name} | {component A} <-> {component B} | {strategy} | + +### Integration Test Cases + +#### Integration: {Name} + +**Test ID:** IT-1 + +**Description:** {what this integration test validates} + +**Preconditions:** +- {precondition 1} + +**Test Steps:** +1. {step} +2. {step} + +**Expected Results:** +- {expected outcome} + +--- + +## 3. Test Cases by Component + +### Component: {Name} + +**Test Coverage Summary:** +- Unit Tests: {count} +- Integration Tests: {count} + +#### Functional Tests + +| Test ID | Requirement | Test Description | Expected Result | Priority | +|---------|-------------|------------------|-----------------|----------| +| TC-1 | FR-1 | {description} | {expected} | P0/P1/P2 | + +--- + +## 4. Edge Cases + +### Identified Edge Cases + +| ID | Scenario | Input Condition | Expected Behavior | +|----|----------|-----------------|-------------------| +| EC-1 | {scenario name} | {condition} | {behavior} | + +### Boundary Conditions + +| Boundary | Min Value | Max Value | At Boundary | Beyond Boundary | +|----------|-----------|-----------|-------------|-----------------| +| {field} | {min} | {max} | {behavior} | {behavior} | + +### Error Conditions + +| Error Type | Trigger Condition | Expected Response | +|------------|-------------------|-------------------| +| {error} | {how to trigger} | {expected message/behavior} | + +--- + +## 5. Test Data Requirements + +### Test Data Sets + +| Data Set | Purpose | Size | Generation Method | +|----------|---------|------|-------------------| +| {name} | {what it tests} | {volume} | Manual/Generated | + +### Sample Test Data + +```json +{ + "example": "data structure" +} +``` + +### Data Cleanup Strategy +{how test data is managed/cleaned} + +--- + +## 6. Acceptance Criteria Mapping + +### PRD Requirement Traceability + +| Requirement ID | Requirement | Test Cases | Coverage Status | +|----------------|-------------|------------|-----------------| +| FR-1 | {requirement text} | TC-1, TC-2 | Covered/Partial | + +### User Story Validation + +| User Story | Acceptance Criteria | Test Case | Automated | +|------------|--------------------| ----------|-----------| +| US-1 | {criteria} | TC-1 | Yes/No | + +--- + +## Appendix + +### Test Environment Requirements +- {environment specifications} + +### Test Tools and Frameworks +- Unit Testing: {framework} +- Integration Testing: {framework} +- Mocking: {library} + +### Known Testing Limitations +- {limitation 1} diff --git a/shell/gitconfig b/shell/gitconfig index aec3675..bd31d41 100644 --- a/shell/gitconfig +++ b/shell/gitconfig @@ -13,7 +13,12 @@ [stash] showPatch = true + [credential] helper = osxkeychain + [init] defaultBranch = master + +[pull] + rebase = true diff --git a/shell/vimrc b/shell/vimrc index e88179d..3b5c674 100644 --- a/shell/vimrc +++ b/shell/vimrc @@ -1,5 +1,3 @@ -"separates vim from vi, allowing the many customizations found in vim -set nocompatible filetype off set rtp+=~/.vim/bundle/Vundle.vim @@ -17,9 +15,18 @@ Plugin 'tpope/vim-fugitive' Plugin 'sheerun/vim-polyglot' Plugin 'Lokaltog/vim-distinguished' Plugin 'morhetz/gruvbox' +Plugin 'vim-test/vim-test' call vundle#end() filetype plugin indent on + +let test#strategy = 'basic' +let test#javascript#vitest#options = '--run' +autocmd BufEnter */e2e/* let b:test_runner = 'playwright' +nmap a :TestSuite +nmap t :TestFile +nmap s :TestNearest + au BufNewFile,BufRead *.prawn set filetype=ruby au BufNewFile,BufRead *.json.jbuilder set filetype=ruby au BufNewFile,BufRead *.axlsx set filetype=ruby @@ -28,8 +35,6 @@ au BufNewFile,BufRead *.axlsx set filetype=ruby au BufNewFile,BufRead *.md set wrap au BufNewFile,BufRead *.txt set wrap -"In the bottom of the screen, it will show me the XY coordinates of my cursor -set ruler set rulerformat='%60(%f:%l\ of\ %L%)' "Highlight cursor line @@ -45,15 +50,6 @@ set nowritebackup "prevents vim from creating a separate swap file, which tends to get in the way of git. set noswapfile -set history=50 - -"shows relevant information at bottom of screen when you are using commands. -set showcmd - -"live searching as you type -set incsearch -"search terms stay highlighted after you hit enter -set hlsearch map "get rid of highlighting after you are done with searching @@ -72,15 +68,6 @@ set tabstop=2 set shiftwidth=2 set expandtab -"hit tab for vim to autocomplete you file name. Hit tab again to cycle to next option. -set wildmode=full - -" Switch syntax highlighting on, when the terminal has colors -" Also switch on highlighting the last used search pattern. -if (&t_Co > 2 || has("gui_running")) && !exists("syntax_on") - syntax on -endif - " Display extra whitespace set list listchars=tab:··,trail:· @@ -109,8 +96,6 @@ endfunction "set toggle line numbers map l :call ToggleLineNumbers() -"set auto indentation -set autoindent "set smartindent <- This has been set off because it interferes with the endwise plugin "movement keys always move cursor to start of a line. @@ -126,9 +111,7 @@ let g:netrw_list_hide='.*\.DS_Store$' "let NERDTreeShowHidden=1 map k :Ex -set t_Co=256 color gruvbox -set noerrorbells "open up todo file map o :e .todo @@ -159,7 +142,7 @@ map o "map << "being typing shell commands -mapi :! +mapi :! "Common mistaken keys for saving and quitting map :W :w diff --git a/shell/zshrc b/shell/zshrc index a9b30c1..fffd243 100644 --- a/shell/zshrc +++ b/shell/zshrc @@ -26,22 +26,30 @@ export PS1="%F{yellow}%n@%1~\${vcs_info_msg_0_} =>%f " export EDITOR=nvim alias vim="nvim" alias v="vim" +alias c="claude" export PYTHONPATH="/usr/local/lib/python:/usr/local/lib/python/site-packages:/usr/local/lib/python/site-packages/caption_positioning:/usr/local/lib/python/site-packages/dsptools:$PYTHONPATH" +# Bash aliases alias rm="rm -i" +alias ls="ls -alF" + +# Git aliases alias gh="git hist" alias gs="git status" +alias gaa="git add -A" alias gcm="git commit -m" -alias gbr="git branch" alias gd="git diff" alias gds="git diff --staged" -alias gaa="git add -A" +alias gbr="git branch" alias gco="git checkout" -alias ls="ls -alF" alias gss="git stash" alias gsp="git stash pop" +alias gfa="git fetch -a" +alias gfp="git fetch -a && git pull" +alias gfrm="git fetch -a && git rebase origin/master" +# Rails aliases alias mrmt="rake db:migrate && rake db:rollback && rake db:migrate && rake db:test:prepare" alias mrm="rake db:migrate && rake db:rollback && rake db:migrate" alias test!="rake db:test:prepare" @@ -57,10 +65,7 @@ export PATH=$PATH:$GOPATH/bin eval "$(rbenv init - zsh)" -# Added by Windsurf -export PATH="/Users/mozhu/.codeium/windsurf/bin:$PATH" - -. "$HOME/.local/bin/env" - # Added by Antigravity export PATH="/Users/mozhu/.antigravity/antigravity/bin:$PATH" + +export PATH="$HOME/.local/bin:$PATH" diff --git a/symlink_script.sh b/symlink_script.sh index fb14231..1b18081 100755 --- a/symlink_script.sh +++ b/symlink_script.sh @@ -12,8 +12,9 @@ mkdir -p "$HOME/.claude" for item in claude/*; do name=$(basename "$item") target="$HOME/.claude/$name" - if [ -d "$target" ] && [ ! -L "$target" ]; then - echo "Removing existing directory $target" + if [ -L "$target" ]; then + rm -f "$target" + elif [ -d "$target" ]; then rm -rf "$target" fi echo "Linking ~/.claude/$name to $PWD/$item"