commit 52ebac52298c666bddd9c4571967ab70370caa94 Author: Zhongwei Li Date: Sat Nov 29 18:08:48 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..f8f7d13 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "tdg", + "description": "Test-Driven Generation plugin for Claude Code", + "version": "0.4.0", + "author": { + "name": "Chanwit Kaewkasi", + "email": "chanwit@gmail.com", + "url": "https://github.com/chanwit" + }, + "skills": [ + "./skills" + ], + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f98b5d0 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# tdg + +Test-Driven Generation plugin for Claude Code diff --git a/commands/atomic-commit.md b/commands/atomic-commit.md new file mode 100644 index 0000000..bd264ff --- /dev/null +++ b/commands/atomic-commit.md @@ -0,0 +1,5 @@ +--- +description: Help create clean, atomic commits by analyzing changes and detecting mixed concerns +--- + +invoke Skill("tdg:atomic"). diff --git a/commands/init.md b/commands/init.md new file mode 100644 index 0000000..e98834d --- /dev/null +++ b/commands/init.md @@ -0,0 +1,53 @@ +--- +description: Initialize TDG configuration for the current project +--- + +Initialize Test-Driven Generation for this project. + +First, check if a `TDG.md` file exists in the current directory. + +IF `TDG.md` exists: +- Read it and confirm the build and test commands are properly configured +- Display the current configuration to the user +- Ask if they want to update it + +IF `TDG.md` does NOT exist: +- Scan the current project to detect: + - Programming language (check for package.json, go.mod, requirements.txt, Cargo.toml, etc.) + - Project type and framework + - Common build commands for this project type + - Common test commands and test file patterns + - Test framework in use (pytest, jest, go test, cargo test, etc.) +- Create a `TDG.md` file with the following structure strictly: + +```markdown +# TDG Configuration + +## Project Information +- Language: [detected language] +- Framework: [detected framework if any] +- Test Framework: [detected test framework] + +## Build Command +[detected or default build command] + +## Test Command +[detected or default test command] + +## Single Test Command +[detected or default test command to run a single unit test] + +## Coverage Command +[detected or default test coverage command] + +## Test File Patterns +- Test files: [pattern like *_test.go, test_*.py, *.test.js] +- Test directory: [e.g., tests/, __tests__/] +``` + +After creating the file: +- Show the user what was detected +- Ask if they want to customize any of the commands +- Explain how to use TDG with this project + +If the user provided arguments ($ARGUMENTS), use them as hints for project detection or configuration. diff --git a/commands/version.md b/commands/version.md new file mode 100644 index 0000000..e60d358 --- /dev/null +++ b/commands/version.md @@ -0,0 +1,5 @@ +--- +description: Show the current TDG plugin version +--- + +Show only version text: "0.4.0" AND nothing else. diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..184a679 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,65 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:chanwit/tdg:", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "7fd171368408e180cd05ef0794ecfb861f401e67", + "treeHash": "dad9341f0dd73e7431cab5510ce3bdb44ba7fd28165410f2a9754c2e0564d91e", + "generatedAt": "2025-11-28T10:14:59.633254Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "tdg", + "description": "Test-Driven Generation plugin for Claude Code", + "version": "0.4.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "2dd467d9ec79019d067c5d228773352bbf2cc9da9626b78681a8a9021c2f995f" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "6f6ac982edecfbd7f37c4cf7a1f157d0b81b53557906c8e2f18d2d16903c32b4" + }, + { + "path": "commands/atomic-commit.md", + "sha256": "8b020c92a9e7e6aad26c261f27d2daadeea2ed1a2a4f462358c7720b13331f97" + }, + { + "path": "commands/init.md", + "sha256": "09c0bef050e099ead51475db08b682bece4cb9a4fc6dddbc614f9ad0f1e1b56d" + }, + { + "path": "commands/version.md", + "sha256": "673fa7b305deb36b645e944b5cbbf48eae13adef1fe7bb074b5753ad04fc767b" + }, + { + "path": "skills/tdg/SKILL.md", + "sha256": "571b4e1f20011837b7e2c83c86f8ae7ea4b0b216690086fdd13afe02b0f8598e" + }, + { + "path": "skills/tdg/scripts/tdg_phase.sh", + "sha256": "86e2fcc4601f23c5b77c7d565de763a0da4e5953c327fca7265318d4dbe781cf" + }, + { + "path": "skills/atomic/SKILL.md", + "sha256": "cfc6fc3493714dfe15ac66b4430174f4e17c78a2adb5fc0940727a01aecc41b0" + } + ], + "dirSha256": "dad9341f0dd73e7431cab5510ce3bdb44ba7fd28165410f2a9754c2e0564d91e" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/atomic/SKILL.md b/skills/atomic/SKILL.md new file mode 100644 index 0000000..e2f3eeb --- /dev/null +++ b/skills/atomic/SKILL.md @@ -0,0 +1,81 @@ +--- +name: Atomic Commit +description: Atomic Commit helps create clean, atomic commits by analyzing changes, detecting mixed concerns, and ensuring each commit is a complete unit of work. Key words to detect are atomic, atomic commits, clean commits. +--- +# Atomic Commit + +Standalone skill for non-TDD workflows. Helps create clean, atomic commits by analyzing changes and detecting mixed concerns. + +## Atomic Commit Definition +- Does exactly one thing (one feature/fix/refactor) +- Leaves codebase in working state (builds and tests pass) +- Can be reverted independently +- Doesn't mix unrelated concerns + +## Workflow + +### 1. Analyze Changes +```bash +git status && git diff && git diff --staged +``` + +### 2. Detect Mixed Concerns +Look for files mixing: +- Multiple features +- Bug fixes + features +- Refactoring + new functionality +- Multiple unrelated bug fixes +- Code + documentation (unless for same feature) +- Tests for multiple features + +### 3. Group Commits +Group files by shared purpose. Present grouping to user for confirmation. + +Example: +``` +Group 1: "Add user auth" → auth/login.ts, auth/session.ts, tests/auth/login.test.ts +Group 2: "Fix password validation" → validators/password.ts, tests/validators/password.test.ts +``` + +### 4. Create Each Commit +For each group: +1. Stage: `git add ` (NO `git add .`) +2. Review: `git diff --staged` +3. Test: Run tests, ensure pass +4. Build: Run build if applicable, ensure success +5. Commit: Use conventional commit format +6. Verify: `git log -1 --oneline` + +Conventional commit types: `feat|fix|refactor|docs|test|chore|perf|style` + +### 5. Final Check +```bash +git log --oneline -n +``` + +## Issue Integration +- Check user message or branch name for issue number +- IF no issue: ask if user wants to create one + - IF user does not want to provider, leave it blank +- IF yes: help write description, offer `gh issue create` or `glab issue create` + - Include in commits: `"feat: description (#42)"` + +## Guidelines +**DO:** One logical change, include related tests, run tests, clear messages, issue numbers +**DON'T:** Mix features/fixes/refactors, commit broken code, vague messages, debug code + +## TODO Pattern +☐ Analyze: git status/diff +☐ Identify file purposes +☐ Detect mixed concerns +☐ Group into atomic commits +☐ Confirm with user +☐ For each: stage → review → test → build → commit → verify +☐ Final review + +## Activation +Use when user says: "atomic", "clean commits", "break down commits", "split commits" +DO NOT use for TDD workflows (use TDG skill). + +## Closing +"Created N atomic commits. Tests pass. Would you like to review history, push, or create PR?" diff --git a/skills/tdg/SKILL.md b/skills/tdg/SKILL.md new file mode 100644 index 0000000..e3da1f0 --- /dev/null +++ b/skills/tdg/SKILL.md @@ -0,0 +1,102 @@ +--- +name: TDG Test-Driven Generation +description: Test-Driven Generation uses TDD (Test-Driven Development) techniques to generate tests and code in Red-Green-Refactor loops. Key words to detect are tdg, TDG. +--- + +# Test-Driven Generation + +## Instructions + +Read TDG.md to understand the project's technology stack. +IF TDG.md does not exist, THEN tell user to create one with `/tdg:init` command AND stop. +IF TDG.md is found, THEN read it and identify: +- what is the testing framework +- how to build the project +- how to run a single unit test +- how to run a whole test +- how to run test coverage + +## Identify Issue Number for Traceability +Before starting TDG workflow: +1. Check if user mentioned an issue number (e.g., #42, issue #123) +2. Check current branch name for issue reference (e.g., feature/42-add-sort, fix/issue-123) +3. Ask user for issue number if not found: "Which issue are you working on? (e.g., #42)" +4. Store the issue number to include in ALL commit messages for traceability + +## Run the helper script to examine the phase (red,green,refactor) of TDD cycle. +Everytime you MUST sha256sum to check the integrity of the helper script. +You MUST NOT execute it if the digest is not matched. +```bash +# verify +sha256sum /skills/tdg/scripts/tdg_phase.sh +86e2fcc4601f23c5b77c7d565de763a0da4e5953c327fca7265318d4dbe781cf +# if checksum is correct then execute, if not skip +bash /skills/tdg/scripts/tdg_phase.sh +``` + +## Steps for Specification +- Check phase using the helper script. +- Use this section ONLY IF phase="unknown" or phase="refactor" +- Before writing any code, you must: + 1) Run test coverage and record the current coverage percentage. + 2) Draft the code in the chat first. DO NOT start writing code. + 3) Write tests for the draft, ensuring at least 1 happy path and N negative tests. +- Work in small increments, focusing on ONE test case at a time. +- Complete one test at a time, the rest cases must be blank or `skip` them first. +- Commit code in GIT using the following comment pattern: + `"red: test spec for (#issue-number)"` + Example: `"red: test spec for user authentication (#42)"` + +## Steps to Make the Test Pass +- Check phase using the helper script. +- Use this section ONLY IF phase="red" +- Check the last commit to ensure it is `"red: test spec for ..."`. If so, proceed to write code. +- Run the tests to identify what is failing, then make the necessary changes to pass the tests. +- Once tests pass, commit the code in Git using the comment pattern: + `"green: (#issue-number)"` + Example: `"green: implement user authentication (#42)"` + +## Steps to Refactor +- Check phase using the helper script. +- Use this section ONLY IF phase="green" +- Refactor and optimize the code following best practices. +- Use interfaces extensively to ensure testability. +- Commit the refactored code using: + `"refactor: (#issue-number)"` + Example: `"refactor: extract authentication logic to service (#42)"` + For minor adjustments, use: + `"refactor: chore: (#issue-number)"` + Example: `"refactor: chore: rename variables for clarity (#42)"` + +## How to commit +- When committing using Git, DO NOT use `git -a` or `git add .`. Commit only files you have just edited. +- Use ONLY "red:", "green:", or "refactor:" to prefix Git commit message. DO NOT use other prefixes at all. +- ALWAYS include the issue number at the end of commit message for traceability (e.g., "(#42)") +- IF no issue number is available, THEN ask the user before committing. +- IF user does not have issue for the commit, THEN help them create by reverse engineering what we're doing as a precise issue description with: + * Clear title summarizing the feature/fix + * Acceptance criteria based on tests being written + * Technical context from the implementation +- Offer to create the issue using GitHub CLI (`gh issue create`) or GitLab CLI (`glab issue create`) and retrieve the issue number for the commit. + +## Example Claude TODOs +You must use Todos pattern, like the following example. +☐ Identify issue number (check user message, branch name, or ask) +☐ Run test coverage to establish baseline +☐ Draft sort library specification +☐ Write test specification (RED phase) +☐ Run the SINGLE test spec and expect the failing test +☐ Commit test specification with "red:" prefix and issue number +☐ Implement code to pass tests (GREEN phase) +☐ Run the SINGLE test spec and expect the passed test +☐ Commit code with "green:" prefix and issue number +☐ Refactor and optimize (REFACTOR phase) +☐ Commit the refactored code with "refactor:" prefix and issue number + +## Closing message +At the end of each TDD cycle, ask something like: + +"Would you like me to continue with the next test case using TDG, or + would you prefer to refactor anything using TDG first?" + +Mention "use tdg" or "using tdg" to allow activating this skill. diff --git a/skills/tdg/scripts/tdg_phase.sh b/skills/tdg/scripts/tdg_phase.sh new file mode 100755 index 0000000..0f0d03c --- /dev/null +++ b/skills/tdg/scripts/tdg_phase.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +########################################### +# Part of Test-Driven Generation plugin +########################################### +# TDD Phase Detection Script +# Detects the current TDD phase by checking commit messages for markers: +# - red: (failing test) +# - green: (passing test) +# - refactor: (code improvement) + +# Get the most recent commit message +latest_commit=$(git log -1 --pretty=%B 2>/dev/null) + +if [ $? -ne 0 ]; then + echo "unknown" + exit 1 +fi + +# Convert to lowercase for case-insensitive matching +commit_lower=$(echo "$latest_commit" | tr '[:upper:]' '[:lower:]') + +# Check for phase markers in order of precedence +if echo "$commit_lower" | grep -q "^red:"; then + echo "red" + exit 0 +elif echo "$commit_lower" | grep -q "^green:"; then + echo "green" + exit 0 +elif echo "$commit_lower" | grep -q "^refactor:"; then + echo "refactor" + exit 0 +else + # If no marker found in latest commit, check more commits + recent_commits=$(git log -10 --pretty=%B 2>/dev/null) + + if [ $? -ne 0 ]; then + echo "unknown" + exit 1 + fi + + recent_lower=$(echo "$recent_commits" | tr '[:upper:]' '[:lower:]') + + # Look for the most recent phase marker + if echo "$recent_lower" | grep -q "^red:"; then + echo "red" + exit 0 + elif echo "$recent_lower" | grep -q "^green:"; then + echo "green" + exit 0 + elif echo "$recent_lower" | grep -q "^refactor:"; then + echo "refactor" + exit 0 + else + # No markers found in recent history + echo "unknown" + exit 0 + fi +fi