diff --git a/.changeset/claude-import-fix-new.md b/.changeset/claude-import-fix-new.md deleted file mode 100644 index acaf95d3..00000000 --- a/.changeset/claude-import-fix-new.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"task-master-ai": patch ---- - -Prevent CLAUDE.md overwrite by using Claude Code's import feature - -- Task Master now creates its instructions in `.taskmaster/CLAUDE.md` instead of overwriting the user's `CLAUDE.md` -- Adds an import section to the user's CLAUDE.md that references the Task Master instructions -- Preserves existing user content in CLAUDE.md files -- Provides clean uninstall that only removes Task Master's additions - -**Breaking Change**: Task Master instructions for Claude Code are now stored in `.taskmaster/CLAUDE.md` and imported into the main CLAUDE.md file. Users who previously had Task Master content directly in their CLAUDE.md will need to run `task-master rules remove claude` followed by `task-master rules add claude` to migrate to the new structure. \ No newline at end of file diff --git a/.changeset/ten-glasses-feel.md b/.changeset/ten-glasses-feel.md deleted file mode 100644 index d91910cd..00000000 --- a/.changeset/ten-glasses-feel.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"task-master-ai": patch ---- - -Fixed the comprehensive taskmaster system integration via custom slash commands with proper syntax - -- Provide claude clode with a complete set of of commands that can trigger task master events directly within Claude Code diff --git a/.changeset/upset-places-take.md b/.changeset/upset-places-take.md deleted file mode 100644 index fb03158e..00000000 --- a/.changeset/upset-places-take.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -"task-master-ai": patch ---- -Complete VS Code extension with React-based kanban board UI. MCP integration for real-time Task Master synchronization. Professional CI/CD workflows for marketplace publishing \ No newline at end of file diff --git a/.github/workflows/extension-ci.yml b/.github/workflows/extension-ci.yml index e18f2333..9409dcf9 100644 --- a/.github/workflows/extension-ci.yml +++ b/.github/workflows/extension-ci.yml @@ -200,4 +200,4 @@ jobs: with: name: extension-test-results path: apps/extension/test-results - retention-days: 30 \ No newline at end of file + retention-days: 30 diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml new file mode 100644 index 00000000..a5ce3250 --- /dev/null +++ b/.github/workflows/version.yml @@ -0,0 +1,69 @@ +name: Version & Publish + +on: + push: + branches: + - main + +permissions: + contents: write + id-token: write + +jobs: + version: + name: Version & Publish Extension + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: latest + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install root dependencies + run: pnpm install --frozen-lockfile + + - name: Install extension dependencies + working-directory: apps/extension + run: pnpm install --frozen-lockfile + + - name: Install vsce and ovsx globally + run: pnpm add -g @vscode/vsce ovsx + + - name: Make release script executable + run: chmod +x scripts/release.sh + + - name: Create Release Pull Request or Publish + uses: changesets/action@v1 + with: + publish: ./scripts/release.sh + title: "Release: Extension Version Packages" + commit: "chore: release extension packages" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + VSCE_PAT: ${{ secrets.VSCE_PAT }} + OVSX_PAT: ${{ secrets.OVSX_PAT }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 48219747..61c46ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # task-master-ai +## 0.20.1 + +### Patch Changes + +- f662654: Prevent CLAUDE.md overwrite by using Claude Code's import feature + - Task Master now creates its instructions in `.taskmaster/CLAUDE.md` instead of overwriting the user's `CLAUDE.md` + - Adds an import section to the user's CLAUDE.md that references the Task Master instructions + - Preserves existing user content in CLAUDE.md files + - Provides clean uninstall that only removes Task Master's additions + + **Breaking Change**: Task Master instructions for Claude Code are now stored in `.taskmaster/CLAUDE.md` and imported into the main CLAUDE.md file. Users who previously had Task Master content directly in their CLAUDE.md will need to run `task-master rules remove claude` followed by `task-master rules add claude` to migrate to the new structure. + +- 7b4803a: Fixed the comprehensive taskmaster system integration via custom slash commands with proper syntax + - Provide claude clode with a complete set of of commands that can trigger task master events directly within Claude Code + +- 8209f8d: Complete VS Code extension with React-based kanban board UI. MCP integration for real-time Task Master synchronization. Professional CI/CD workflows for marketplace publishing + ## 0.20.0 ### Minor Changes diff --git a/apps/extension/CHANGELOG.md b/apps/extension/CHANGELOG.md index f8a9729a..351487a9 100644 --- a/apps/extension/CHANGELOG.md +++ b/apps/extension/CHANGELOG.md @@ -1,9 +1,26 @@ # Change Log +## 1.0.0 + +### Minor Changes + +- Add automated CI/CD pipeline for VS Code extension + - Add automated version management and publishing via changesets + - Create tag-extension script to manage extension-specific git tags + - Add automated publishing to VS Code Marketplace and Open VSX Registry + - Set up release script with proper build process integration + - Add version.yml workflow for automated releases on main branch pushes + +### Patch Changes + +- Test extension version automation + - Verify that extension versions are properly managed by changesets + - Ensure proper integration with the 3-file packaging system + All notable changes to the vscode extension will be documented in this file. Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. ## [Unreleased] -- Initial release \ No newline at end of file +- Initial release diff --git a/apps/extension/docs/extension-CI-setup.md b/apps/extension/docs/extension-CI-setup.md index fb1116e2..37b0e3ee 100644 --- a/apps/extension/docs/extension-CI-setup.md +++ b/apps/extension/docs/extension-CI-setup.md @@ -1,15 +1,17 @@ # VS Code Extension CI/CD Setup -This document explains the CI/CD setup for the Task Master VS Code extension. +This document explains the CI/CD setup for the Task Master VS Code extension using automated changesets. ## ๐Ÿ”„ Workflows Overview ### 1. Extension CI (`extension-ci.yml`) + **Triggers:** - Push to `main` or `next` branches (only when extension files change) - Pull requests to `main` or `next` (only when extension files change) **What it does:** + - โœ… Lints and type-checks the extension code - ๐Ÿ”จ Builds the extension (`pnpm run build`) - ๐Ÿ“ฆ Creates a clean package (`pnpm run package`) @@ -17,23 +19,56 @@ This document explains the CI/CD setup for the Task Master VS Code extension. - ๐Ÿ“‹ Creates a test VSIX package to verify packaging works - ๐Ÿ’พ Uploads build artifacts for inspection -### 2. Extension Release (`extension-release.yml`) +### 2. Version & Publish (`version.yml`) + **Triggers:** -- Push to `main` branch (only when extension files change AND version changes) -- Manual trigger with `workflow_dispatch` (with optional force publish) +- Push to `main` branch **What it does:** -- ๐Ÿ” Checks if the extension version changed -- ๐Ÿงช Runs full test suite (lint, typecheck, tests) -- ๐Ÿ”จ Builds and packages the extension -- ๐Ÿ“ค Publishes to VS Code Marketplace -- ๐ŸŒ Publishes to Open VSX Registry (for VSCodium, Gitpod, etc.) -- ๐Ÿท๏ธ Creates a GitHub release with the VSIX file -- ๐Ÿ“Š Uploads release artifacts +- ๐Ÿ” Detects changeset files for pending releases +- ๐Ÿ“ Creates "Version Packages" PR with updated versions and CHANGELOG +- ๐Ÿค– When Version PR is merged, automatically: + - ๐Ÿ”จ Builds and packages the extension + - ๐Ÿท๏ธ Creates git tags with changeset automation + - ๐Ÿ“ค Publishes to VS Code Marketplace + - ๐ŸŒ Publishes to Open VSX Registry + - ๐Ÿ“Š Updates package versions and CHANGELOG + +## ๐Ÿš€ Changeset Workflow + +### Creating Changes +When making changes to the extension: + +1. **Make your code changes** +2. **Create a changeset**: + ```bash + # From project root + npx changeset add + ``` +3. **Select the extension package**: Choose `taskr-kanban` when prompted +4. **Select version bump type**: + - `patch`: Bug fixes, minor updates + - `minor`: New features, backwards compatible + - `major`: Breaking changes +5. **Write a summary**: Describe what changed for users +6. **Commit changeset file** along with your code changes +7. **Push to feature branch** and create PR + +### Automated Publishing Process +1. **PR with changeset** gets merged to `main` +2. **Version workflow** detects changesets and creates "Version Packages" PR +3. **Review and merge** the Version PR +4. **Automated publishing** happens immediately: + - Extension is built using 3-file packaging system + - VSIX package is created and tested + - Published to VS Code Marketplace (if `VSCE_PAT` is set) + - Published to Open VSX Registry (if `OVSX_PAT` is set) + - Git tags are created: `taskr-kanban@1.0.1` + - CHANGELOG is updated automatically ## ๐Ÿ”‘ Required Secrets -To use the release workflow, you need to set up these GitHub repository secrets: +To use the automated publishing, you need to set up these GitHub repository secrets: ### `VSCE_PAT` (VS Code Marketplace Personal Access Token) 1. Go to [Azure DevOps](https://dev.azure.com/) @@ -57,45 +92,32 @@ To use the release workflow, you need to set up these GitHub repository secrets: ### `GITHUB_TOKEN` (automatically provided) This is automatically available in GitHub Actions - no setup required. -## ๐Ÿš€ Publishing Process - -### Automatic Publishing (Recommended) -1. **Make changes** to the extension code -2. **Update version** in both: - - `apps/extension/package.json` - - `apps/extension/package.publish.json` -3. **Commit and push** to `main` branch -4. **CI automatically triggers** and publishes if version changed - -### Manual Publishing -1. Go to **Actions** tab in GitHub -2. Select **Extension Release** workflow -3. Click **Run workflow** -4. Check **"Force publish even without version changes"** if needed -5. Click **Run workflow** - ## ๐Ÿ“‹ Version Management -### Version Sync Checklist -When updating the extension version, ensure these fields match in both files: +### Changeset-Based Versioning +Versions are automatically managed by changesets: + +- **No manual version updates needed** - changesets handle this automatically +- **Semantic versioning** is enforced based on changeset types +- **Changelog generation** happens automatically +- **Git tagging** is handled by the automation + +### Critical Fields Sync +The automation ensures these fields stay in sync between `package.json` and `package.publish.json`: -**`package.json` and `package.publish.json`:** ```json { - "version": "1.0.2", // โš ๏ธ MUST MATCH - "publisher": "DavidMaliglowka", // โš ๏ธ MUST MATCH - "displayName": "Task Master Kanban", // โš ๏ธ MUST MATCH - "description": "...", // โš ๏ธ MUST MATCH - "engines": { "vscode": "^1.93.0" }, // โš ๏ธ MUST MATCH - "categories": [...], // โš ๏ธ MUST MATCH - "contributes": { ... } // โš ๏ธ MUST MATCH + "version": "1.0.2", // โœ… AUTO-SYNCED + "publisher": "DavidMaliglowka", // โš ๏ธ MUST MATCH MANUALLY + "displayName": "taskr: Task Master Kanban", // โš ๏ธ MUST MATCH MANUALLY + "description": "...", // โš ๏ธ MUST MATCH MANUALLY + "engines": { "vscode": "^1.93.0" }, // โš ๏ธ MUST MATCH MANUALLY + "categories": [...], // โš ๏ธ MUST MATCH MANUALLY + "contributes": { ... } // โš ๏ธ MUST MATCH MANUALLY } ``` -### Version Detection Logic -The release workflow only publishes when: -- Extension files changed in the push, AND -- Version field changed in `package.json` or `package.publish.json` +**Note**: Only `version` is automatically synced. Other fields must be manually kept in sync. ## ๐Ÿ” Monitoring Builds @@ -104,44 +126,61 @@ The release workflow only publishes when: - **Red โŒ**: Build/test failures - check logs for details - **Yellow ๐ŸŸก**: Partial success - some jobs may have warnings +### Version PR Status +- **Version PR Created**: Changesets detected, review and merge to publish +- **No Version PR**: No changesets found, no releases pending +- **Version PR Merged**: Automated publishing triggered + ### Release Status -- **Published ๐ŸŽ‰**: Extension live on VS Code Marketplace -- **Skipped โ„น๏ธ**: No version changes detected +- **Published ๐ŸŽ‰**: Extension live on VS Code Marketplace and Open VSX +- **Skipped โ„น๏ธ**: No changesets found, no release needed - **Failed โŒ**: Check logs - often missing secrets or build issues ### Artifacts -Both workflows upload artifacts that you can download: +Workflows upload artifacts that you can download: - **CI**: Test results, built files, and VSIX package -- **Release**: Final VSIX package and build artifacts (90-day retention) +- **Version**: Final VSIX package and published extension ## ๐Ÿ› ๏ธ Troubleshooting ### Common Issues -**"VSCE_PAT is not set" Error** +#### No Version PR Created +- **Check**: Changeset files exist in `.changeset/` directory +- **Check**: Changeset refers to `taskr-kanban` package name +- **Check**: Changes were pushed to `main` branch +- **Solution**: Create changeset with `npx changeset add` + +#### Version PR Not Publishing +- **Check**: Version PR was actually merged (not just closed) +- **Check**: Required secrets (`VSCE_PAT`, `OVSX_PAT`) are set +- **Check**: No build failures in workflow logs +- **Solution**: Re-run workflow or check secret configuration + +#### `VSCE_PAT` is not set Error - Ensure `VSCE_PAT` secret is added to repository - Check token hasn't expired -- Verify token has Marketplace > Manage permissions +- Verify token has Marketplace โ†’ Manage permissions -**"OVSX_PAT is not set" Error** +#### `OVSX_PAT` is not set Error - Ensure `OVSX_PAT` secret is added to repository - Check token hasn't expired - Verify you're signed in to Open VSX Registry with GitHub -**"Version not changed" Skipped Release** -- Update version in both `package.json` AND `package.publish.json` -- Ensure files are committed and pushed -- Use manual trigger with force publish if needed - -**Build Failures** +#### Build Failures - Check extension code compiles locally: `cd apps/extension && pnpm run build` - Verify tests pass locally: `pnpm run test` - Check for TypeScript errors: `pnpm run check-types` -**Packaging Failures** +#### Packaging Failures - Ensure clean package builds: `pnpm run package` - Check vsix-build structure is correct -- Verify package.publish.json has correct fields +- Verify `package.publish.json` has correct `repository` field + +#### Changeset Issues +- **Wrong package name**: Ensure changeset refers to `taskr-kanban` +- **Invalid format**: Check changeset markdown format is correct +- **Merge conflicts**: Resolve any conflicts in changeset files ## ๐Ÿ“ File Structure Impact @@ -149,11 +188,22 @@ The CI workflows respect the 3-file packaging system: - **Development**: Uses `package.json` for dependencies and scripts - **Release**: Uses `package.publish.json` for clean marketplace package - **Build**: Uses `package.mjs` to create `vsix-build/` for final packaging +- **Changesets**: Automatically manage versions across the system -This ensures clean, conflict-free publishing to both VS Code Marketplace and Open VSX Registry! ๐Ÿš€ - -## ๐ŸŒ **Dual Registry Publishing** +## ๐ŸŒ Dual Registry Publishing Your extension will be automatically published to both: - **VS Code Marketplace** - For official VS Code users -- **Open VSX Registry** - For Cursor, Windsurf, VSCodium, Gitpod, Eclipse Theia, and other compatible editors \ No newline at end of file +- **Open VSX Registry** - For Cursor, Windsurf, VSCodium, Gitpod, Eclipse Theia, and other compatible editors + +## ๐ŸŽฏ Benefits of Changeset Automation + +- โœ… **Automated versioning**: No manual version bumps needed +- โœ… **Generated changelogs**: Automatic, accurate release notes +- โœ… **Semantic versioning**: Enforced through changeset types +- โœ… **Git tagging**: Proper tags for extension releases +- โœ… **Conflict prevention**: Clear separation of extension vs. main package versions +- โœ… **Review process**: Version changes are reviewable via PR +- โœ… **Rollback capability**: Easy to revert if issues arise + +This ensures clean, predictable, and fully automated publishing to both registries! ๐Ÿš€ \ No newline at end of file diff --git a/apps/extension/docs/extension-development-guide.md b/apps/extension/docs/extension-development-guide.md index 4d97b6b2..0575d660 100644 --- a/apps/extension/docs/extension-development-guide.md +++ b/apps/extension/docs/extension-development-guide.md @@ -138,6 +138,65 @@ When updating extension metadata, ensure these fields match between `package.jso } ``` +## ๐Ÿค– Automated Release Process + +### Changesets Workflow +This extension uses [Changesets](https://github.com/changesets/changesets) for automated version management and publishing. + +#### Adding Changes +When making changes to the extension: + +1. **Make your code changes** +2. **Create a changeset**: + ```bash + # From project root + npx changeset add + ``` +3. **Select the extension package**: Choose `taskr-kanban` when prompted +4. **Select version bump type**: + - `patch`: Bug fixes, minor updates + - `minor`: New features, backwards compatible + - `major`: Breaking changes +5. **Write a summary**: Describe what changed for users + +#### Automated Publishing +The automation workflow runs on pushes to `main`: + +1. **Version Workflow** (`.github/workflows/version.yml`): + - Detects when changesets exist + - Creates a "Version Packages" PR with updated versions and CHANGELOG + - When the PR is merged, automatically publishes the extension + +2. **Release Process** (`scripts/release.sh`): + - Builds the extension using the 3-file packaging system + - Creates VSIX package + - Publishes to VS Code Marketplace (if `VSCE_PAT` is set) + - Publishes to Open VSX Registry (if `OVSX_PAT` is set) + - Creates git tags for the extension version + +#### Required Secrets +For automated publishing, these secrets must be set in the repository: + +- `VSCE_PAT`: Personal Access Token for VS Code Marketplace +- `OVSX_PAT`: Personal Access Token for Open VSX Registry +- `GITHUB_TOKEN`: Automatically provided by GitHub Actions + +#### Manual Release +If needed, you can manually trigger a release: + +```bash +# From project root +./scripts/release.sh +``` + +### Extension Tagging +The extension uses a separate tagging strategy from the main package: + +- **Extension tags**: `taskr-kanban@1.0.1` +- **Main package tags**: `task-master-ai@2.1.0` + +This allows independent versioning and prevents conflicts in the monorepo. + ## ๐Ÿ” Troubleshooting ### Dependency Conflicts @@ -155,14 +214,32 @@ When updating extension metadata, ensure these fields match between `package.jso **Problem**: Extension works locally but fails when packaged **Check**: Ensure critical fields are synced between package files +### Changeset Issues +**Problem**: Version workflow not triggering +**Check**: +1. Changeset files exist in `.changeset/` +2. Package name in changeset matches `package.publish.json` +3. Changes are pushed to `main` branch + +**Problem**: Publishing fails +**Check**: +1. Required secrets are set in repository settings +2. `package.publish.json` has correct repository URL +3. Build process completes successfully + ## ๐Ÿ“ Version Release Checklist -1. **Update version** in both `package.json` and `package.publish.json` -2. **Update CHANGELOG.md** with new features/fixes +### Manual Releases +1. **Create changeset**: `npx changeset add` +2. **Update critical fields** in both `package.json` and `package.publish.json` 3. **Test locally** with `F5` in VS Code -4. **Build clean package**: `pnpm run package` -5. **Test packaged extension**: Install `.vsix` file -6. **Publish**: Upload to marketplace or distribute `.vsix` +4. **Commit and push** to trigger automated workflow + +### Automated Releases (Recommended) +1. **Create changeset**: `npx changeset add` +2. **Push to feature branch** and create PR +3. **Merge PR** - this triggers version PR creation +4. **Review and merge version PR** - this triggers automated publishing ## ๐ŸŽฏ Why This System? @@ -171,7 +248,9 @@ When updating extension metadata, ensure these fields match between `package.jso - **Faster packaging**: No dependency resolution during `vsce package` - **Maintainable**: Clear separation of dev vs. production configs - **Reliable**: Consistent, conflict-free packaging process +- **Automated**: Changesets handle versioning and publishing automatically +- **Traceable**: Clear changelog and git tags for every release --- -**Remember**: Always use `pnpm run package` โ†’ `cd vsix-build` โ†’ `vsce package --no-dependencies` for production builds! ๐Ÿš€ \ No newline at end of file +**Remember**: Always use `npx changeset add` for changes, then push to trigger automated releases! ๐Ÿš€ diff --git a/apps/extension/package.json b/apps/extension/package.json index cf1ea2e4..cc2f2e04 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -1,14 +1,20 @@ { "name": "taskr", + "private": true, "displayName": "Task Master Kanban", "description": "A visual Kanban board interface for Task Master projects in VS Code", - "version": "1.0.0", + "version": "1.1.0", "publisher": "DavidMaliglowka", "icon": "assets/icon.png", "engines": { "vscode": "^1.93.0" }, - "categories": ["AI", "Visualization", "Education", "Other"], + "categories": [ + "AI", + "Visualization", + "Education", + "Other" + ], "main": "./dist/extension.js", "contributes": { "commands": [ @@ -42,7 +48,11 @@ "items": { "type": "string" }, - "default": ["-y", "--package=task-master-ai", "task-master-ai"], + "default": [ + "-y", + "--package=task-master-ai", + "task-master-ai" + ], "description": "An array of arguments to pass to the MCP server command." }, "taskmaster.mcp.cwd": { @@ -102,7 +112,11 @@ }, "taskmaster.ui.theme": { "type": "string", - "enum": ["auto", "light", "dark"], + "enum": [ + "auto", + "light", + "dark" + ], "default": "auto", "description": "UI theme preference" }, @@ -163,7 +177,12 @@ }, "taskmaster.debug.logLevel": { "type": "string", - "enum": ["error", "warn", "info", "debug"], + "enum": [ + "error", + "warn", + "info", + "debug" + ], "default": "info", "description": "Logging level" }, diff --git a/apps/extension/package.mjs b/apps/extension/package.mjs index 1efd91fc..88ec5587 100644 --- a/apps/extension/package.mjs +++ b/apps/extension/package.mjs @@ -52,12 +52,30 @@ try { } } - // 5. Copy and RENAME the clean manifest - console.log('Copying and preparing the final package.json...'); - fs.copySync( - path.resolve(__dirname, 'package.publish.json'), - path.resolve(packageDir, 'package.json') - ); + // 5. Sync versions and prepare the final package.json + console.log('Syncing versions and preparing the final package.json...'); + + // Read current versions + const devPackagePath = path.resolve(__dirname, 'package.json'); + const publishPackagePath = path.resolve(__dirname, 'package.publish.json'); + + const devPackage = JSON.parse(fs.readFileSync(devPackagePath, 'utf8')); + const publishPackage = JSON.parse(fs.readFileSync(publishPackagePath, 'utf8')); + + // Check if versions are in sync + if (devPackage.version !== publishPackage.version) { + console.log(` - Version sync needed: ${publishPackage.version} โ†’ ${devPackage.version}`); + publishPackage.version = devPackage.version; + + // Update the source package.publish.json file + fs.writeFileSync(publishPackagePath, JSON.stringify(publishPackage, null, '\t') + '\n'); + console.log(` - Updated package.publish.json version to ${devPackage.version}`); + } else { + console.log(` - Versions already in sync: ${devPackage.version}`); + } + + // Copy the (now synced) package.publish.json as package.json + fs.copySync(publishPackagePath, path.resolve(packageDir, 'package.json')); console.log(' - Copied package.publish.json as package.json'); // 6. Copy .vscodeignore if it exists @@ -97,13 +115,10 @@ try { `cd vsix-build && pnpm exec vsce package --no-dependencies` ); - // Read version from package.publish.json - const publishPackage = JSON.parse( - fs.readFileSync(path.resolve(__dirname, 'package.publish.json'), 'utf8') - ); - const version = publishPackage.version; + // Use the synced version for output + const finalVersion = devPackage.version; console.log( - `\nYour extension will be packaged to: vsix-build/taskr-${version}.vsix` + `\nYour extension will be packaged to: vsix-build/taskr-kanban-${finalVersion}.vsix` ); } catch (error) { console.error('\nโŒ Packaging failed!'); diff --git a/apps/extension/package.publish.json b/apps/extension/package.publish.json index 7bc7c71a..a2f4710a 100644 --- a/apps/extension/package.publish.json +++ b/apps/extension/package.publish.json @@ -8,7 +8,12 @@ "engines": { "vscode": "^1.93.0" }, - "categories": ["AI", "Visualization", "Education", "Other"], + "categories": [ + "AI", + "Visualization", + "Education", + "Other" + ], "keywords": [ "kanban", "kanban board", @@ -82,7 +87,11 @@ "items": { "type": "string" }, - "default": ["-y", "--package=task-master-ai", "task-master-ai"], + "default": [ + "-y", + "--package=task-master-ai", + "task-master-ai" + ], "description": "An array of arguments to pass to the MCP server command." }, "taskmaster.mcp.cwd": { @@ -142,7 +151,11 @@ }, "taskmaster.ui.theme": { "type": "string", - "enum": ["auto", "light", "dark"], + "enum": [ + "auto", + "light", + "dark" + ], "default": "auto", "description": "UI theme preference" }, @@ -203,7 +216,12 @@ }, "taskmaster.debug.logLevel": { "type": "string", - "enum": ["error", "warn", "info", "debug"], + "enum": [ + "error", + "warn", + "info", + "debug" + ], "default": "info", "description": "Logging level" }, diff --git a/apps/extension/src/components/TaskDetailsView.tsx b/apps/extension/src/components/TaskDetailsView.tsx index 72a71b5f..a0f42ca5 100644 --- a/apps/extension/src/components/TaskDetailsView.tsx +++ b/apps/extension/src/components/TaskDetailsView.tsx @@ -262,7 +262,7 @@ export const TaskDetailsView: React.FC = ({ }) => { const context = useContext(VSCodeContext); if (!context) - throw new Error('TaskDetailsView must be used within VSCodeContext'); + {throw new Error('TaskDetailsView must be used within VSCodeContext');} const { state, sendMessage } = context; const { tasks } = state; @@ -372,7 +372,7 @@ export const TaskDetailsView: React.FC = ({ // Handle running complexity analysis for a task const handleRunComplexityAnalysis = useCallback(async () => { - if (!currentTask) return; + if (!currentTask) {return;} setIsLoadingComplexity(true); try { @@ -416,7 +416,7 @@ export const TaskDetailsView: React.FC = ({ // Function to fetch task file data (implementation details and test strategy only) const fetchTaskFileData = async () => { - if (!currentTask?.id) return; + if (!currentTask?.id) {return;} setIsLoadingTaskFileData(true); setTaskFileDataError(null); @@ -543,7 +543,7 @@ export const TaskDetailsView: React.FC = ({ // Handle AI Actions const handleRegenerate = async () => { - if (!currentTask || !prompt.trim()) return; + if (!currentTask || !prompt.trim()) {return;} setIsRegenerating(true); try { @@ -584,7 +584,7 @@ export const TaskDetailsView: React.FC = ({ }; const handleAppend = async () => { - if (!currentTask || !prompt.trim()) return; + if (!currentTask || !prompt.trim()) {return;} setIsAppending(true); try { @@ -626,7 +626,7 @@ export const TaskDetailsView: React.FC = ({ // Handle adding a new subtask const handleAddSubtask = async () => { - if (!currentTask || !newSubtaskTitle.trim() || isSubtask) return; + if (!currentTask || !newSubtaskTitle.trim() || isSubtask) {return;} setIsSubmittingSubtask(true); try { @@ -672,7 +672,7 @@ export const TaskDetailsView: React.FC = ({ // Handle status change const handleStatusChange = async (newStatus: TaskMasterTask['status']) => { - if (!currentTask) return; + if (!currentTask) {return;} try { await sendMessage({ diff --git a/apps/extension/src/extension.ts b/apps/extension/src/extension.ts index 1fbf0d96..d7da74e1 100644 --- a/apps/extension/src/extension.ts +++ b/apps/extension/src/extension.ts @@ -24,6 +24,7 @@ import { } from './utils/errorHandler'; import { getToastDuration } from './utils/notificationPreferences'; import { parseTaskFileData } from './utils/taskFileReader'; +import { TaskMasterTask } from './utils/taskMasterApi'; // Global MCP client manager instance let mcpClient: MCPClientManager | null = null; @@ -39,7 +40,7 @@ interface PollingState { timer?: NodeJS.Timeout; isPolling: boolean; interval: number; - lastTaskData?: any[]; + lastTaskData?: TaskMasterTask[]; errorCount: number; maxErrors: number; // Adaptive frequency properties @@ -55,7 +56,7 @@ interface PollingState { reconnectBackoffMultiplier: number; lastSuccessfulConnection?: number; isOfflineMode: boolean; - cachedTaskData?: any[]; + cachedTaskData?: TaskMasterTask[]; } let pollingState: PollingState = { @@ -351,7 +352,7 @@ function adjustPollingFrequency(): void { // Low activity: reduce polling frequency with exponential backoff const backoffMultiplier = Math.min( 4, - Math.pow(1.5, pollingState.consecutiveNoChanges - 3) + 1.5 ** (pollingState.consecutiveNoChanges - 3) ); newInterval = Math.min( pollingState.maxInterval, @@ -1031,43 +1032,65 @@ export function activate(context: vscode.ExtensionContext) { case 'readTaskFileData': console.log('๐Ÿ“„ Reading task file data:', message.data); - const { requestId } = message; - try { - const { taskId, tag: tagName = 'master' } = message.data; + { + const { requestId } = message; + try { + const { taskId, tag: tagName = 'master' } = message.data; - // Get workspace folder - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) { - throw new Error('No workspace folder found'); - } + // Get workspace folder + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (!workspaceFolder) { + throw new Error('No workspace folder found'); + } - // Build path to tasks.json - const tasksJsonPath = path.join( - workspaceFolder.uri.fsPath, - '.taskmaster', - 'tasks', - 'tasks.json' - ); - console.log('๐Ÿ” Looking for tasks.json at:', tasksJsonPath); - - // Check if file exists - if (!fs.existsSync(tasksJsonPath)) { - // Try legacy location - const legacyPath = path.join( + // Build path to tasks.json + const tasksJsonPath = path.join( workspaceFolder.uri.fsPath, + '.taskmaster', 'tasks', 'tasks.json' ); - console.log('๐Ÿ” Trying legacy path:', legacyPath); - if (!fs.existsSync(legacyPath)) { - throw new Error( - 'tasks.json not found in .taskmaster/tasks/ or tasks/ directory' + console.log('๐Ÿ” Looking for tasks.json at:', tasksJsonPath); + + // Check if file exists + if (!fs.existsSync(tasksJsonPath)) { + // Try legacy location + const legacyPath = path.join( + workspaceFolder.uri.fsPath, + 'tasks', + 'tasks.json' ); + console.log('๐Ÿ” Trying legacy path:', legacyPath); + if (!fs.existsSync(legacyPath)) { + throw new Error( + 'tasks.json not found in .taskmaster/tasks/ or tasks/ directory' + ); + } + // Use legacy path + const content = fs.readFileSync(legacyPath, 'utf8'); + console.log( + '๐Ÿ“– Read legacy tasks.json, content length:', + content.length + ); + const taskData = parseTaskFileData( + content, + taskId, + tagName, + workspaceFolder.uri.fsPath + ); + console.log('โœ… Parsed task data for legacy path:', taskData); + panel.webview.postMessage({ + type: 'response', + requestId, + data: taskData + }); + return; } - // Use legacy path - const content = fs.readFileSync(legacyPath, 'utf8'); + + // Read and parse tasks.json + const content = fs.readFileSync(tasksJsonPath, 'utf8'); console.log( - '๐Ÿ“– Read legacy tasks.json, content length:', + '๐Ÿ“– Read tasks.json, content length:', content.length ); const taskData = parseTaskFileData( @@ -1076,46 +1099,26 @@ export function activate(context: vscode.ExtensionContext) { tagName, workspaceFolder.uri.fsPath ); - console.log('โœ… Parsed task data for legacy path:', taskData); + console.log('โœ… Parsed task data:', taskData); + panel.webview.postMessage({ type: 'response', requestId, data: taskData }); - return; + + console.log(`โœ… Retrieved task file data for task ${taskId}`); + } catch (error) { + console.error('โŒ Error reading task file data:', error); + panel.webview.postMessage({ + type: 'error', + requestId, + error: + error instanceof Error + ? error.message + : 'Failed to read task file data' + }); } - - // Read and parse tasks.json - const content = fs.readFileSync(tasksJsonPath, 'utf8'); - console.log( - '๐Ÿ“– Read tasks.json, content length:', - content.length - ); - const taskData = parseTaskFileData( - content, - taskId, - tagName, - workspaceFolder.uri.fsPath - ); - console.log('โœ… Parsed task data:', taskData); - - panel.webview.postMessage({ - type: 'response', - requestId, - data: taskData - }); - - console.log(`โœ… Retrieved task file data for task ${taskId}`); - } catch (error) { - console.error('โŒ Error reading task file data:', error); - panel.webview.postMessage({ - type: 'error', - requestId, - error: - error instanceof Error - ? error.message - : 'Failed to read task file data' - }); } break; diff --git a/apps/extension/src/webview/index.tsx b/apps/extension/src/webview/index.tsx index ed2aa8c5..f5b4cf06 100644 --- a/apps/extension/src/webview/index.tsx +++ b/apps/extension/src/webview/index.tsx @@ -648,14 +648,14 @@ const TaskEditModal: React.FC<{ // Only include changed fields const updates: TaskUpdates = {}; - if (formData.title !== task.title) updates.title = formData.title; + if (formData.title !== task.title) {updates.title = formData.title;} if (formData.description !== task.description) - updates.description = formData.description; - if (formData.details !== task.details) updates.details = formData.details; + {updates.description = formData.description;} + if (formData.details !== task.details) {updates.details = formData.details;} if (formData.priority !== task.priority) - updates.priority = formData.priority; + {updates.priority = formData.priority;} if (formData.testStrategy !== task.testStrategy) - updates.testStrategy = formData.testStrategy; + {updates.testStrategy = formData.testStrategy;} if ( JSON.stringify(formData.dependencies) !== JSON.stringify(task.dependencies) @@ -875,7 +875,7 @@ const TaskCard: React.FC<{ const TaskMasterKanban: React.FC = () => { const context = useContext(VSCodeContext); if (!context) - throw new Error('TaskMasterKanban must be used within VSCodeContext'); + {throw new Error('TaskMasterKanban must be used within VSCodeContext');} const { state, dispatch, sendMessage, availableHeight } = context; const { @@ -984,14 +984,14 @@ const TaskMasterKanban: React.FC = () => { dispatch({ type: 'SET_USER_INTERACTING', payload: false }); }, 1000); // 1 second delay to ensure smooth completion - if (!over) return; + if (!over) {return;} const taskId = active.id as string; const newStatus = over.id as TaskMasterTask['status']; // Find the task that was moved const task = tasks.find((t) => t.id === taskId); - if (!task || task.status === newStatus) return; + if (!task || task.status === newStatus) {return;} console.log(`๐Ÿ”„ Moving task ${taskId} from ${task.status} to ${newStatus}`); @@ -1382,7 +1382,7 @@ const App: React.FC = () => { // Handle messages from extension useEffect(() => { - if (!vscode) return; + if (!vscode) {return;} const handleMessage = (event: MessageEvent) => { const message: WebviewMessage = event.data; @@ -1526,13 +1526,13 @@ const App: React.FC = () => { // Map error severity to toast type let toastType: ToastNotification['type'] = 'error'; - if (errorData.severity === 'low') toastType = 'info'; - else if (errorData.severity === 'medium') toastType = 'warning'; + if (errorData.severity === 'low') {toastType = 'info';} + else if (errorData.severity === 'medium') {toastType = 'warning';} else if ( errorData.severity === 'high' || errorData.severity === 'critical' ) - toastType = 'error'; + {toastType = 'error';} // Create appropriate toast based on error category const title = diff --git a/package.json b/package.json index 7af991f2..ea209d63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "task-master-ai", - "version": "0.20.0", + "version": "0.20.1", "description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.", "main": "index.js", "type": "module", @@ -9,7 +9,10 @@ "task-master-mcp": "mcp-server/server.js", "task-master-ai": "mcp-server/server.js" }, - "workspaces": ["apps/*", "."], + "workspaces": [ + "apps/*", + "." + ], "scripts": { "test": "node --experimental-vm-modules node_modules/.bin/jest", "test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures", diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 00000000..58673ce0 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,91 @@ +#!/bin/bash +set -e + +echo "๐Ÿš€ Starting release process..." + +# Ensure we're in the project root +cd "$(dirname "$0")/.." + +echo "๐Ÿ“ฆ Building and packaging extension..." + +# Navigate to extension directory and build +cd apps/extension + +# Install dependencies (in case they're not cached) +echo "๐Ÿ“ฅ Installing extension dependencies..." +pnpm install --frozen-lockfile + +# Run quality checks first (same as CI) +echo "๐Ÿ” Running lint checks..." +pnpm run lint + +echo "๐Ÿ” Running type checks..." +pnpm run check-types + +# Build the extension +echo "๐Ÿ”จ Building extension..." +pnpm run build + +# Create clean package +echo "๐Ÿ“ฆ Creating clean package..." +pnpm run package + +# Verify package contents (same as CI) +echo "๐Ÿ” Verifying package contents..." +echo "Checking vsix-build contents..." +ls -la vsix-build/ +echo "Checking dist contents..." +ls -la vsix-build/dist/ +echo "Checking package.json exists..." +test -f vsix-build/package.json + +# Create VSIX package +echo "๐Ÿ“ฆ Creating VSIX package..." +cd vsix-build +pnpm exec vsce package --no-dependencies --out "$PWD" + +# Run tests before publishing +echo "๐Ÿงช Running extension tests..." +cd .. +# Note: Tests run with xvfb-run in CI, but in release context we'll skip or handle differently +echo "โš ๏ธ Skipping tests in release context (run in CI validation)" + +# Go back to project root for tagging and changeset operations +cd ../.. + +echo "๐Ÿท๏ธ Checking extension tag..." +node scripts/tag-extension.mjs + +echo "๐Ÿ“ Publishing packages with changesets..." +# Let changesets handle all the npm publishing first +npx changeset publish + +echo "๐ŸŒ Publishing extension to VS Code Marketplace..." +# Find the generated VSIX file +VSIX_FILE=$(find apps/extension/vsix-build -name "*.vsix" | head -n 1) + +if [ -n "$VSIX_FILE" ]; then + echo "Found VSIX file: $VSIX_FILE" + + # Publish to VS Code Marketplace + if [ -n "$VSCE_PAT" ]; then + echo "Publishing to VS Code Marketplace..." + npx vsce publish --packagePath "$VSIX_FILE" + else + echo "โš ๏ธ VSCE_PAT not set, skipping VS Code Marketplace publish" + fi + + # Publish to Open VSX Registry + if [ -n "$OVSX_PAT" ]; then + echo "Publishing to Open VSX Registry..." + npx ovsx publish --packagePath "$VSIX_FILE" + else + echo "โš ๏ธ OVSX_PAT not set, skipping Open VSX publish" + fi + +else + echo "โŒ No VSIX file found!" + exit 1 +fi + +echo "โœ… Release process completed!" \ No newline at end of file diff --git a/scripts/tag-extension.mjs b/scripts/tag-extension.mjs new file mode 100644 index 00000000..bfd81931 --- /dev/null +++ b/scripts/tag-extension.mjs @@ -0,0 +1,52 @@ +#!/usr/bin/env node +import assert from 'node:assert/strict' +import { spawnSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { join, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +// Read the extension's publishing package.json for accurate version/name +const extensionDir = join(__dirname, '..', 'apps', 'extension') +const publishPkgPath = join(extensionDir, 'package.publish.json') + +let pkg +try { + const pkgContent = readFileSync(publishPkgPath, 'utf8') + pkg = JSON.parse(pkgContent) +} catch (error) { + console.error('Failed to read package.publish.json:', error.message) + process.exit(1) +} + +// Ensure we have required fields +assert(pkg.name, 'package.publish.json must have a name field') +assert(pkg.version, 'package.publish.json must have a version field') +assert(pkg.repository, 'package.publish.json must have a repository field') + +const tag = `${pkg.name}@${pkg.version}` + +// Get repository URL - handle both string and object format +const repoUrl = typeof pkg.repository === 'string' + ? pkg.repository + : pkg.repository.url + +assert(repoUrl, 'Repository URL not found in package.publish.json') + +const { status, stdout, error } = spawnSync('git', [ + 'ls-remote', + repoUrl, + tag +]) + +assert.equal(status, 0, error) + +const exists = String(stdout).trim() !== '' + +if (!exists) { + console.log(`\nNew extension tag: ${tag}`) +} else { + console.log(`\nExtension tag already exists: ${tag}`) +} \ No newline at end of file