Compare commits
1 Commits
task-maste
...
docs/auto-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ee8f67d9c |
5
.changeset/clarify-force-move-docs.md
Normal file
5
.changeset/clarify-force-move-docs.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
docs(move): clarify cross-tag move docs; deprecate "force"; add explicit --with-dependencies/--ignore-dependencies examples
|
||||||
@@ -6,14 +6,13 @@
|
|||||||
"repo": "eyaltoledano/claude-task-master"
|
"repo": "eyaltoledano/claude-task-master"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"commit": false,
|
"commit": true,
|
||||||
"fixed": [],
|
"fixed": [],
|
||||||
|
"linked": [],
|
||||||
"access": "public",
|
"access": "public",
|
||||||
"baseBranch": "main",
|
"baseBranch": "main",
|
||||||
|
"updateInternalDependencies": "patch",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"docs",
|
"docs"
|
||||||
"@tm/cli",
|
|
||||||
"@tm/core",
|
|
||||||
"@tm/build-config"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
5
.changeset/crazy-zebras-drum.md
Normal file
5
.changeset/crazy-zebras-drum.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Restore Taskmaster claude-code commands and move clear commands under /remove to avoid collision with the claude-code /clear command.
|
||||||
9
.changeset/curvy-moons-dig.md
Normal file
9
.changeset/curvy-moons-dig.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Enhanced Gemini CLI provider with codebase-aware task generation
|
||||||
|
|
||||||
|
Added automatic codebase analysis for Gemini CLI provider in parse-prd, and analyze-complexity, add-task, udpate-task, update, update-subtask commands
|
||||||
|
When using Gemini CLI as the AI provider, Task Master now instructs the AI to analyze the project structure, existing implementations, and patterns before generating tasks or subtasks
|
||||||
|
Tasks and subtasks generated by Claude Code are now informed by actual codebase analysis, resulting in more accurate and contextual outputs
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"task-master-ai": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Testing one more pre-release iteration
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"task-master-ai": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Test out the RC
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"@tm/cli": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
testing this stuff out to see how the release candidate works with monorepo
|
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
{
|
{
|
||||||
"mode": "pre",
|
"mode": "exit",
|
||||||
"tag": "rc",
|
"tag": "rc",
|
||||||
"initialVersions": {
|
"initialVersions": {
|
||||||
"task-master-ai": "0.26.0",
|
"task-master-ai": "0.25.1",
|
||||||
"@tm/cli": "0.26.0",
|
"docs": "0.0.1",
|
||||||
"docs": "0.0.2",
|
"extension": "0.24.1"
|
||||||
"extension": "0.24.2",
|
|
||||||
"@tm/build-config": "1.0.0",
|
|
||||||
"@tm/core": "0.26.0"
|
|
||||||
},
|
},
|
||||||
"changesets": [
|
"changesets": [
|
||||||
"easy-deer-heal",
|
"clarify-force-move-docs",
|
||||||
"moody-oranges-slide",
|
"curvy-moons-dig",
|
||||||
"odd-otters-tan",
|
"sour-coins-lay",
|
||||||
"wild-ears-look"
|
"strong-eagles-vanish",
|
||||||
|
"wet-candies-accept"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"task-master-ai": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
@tm/cli: add auto-update functionality to every command
|
|
||||||
11
.changeset/sour-coins-lay.md
Normal file
11
.changeset/sour-coins-lay.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add configurable codebase analysis feature flag with multiple configuration sources
|
||||||
|
|
||||||
|
Users can now control whether codebase analysis features (Claude Code and Gemini CLI integration) are enabled through environment variables, MCP configuration, or project config files.
|
||||||
|
|
||||||
|
Priority order: .env > MCP session env > .taskmaster/config.json.
|
||||||
|
|
||||||
|
Set `TASKMASTER_ENABLE_CODEBASE_ANALYSIS=false` in `.env` to disable codebase analysis prompts and tool integration.
|
||||||
12
.changeset/strong-eagles-vanish.md
Normal file
12
.changeset/strong-eagles-vanish.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(move): improve cross-tag move UX and safety
|
||||||
|
|
||||||
|
- CLI: print "Next Steps" tips after cross-tag moves that used --ignore-dependencies (validate/fix guidance)
|
||||||
|
- CLI: show dedicated help block on ID collisions (destination tag already has the ID)
|
||||||
|
- Core: add structured suggestions to TASK_ALREADY_EXISTS errors
|
||||||
|
- MCP: map ID collision errors to TASK_ALREADY_EXISTS and include suggestions
|
||||||
|
- Tests: cover MCP options, error suggestions, CLI tips printing, and integration error payload suggestions
|
||||||
|
---
|
||||||
14
.changeset/wet-candies-accept.md
Normal file
14
.changeset/wet-candies-accept.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Enhanced Claude Code and Google CLI integration with automatic codebase analysis for task operations
|
||||||
|
|
||||||
|
When using Claude Code as the AI provider, task management commands now automatically analyze your codebase before generating or updating tasks. This provides more accurate, context-aware implementation details that align with your project's existing architecture and patterns.
|
||||||
|
|
||||||
|
Commands contextualised:
|
||||||
|
|
||||||
|
- add-task
|
||||||
|
- update-subtask
|
||||||
|
- update-task
|
||||||
|
- update
|
||||||
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -11,10 +11,6 @@ on:
|
|||||||
- next
|
- next
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -92,16 +88,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
|
|
||||||
TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
|
|
||||||
TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: build-artifacts
|
|
||||||
path: dist/
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
@@ -122,11 +108,10 @@ jobs:
|
|||||||
run: npm install --frozen-lockfile --prefer-offline
|
run: npm install --frozen-lockfile --prefer-offline
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
|
|
||||||
- name: Download build artifacts
|
- name: Build packages (required for tests)
|
||||||
uses: actions/download-artifact@v4
|
run: npm run build:packages
|
||||||
with:
|
env:
|
||||||
name: build-artifacts
|
NODE_ENV: production
|
||||||
path: dist/
|
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
14
.github/workflows/pre-release.yml
vendored
14
.github/workflows/pre-release.yml
vendored
@@ -65,20 +65,6 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
- name: Run format
|
|
||||||
run: npm run format
|
|
||||||
env:
|
|
||||||
FORCE_COLOR: 1
|
|
||||||
|
|
||||||
- name: Build packages
|
|
||||||
run: npm run turbo:build
|
|
||||||
env:
|
|
||||||
NODE_ENV: production
|
|
||||||
FORCE_COLOR: 1
|
|
||||||
TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
|
|
||||||
TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
|
|
||||||
TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}
|
|
||||||
|
|
||||||
- name: Create Release Candidate Pull Request or Publish Release Candidate to npm
|
- name: Create Release Candidate Pull Request or Publish Release Candidate to npm
|
||||||
uses: changesets/action@v1
|
uses: changesets/action@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "npm"
|
cache: 'npm'
|
||||||
|
|
||||||
- name: Cache node_modules
|
- name: Cache node_modules
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@@ -41,15 +41,6 @@ jobs:
|
|||||||
- name: Check pre-release mode
|
- name: Check pre-release mode
|
||||||
run: node ./.github/scripts/check-pre-release-mode.mjs "main"
|
run: node ./.github/scripts/check-pre-release-mode.mjs "main"
|
||||||
|
|
||||||
- name: Build packages
|
|
||||||
run: npm run turbo:build
|
|
||||||
env:
|
|
||||||
NODE_ENV: production
|
|
||||||
FORCE_COLOR: 1
|
|
||||||
TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
|
|
||||||
TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
|
|
||||||
TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}
|
|
||||||
|
|
||||||
- name: Create Release Pull Request or Publish to npm
|
- name: Create Release Pull Request or Publish to npm
|
||||||
uses: changesets/action@v1
|
uses: changesets/action@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
66
CHANGELOG.md
66
CHANGELOG.md
@@ -1,71 +1,5 @@
|
|||||||
# task-master-ai
|
# task-master-ai
|
||||||
|
|
||||||
## 1.0.0-rc.2
|
|
||||||
|
|
||||||
### Major Changes
|
|
||||||
|
|
||||||
- [#1217](https://github.com/eyaltoledano/claude-task-master/pull/1217) [`e6de285`](https://github.com/eyaltoledano/claude-task-master/commit/e6de285ceacb0a397e952a63435cd32a9c731515) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - @tm/cli: add auto-update functionality to every command
|
|
||||||
|
|
||||||
## 0.27.0-rc.1
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- [`255b9f0`](https://github.com/eyaltoledano/claude-task-master/commit/255b9f0334555b0063280abde701445cd62fa11b) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Testing one more pre-release iteration
|
|
||||||
|
|
||||||
## 0.27.0-rc.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- [#1213](https://github.com/eyaltoledano/claude-task-master/pull/1213) [`137ef36`](https://github.com/eyaltoledano/claude-task-master/commit/137ef362789a9cdfdb1925e35e0438c1fa6c69ee) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Test out the RC
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [[`137ef36`](https://github.com/eyaltoledano/claude-task-master/commit/137ef362789a9cdfdb1925e35e0438c1fa6c69ee)]:
|
|
||||||
- @tm/cli@0.27.0-rc.0
|
|
||||||
|
|
||||||
## 0.26.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- [#1133](https://github.com/eyaltoledano/claude-task-master/pull/1133) [`df26c65`](https://github.com/eyaltoledano/claude-task-master/commit/df26c65632000874a73504963b08f18c46283144) Thanks [@neonwatty](https://github.com/neonwatty)! - Restore Taskmaster claude-code commands and move clear commands under /remove to avoid collision with the claude-code /clear command.
|
|
||||||
|
|
||||||
- [#1163](https://github.com/eyaltoledano/claude-task-master/pull/1163) [`37af0f1`](https://github.com/eyaltoledano/claude-task-master/commit/37af0f191227a68d119b7f89a377bf932ee3ac66) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhanced Gemini CLI provider with codebase-aware task generation
|
|
||||||
|
|
||||||
Added automatic codebase analysis for Gemini CLI provider in parse-prd, and analyze-complexity, add-task, udpate-task, update, update-subtask commands
|
|
||||||
When using Gemini CLI as the AI provider, Task Master now instructs the AI to analyze the project structure, existing implementations, and patterns before generating tasks or subtasks
|
|
||||||
Tasks and subtasks generated by Claude Code are now informed by actual codebase analysis, resulting in more accurate and contextual outputs
|
|
||||||
|
|
||||||
- [#1165](https://github.com/eyaltoledano/claude-task-master/pull/1165) [`c4f92f6`](https://github.com/eyaltoledano/claude-task-master/commit/c4f92f6a0aee3435c56eb8d27d9aa9204284833e) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add configurable codebase analysis feature flag with multiple configuration sources
|
|
||||||
|
|
||||||
Users can now control whether codebase analysis features (Claude Code and Gemini CLI integration) are enabled through environment variables, MCP configuration, or project config files.
|
|
||||||
|
|
||||||
Priority order: .env > MCP session env > .taskmaster/config.json.
|
|
||||||
|
|
||||||
Set `TASKMASTER_ENABLE_CODEBASE_ANALYSIS=false` in `.env` to disable codebase analysis prompts and tool integration.
|
|
||||||
|
|
||||||
- [#1135](https://github.com/eyaltoledano/claude-task-master/pull/1135) [`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f) Thanks [@mm-parthy](https://github.com/mm-parthy)! - feat(move): improve cross-tag move UX and safety
|
|
||||||
- CLI: print "Next Steps" tips after cross-tag moves that used --ignore-dependencies (validate/fix guidance)
|
|
||||||
- CLI: show dedicated help block on ID collisions (destination tag already has the ID)
|
|
||||||
- Core: add structured suggestions to TASK_ALREADY_EXISTS errors
|
|
||||||
- MCP: map ID collision errors to TASK_ALREADY_EXISTS and include suggestions
|
|
||||||
- Tests: cover MCP options, error suggestions, CLI tips printing, and integration error payload suggestions
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
- [#1162](https://github.com/eyaltoledano/claude-task-master/pull/1162) [`4dad2fd`](https://github.com/eyaltoledano/claude-task-master/commit/4dad2fd613ceac56a65ae9d3c1c03092b8860ac9) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhanced Claude Code and Google CLI integration with automatic codebase analysis for task operations
|
|
||||||
|
|
||||||
When using Claude Code as the AI provider, task management commands now automatically analyze your codebase before generating or updating tasks. This provides more accurate, context-aware implementation details that align with your project's existing architecture and patterns.
|
|
||||||
|
|
||||||
Commands contextualised:
|
|
||||||
- add-task
|
|
||||||
- update-subtask
|
|
||||||
- update-task
|
|
||||||
- update
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- [#1135](https://github.com/eyaltoledano/claude-task-master/pull/1135) [`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f) Thanks [@mm-parthy](https://github.com/mm-parthy)! - docs(move): clarify cross-tag move docs; deprecate "force"; add explicit --with-dependencies/--ignore-dependencies examples
|
|
||||||
|
|
||||||
## 0.26.0-rc.1
|
## 0.26.0-rc.1
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# @tm/cli
|
|
||||||
|
|
||||||
## 0.27.0-rc.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- [#1213](https://github.com/eyaltoledano/claude-task-master/pull/1213) [`137ef36`](https://github.com/eyaltoledano/claude-task-master/commit/137ef362789a9cdfdb1925e35e0438c1fa6c69ee) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - testing this stuff out to see how the release candidate works with monorepo
|
|
||||||
|
|
||||||
## 1.1.0-rc.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- [#1213](https://github.com/eyaltoledano/claude-task-master/pull/1213) [`cd90b4d`](https://github.com/eyaltoledano/claude-task-master/commit/cd90b4d65fc2f04bdad9fb73aba320b58a124240) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - testing this stuff out to see how the release candidate works with monorepo
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@tm/cli",
|
"name": "@tm/cli",
|
||||||
"version": "0.27.0-rc.0",
|
"version": "1.0.0",
|
||||||
"description": "Task Master CLI - Command line interface for task management",
|
"description": "Task Master CLI - Command line interface for task management",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./src/index.ts",
|
"types": "./src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { Command } from 'commander';
|
import { Command } from 'commander';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import inquirer from 'inquirer';
|
import inquirer from 'inquirer';
|
||||||
import ora, { Ora } from 'ora';
|
import ora from 'ora';
|
||||||
import {
|
import {
|
||||||
AuthManager,
|
AuthManager,
|
||||||
AuthenticationError,
|
AuthenticationError,
|
||||||
@@ -49,15 +49,8 @@ export class ContextCommand extends Command {
|
|||||||
this.addClearCommand();
|
this.addClearCommand();
|
||||||
this.addSetCommand();
|
this.addSetCommand();
|
||||||
|
|
||||||
// Accept optional positional argument for brief ID or Hamster URL
|
// Default action shows current context
|
||||||
this.argument('[briefOrUrl]', 'Brief ID or Hamster brief URL');
|
this.action(async () => {
|
||||||
|
|
||||||
// Default action: if an argument is provided, resolve and set context; else show
|
|
||||||
this.action(async (briefOrUrl?: string) => {
|
|
||||||
if (briefOrUrl && briefOrUrl.trim().length > 0) {
|
|
||||||
await this.executeSetFromBriefInput(briefOrUrl.trim());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.executeShow();
|
await this.executeShow();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -333,7 +326,7 @@ export class ContextCommand extends Command {
|
|||||||
choices: [
|
choices: [
|
||||||
{ name: '(No brief - organization level)', value: null },
|
{ name: '(No brief - organization level)', value: null },
|
||||||
...briefs.map((brief) => ({
|
...briefs.map((brief) => ({
|
||||||
name: `Brief ${brief.id} (${new Date(brief.createdAt).toLocaleDateString()})`,
|
name: `Brief ${brief.id.slice(0, 8)} (${new Date(brief.createdAt).toLocaleDateString()})`,
|
||||||
value: brief
|
value: brief
|
||||||
}))
|
}))
|
||||||
]
|
]
|
||||||
@@ -448,142 +441,6 @@ export class ContextCommand extends Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute setting context from a brief ID or Hamster URL
|
|
||||||
*/
|
|
||||||
private async executeSetFromBriefInput(briefOrUrl: string): Promise<void> {
|
|
||||||
let spinner: Ora | undefined;
|
|
||||||
try {
|
|
||||||
// Check authentication
|
|
||||||
if (!this.authManager.isAuthenticated()) {
|
|
||||||
ui.displayError('Not authenticated. Run "tm auth login" first.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
spinner = ora('Resolving brief...');
|
|
||||||
spinner.start();
|
|
||||||
|
|
||||||
// Extract brief ID
|
|
||||||
const briefId = this.extractBriefId(briefOrUrl);
|
|
||||||
if (!briefId) {
|
|
||||||
spinner.fail('Could not extract a brief ID from the provided input');
|
|
||||||
ui.displayError(
|
|
||||||
`Provide a valid brief ID or a Hamster brief URL, e.g. https://${process.env.TM_PUBLIC_BASE_DOMAIN}/home/hamster/briefs/<id>`
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch brief and resolve its organization
|
|
||||||
const brief = await this.authManager.getBrief(briefId);
|
|
||||||
if (!brief) {
|
|
||||||
spinner.fail('Brief not found or you do not have access');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch org to get a friendly name (optional)
|
|
||||||
let orgName: string | undefined;
|
|
||||||
try {
|
|
||||||
const org = await this.authManager.getOrganization(brief.accountId);
|
|
||||||
orgName = org?.name;
|
|
||||||
} catch {
|
|
||||||
// Non-fatal if org lookup fails
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update context: set org and brief
|
|
||||||
const briefName = `Brief ${brief.id.slice(0, 8)}`;
|
|
||||||
await this.authManager.updateContext({
|
|
||||||
orgId: brief.accountId,
|
|
||||||
orgName,
|
|
||||||
briefId: brief.id,
|
|
||||||
briefName
|
|
||||||
});
|
|
||||||
|
|
||||||
spinner.succeed('Context set from brief');
|
|
||||||
console.log(
|
|
||||||
chalk.gray(
|
|
||||||
` Organization: ${orgName || brief.accountId}\n Brief: ${briefName}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.setLastResult({
|
|
||||||
success: true,
|
|
||||||
action: 'set',
|
|
||||||
context: this.authManager.getContext() || undefined,
|
|
||||||
message: 'Context set from brief'
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
try {
|
|
||||||
if (spinner?.isSpinning) spinner.stop();
|
|
||||||
} catch {}
|
|
||||||
this.handleError(error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract a brief ID from raw input (ID or Hamster URL)
|
|
||||||
*/
|
|
||||||
private extractBriefId(input: string): string | null {
|
|
||||||
const raw = input?.trim() ?? '';
|
|
||||||
if (!raw) return null;
|
|
||||||
|
|
||||||
const parseUrl = (s: string): URL | null => {
|
|
||||||
try {
|
|
||||||
return new URL(s);
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
return new URL(`https://${s}`);
|
|
||||||
} catch {}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fromParts = (path: string): string | null => {
|
|
||||||
const parts = path.split('/').filter(Boolean);
|
|
||||||
const briefsIdx = parts.lastIndexOf('briefs');
|
|
||||||
const candidate =
|
|
||||||
briefsIdx >= 0 && parts.length > briefsIdx + 1
|
|
||||||
? parts[briefsIdx + 1]
|
|
||||||
: parts[parts.length - 1];
|
|
||||||
return candidate?.trim() || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1) URL (absolute or scheme‑less)
|
|
||||||
const url = parseUrl(raw);
|
|
||||||
if (url) {
|
|
||||||
const qId = url.searchParams.get('id') || url.searchParams.get('briefId');
|
|
||||||
const candidate = (qId || fromParts(url.pathname)) ?? null;
|
|
||||||
if (candidate) {
|
|
||||||
// Light sanity check; let API be the final validator
|
|
||||||
if (this.isLikelyId(candidate) || candidate.length >= 8)
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Looks like a path without scheme
|
|
||||||
if (raw.includes('/')) {
|
|
||||||
const candidate = fromParts(raw);
|
|
||||||
if (candidate && (this.isLikelyId(candidate) || candidate.length >= 8)) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3) Fallback: raw token
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Heuristic to check if a string looks like a brief ID (UUID-like)
|
|
||||||
*/
|
|
||||||
private isLikelyId(value: string): boolean {
|
|
||||||
const uuidRegex =
|
|
||||||
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
||||||
const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; // ULID
|
|
||||||
const slugRegex = /^[A-Za-z0-9_-]{16,}$/; // general token
|
|
||||||
return (
|
|
||||||
uuidRegex.test(value) || ulidRegex.test(value) || slugRegex.test(value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set context directly from options
|
* Set context directly from options
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,318 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview SetStatusCommand using Commander's native class pattern
|
|
||||||
* Extends Commander.Command for better integration with the framework
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Command } from 'commander';
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import boxen from 'boxen';
|
|
||||||
import {
|
|
||||||
createTaskMasterCore,
|
|
||||||
type TaskMasterCore,
|
|
||||||
type TaskStatus
|
|
||||||
} from '@tm/core';
|
|
||||||
import type { StorageType } from '@tm/core/types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Valid task status values for validation
|
|
||||||
*/
|
|
||||||
const VALID_TASK_STATUSES: TaskStatus[] = [
|
|
||||||
'pending',
|
|
||||||
'in-progress',
|
|
||||||
'done',
|
|
||||||
'deferred',
|
|
||||||
'cancelled',
|
|
||||||
'blocked',
|
|
||||||
'review'
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options interface for the set-status command
|
|
||||||
*/
|
|
||||||
export interface SetStatusCommandOptions {
|
|
||||||
id?: string;
|
|
||||||
status?: TaskStatus;
|
|
||||||
format?: 'text' | 'json';
|
|
||||||
silent?: boolean;
|
|
||||||
project?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Result type from set-status command
|
|
||||||
*/
|
|
||||||
export interface SetStatusResult {
|
|
||||||
success: boolean;
|
|
||||||
updatedTasks: Array<{
|
|
||||||
taskId: string;
|
|
||||||
oldStatus: TaskStatus;
|
|
||||||
newStatus: TaskStatus;
|
|
||||||
}>;
|
|
||||||
storageType: Exclude<StorageType, 'auto'>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SetStatusCommand extending Commander's Command class
|
|
||||||
* This is a thin presentation layer over @tm/core
|
|
||||||
*/
|
|
||||||
export class SetStatusCommand extends Command {
|
|
||||||
private tmCore?: TaskMasterCore;
|
|
||||||
private lastResult?: SetStatusResult;
|
|
||||||
|
|
||||||
constructor(name?: string) {
|
|
||||||
super(name || 'set-status');
|
|
||||||
|
|
||||||
// Configure the command
|
|
||||||
this.description('Update the status of one or more tasks')
|
|
||||||
.requiredOption(
|
|
||||||
'-i, --id <id>',
|
|
||||||
'Task ID(s) to update (comma-separated for multiple, supports subtasks like 5.2)'
|
|
||||||
)
|
|
||||||
.requiredOption(
|
|
||||||
'-s, --status <status>',
|
|
||||||
`New status (${VALID_TASK_STATUSES.join(', ')})`
|
|
||||||
)
|
|
||||||
.option('-f, --format <format>', 'Output format (text, json)', 'text')
|
|
||||||
.option('--silent', 'Suppress output (useful for programmatic usage)')
|
|
||||||
.option('-p, --project <path>', 'Project root directory', process.cwd())
|
|
||||||
.action(async (options: SetStatusCommandOptions) => {
|
|
||||||
await this.executeCommand(options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the set-status command
|
|
||||||
*/
|
|
||||||
private async executeCommand(
|
|
||||||
options: SetStatusCommandOptions
|
|
||||||
): Promise<void> {
|
|
||||||
try {
|
|
||||||
// Validate required options
|
|
||||||
if (!options.id) {
|
|
||||||
console.error(chalk.red('Error: Task ID is required. Use -i or --id'));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.status) {
|
|
||||||
console.error(
|
|
||||||
chalk.red('Error: Status is required. Use -s or --status')
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate status
|
|
||||||
if (!VALID_TASK_STATUSES.includes(options.status)) {
|
|
||||||
console.error(
|
|
||||||
chalk.red(
|
|
||||||
`Error: Invalid status "${options.status}". Valid options: ${VALID_TASK_STATUSES.join(', ')}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize TaskMaster core
|
|
||||||
this.tmCore = await createTaskMasterCore({
|
|
||||||
projectPath: options.project || process.cwd()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Parse task IDs (handle comma-separated values)
|
|
||||||
const taskIds = options.id.split(',').map((id) => id.trim());
|
|
||||||
|
|
||||||
// Update each task
|
|
||||||
const updatedTasks: Array<{
|
|
||||||
taskId: string;
|
|
||||||
oldStatus: TaskStatus;
|
|
||||||
newStatus: TaskStatus;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
for (const taskId of taskIds) {
|
|
||||||
try {
|
|
||||||
const result = await this.tmCore.updateTaskStatus(
|
|
||||||
taskId,
|
|
||||||
options.status
|
|
||||||
);
|
|
||||||
updatedTasks.push({
|
|
||||||
taskId: result.taskId,
|
|
||||||
oldStatus: result.oldStatus,
|
|
||||||
newStatus: result.newStatus
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage =
|
|
||||||
error instanceof Error ? error.message : String(error);
|
|
||||||
|
|
||||||
if (!options.silent) {
|
|
||||||
console.error(
|
|
||||||
chalk.red(`Failed to update task ${taskId}: ${errorMessage}`)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (options.format === 'json') {
|
|
||||||
console.log(
|
|
||||||
JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
error: errorMessage,
|
|
||||||
taskId,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store result for potential reuse
|
|
||||||
this.lastResult = {
|
|
||||||
success: true,
|
|
||||||
updatedTasks,
|
|
||||||
storageType: this.tmCore.getStorageType() as Exclude<
|
|
||||||
StorageType,
|
|
||||||
'auto'
|
|
||||||
>
|
|
||||||
};
|
|
||||||
|
|
||||||
// Display results
|
|
||||||
this.displayResults(this.lastResult, options);
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage =
|
|
||||||
error instanceof Error ? error.message : 'Unknown error occurred';
|
|
||||||
|
|
||||||
if (!options.silent) {
|
|
||||||
console.error(chalk.red(`Error: ${errorMessage}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.format === 'json') {
|
|
||||||
console.log(JSON.stringify({ success: false, error: errorMessage }));
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
} finally {
|
|
||||||
// Clean up resources
|
|
||||||
if (this.tmCore) {
|
|
||||||
await this.tmCore.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display results based on format
|
|
||||||
*/
|
|
||||||
private displayResults(
|
|
||||||
result: SetStatusResult,
|
|
||||||
options: SetStatusCommandOptions
|
|
||||||
): void {
|
|
||||||
const format = options.format || 'text';
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
case 'json':
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'text':
|
|
||||||
default:
|
|
||||||
if (!options.silent) {
|
|
||||||
this.displayTextResults(result);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display results in text format
|
|
||||||
*/
|
|
||||||
private displayTextResults(result: SetStatusResult): void {
|
|
||||||
if (result.updatedTasks.length === 1) {
|
|
||||||
// Single task update
|
|
||||||
const update = result.updatedTasks[0];
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
chalk.white.bold(`✅ Successfully updated task ${update.taskId}`) +
|
|
||||||
'\n\n' +
|
|
||||||
`${chalk.blue('From:')} ${this.getStatusDisplay(update.oldStatus)}\n` +
|
|
||||||
`${chalk.blue('To:')} ${this.getStatusDisplay(update.newStatus)}`,
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'green',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 1 }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Multiple task updates
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
chalk.white.bold(
|
|
||||||
`✅ Successfully updated ${result.updatedTasks.length} tasks`
|
|
||||||
) +
|
|
||||||
'\n\n' +
|
|
||||||
result.updatedTasks
|
|
||||||
.map(
|
|
||||||
(update) =>
|
|
||||||
`${chalk.cyan(update.taskId)}: ${this.getStatusDisplay(update.oldStatus)} → ${this.getStatusDisplay(update.newStatus)}`
|
|
||||||
)
|
|
||||||
.join('\n'),
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'green',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 1 }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show storage info
|
|
||||||
console.log(chalk.gray(`\nUsing ${result.storageType} storage`));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get colored status display
|
|
||||||
*/
|
|
||||||
private getStatusDisplay(status: TaskStatus): string {
|
|
||||||
const statusColors: Record<TaskStatus, (text: string) => string> = {
|
|
||||||
pending: chalk.yellow,
|
|
||||||
'in-progress': chalk.blue,
|
|
||||||
done: chalk.green,
|
|
||||||
deferred: chalk.gray,
|
|
||||||
cancelled: chalk.red,
|
|
||||||
blocked: chalk.red,
|
|
||||||
review: chalk.magenta,
|
|
||||||
completed: chalk.green
|
|
||||||
};
|
|
||||||
|
|
||||||
const colorFn = statusColors[status] || chalk.white;
|
|
||||||
return colorFn(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the last command result (useful for testing or chaining)
|
|
||||||
*/
|
|
||||||
getLastResult(): SetStatusResult | undefined {
|
|
||||||
return this.lastResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Static method to register this command on an existing program
|
|
||||||
* This is for gradual migration - allows commands.js to use this
|
|
||||||
*/
|
|
||||||
static registerOn(program: Command): Command {
|
|
||||||
const setStatusCommand = new SetStatusCommand();
|
|
||||||
program.addCommand(setStatusCommand);
|
|
||||||
return setStatusCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alternative registration that returns the command for chaining
|
|
||||||
* Can also configure the command name if needed
|
|
||||||
*/
|
|
||||||
static register(program: Command, name?: string): SetStatusCommand {
|
|
||||||
const setStatusCommand = new SetStatusCommand(name);
|
|
||||||
program.addCommand(setStatusCommand);
|
|
||||||
return setStatusCommand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory function to create and configure the set-status command
|
|
||||||
*/
|
|
||||||
export function createSetStatusCommand(): SetStatusCommand {
|
|
||||||
return new SetStatusCommand();
|
|
||||||
}
|
|
||||||
@@ -8,18 +8,10 @@ export { ListTasksCommand } from './commands/list.command.js';
|
|||||||
export { ShowCommand } from './commands/show.command.js';
|
export { ShowCommand } from './commands/show.command.js';
|
||||||
export { AuthCommand } from './commands/auth.command.js';
|
export { AuthCommand } from './commands/auth.command.js';
|
||||||
export { ContextCommand } from './commands/context.command.js';
|
export { ContextCommand } from './commands/context.command.js';
|
||||||
export { SetStatusCommand } from './commands/set-status.command.js';
|
|
||||||
|
|
||||||
// UI utilities (for other commands to use)
|
// UI utilities (for other commands to use)
|
||||||
export * as ui from './utils/ui.js';
|
export * as ui from './utils/ui.js';
|
||||||
|
|
||||||
// Auto-update utilities
|
|
||||||
export {
|
|
||||||
checkForUpdate,
|
|
||||||
performAutoUpdate,
|
|
||||||
displayUpgradeNotification
|
|
||||||
} from './utils/auto-update.js';
|
|
||||||
|
|
||||||
// Re-export commonly used types from tm-core
|
// Re-export commonly used types from tm-core
|
||||||
export type {
|
export type {
|
||||||
Task,
|
Task,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import figlet from 'figlet';
|
||||||
|
import gradient from 'gradient-string';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Header configuration options
|
* Header configuration options
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Auto-update utilities for task-master-ai CLI
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { spawn } from 'child_process';
|
|
||||||
import https from 'https';
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import ora from 'ora';
|
|
||||||
import boxen from 'boxen';
|
|
||||||
import packageJson from '../../../../package.json' with { type: 'json' };
|
|
||||||
|
|
||||||
export interface UpdateInfo {
|
|
||||||
currentVersion: string;
|
|
||||||
latestVersion: string;
|
|
||||||
needsUpdate: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current version from package.json
|
|
||||||
*/
|
|
||||||
function getCurrentVersion(): string {
|
|
||||||
try {
|
|
||||||
return packageJson.version;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Could not read package.json for version info');
|
|
||||||
return '0.0.0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare semantic versions with proper pre-release handling
|
|
||||||
* @param v1 - First version
|
|
||||||
* @param v2 - Second version
|
|
||||||
* @returns -1 if v1 < v2, 0 if v1 = v2, 1 if v1 > v2
|
|
||||||
*/
|
|
||||||
function compareVersions(v1: string, v2: string): number {
|
|
||||||
const toParts = (v: string) => {
|
|
||||||
const [core, pre = ''] = v.split('-', 2);
|
|
||||||
const nums = core.split('.').map((n) => Number.parseInt(n, 10) || 0);
|
|
||||||
return { nums, pre };
|
|
||||||
};
|
|
||||||
|
|
||||||
const a = toParts(v1);
|
|
||||||
const b = toParts(v2);
|
|
||||||
const len = Math.max(a.nums.length, b.nums.length);
|
|
||||||
|
|
||||||
// Compare numeric parts
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
const d = (a.nums[i] || 0) - (b.nums[i] || 0);
|
|
||||||
if (d !== 0) return d < 0 ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle pre-release comparison
|
|
||||||
if (a.pre && !b.pre) return -1; // prerelease < release
|
|
||||||
if (!a.pre && b.pre) return 1; // release > prerelease
|
|
||||||
if (a.pre === b.pre) return 0; // same or both empty
|
|
||||||
return a.pre < b.pre ? -1 : 1; // basic prerelease tie-break
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for newer version of task-master-ai
|
|
||||||
*/
|
|
||||||
export async function checkForUpdate(
|
|
||||||
currentVersionOverride?: string
|
|
||||||
): Promise<UpdateInfo> {
|
|
||||||
const currentVersion = currentVersionOverride || getCurrentVersion();
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const options = {
|
|
||||||
hostname: 'registry.npmjs.org',
|
|
||||||
path: '/task-master-ai',
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/vnd.npm.install-v1+json',
|
|
||||||
'User-Agent': `task-master-ai/${currentVersion}`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const req = https.request(options, (res) => {
|
|
||||||
let data = '';
|
|
||||||
|
|
||||||
res.on('data', (chunk) => {
|
|
||||||
data += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
res.on('end', () => {
|
|
||||||
try {
|
|
||||||
if (res.statusCode !== 200)
|
|
||||||
throw new Error(`npm registry status ${res.statusCode}`);
|
|
||||||
const npmData = JSON.parse(data);
|
|
||||||
const latestVersion = npmData['dist-tags']?.latest || currentVersion;
|
|
||||||
|
|
||||||
const needsUpdate =
|
|
||||||
compareVersions(currentVersion, latestVersion) < 0;
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
currentVersion,
|
|
||||||
latestVersion,
|
|
||||||
needsUpdate
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
resolve({
|
|
||||||
currentVersion,
|
|
||||||
latestVersion: currentVersion,
|
|
||||||
needsUpdate: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
req.on('error', () => {
|
|
||||||
resolve({
|
|
||||||
currentVersion,
|
|
||||||
latestVersion: currentVersion,
|
|
||||||
needsUpdate: false
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
req.setTimeout(3000, () => {
|
|
||||||
req.destroy();
|
|
||||||
resolve({
|
|
||||||
currentVersion,
|
|
||||||
latestVersion: currentVersion,
|
|
||||||
needsUpdate: false
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
req.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display upgrade notification message
|
|
||||||
*/
|
|
||||||
export function displayUpgradeNotification(
|
|
||||||
currentVersion: string,
|
|
||||||
latestVersion: string
|
|
||||||
) {
|
|
||||||
const message = boxen(
|
|
||||||
`${chalk.blue.bold('Update Available!')} ${chalk.dim(currentVersion)} → ${chalk.green(latestVersion)}\n\n` +
|
|
||||||
`Auto-updating to the latest version with new features and bug fixes...`,
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
margin: { top: 1, bottom: 1 },
|
|
||||||
borderColor: 'yellow',
|
|
||||||
borderStyle: 'round'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically update task-master-ai to the latest version
|
|
||||||
*/
|
|
||||||
export async function performAutoUpdate(
|
|
||||||
latestVersion: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
if (process.env.TASKMASTER_SKIP_AUTO_UPDATE === '1' || process.env.CI) {
|
|
||||||
console.log(
|
|
||||||
chalk.dim('Skipping auto-update (TASKMASTER_SKIP_AUTO_UPDATE/CI).')
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const spinner = ora({
|
|
||||||
text: chalk.blue(
|
|
||||||
`Updating task-master-ai to version ${chalk.green(latestVersion)}`
|
|
||||||
),
|
|
||||||
spinner: 'dots',
|
|
||||||
color: 'blue'
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const updateProcess = spawn(
|
|
||||||
'npm',
|
|
||||||
[
|
|
||||||
'install',
|
|
||||||
'-g',
|
|
||||||
`task-master-ai@${latestVersion}`,
|
|
||||||
'--no-fund',
|
|
||||||
'--no-audit',
|
|
||||||
'--loglevel=warn'
|
|
||||||
],
|
|
||||||
{
|
|
||||||
stdio: ['ignore', 'pipe', 'pipe']
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let errorOutput = '';
|
|
||||||
|
|
||||||
updateProcess.stdout.on('data', () => {
|
|
||||||
// Update spinner text with progress
|
|
||||||
spinner.text = chalk.blue(
|
|
||||||
`Installing task-master-ai@${latestVersion}...`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
updateProcess.stderr.on('data', (data) => {
|
|
||||||
errorOutput += data.toString();
|
|
||||||
});
|
|
||||||
|
|
||||||
updateProcess.on('close', (code) => {
|
|
||||||
if (code === 0) {
|
|
||||||
spinner.succeed(
|
|
||||||
chalk.green(
|
|
||||||
`Successfully updated to version ${chalk.bold(latestVersion)}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
chalk.dim('Please restart your command to use the new version.')
|
|
||||||
);
|
|
||||||
resolve(true);
|
|
||||||
} else {
|
|
||||||
spinner.fail(chalk.red('Auto-update failed'));
|
|
||||||
console.log(
|
|
||||||
chalk.cyan(
|
|
||||||
`Please run manually: npm install -g task-master-ai@${latestVersion}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if (errorOutput) {
|
|
||||||
console.log(chalk.dim(`Error: ${errorOutput.trim()}`));
|
|
||||||
}
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
updateProcess.on('error', (error) => {
|
|
||||||
spinner.fail(chalk.red('Auto-update failed'));
|
|
||||||
console.log(chalk.red('Error:'), error.message);
|
|
||||||
console.log(
|
|
||||||
chalk.cyan(
|
|
||||||
`Please run manually: npm install -g task-master-ai@${latestVersion}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
resolve(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -50,11 +50,6 @@ export function getStatusWithColor(
|
|||||||
color: chalk.red,
|
color: chalk.red,
|
||||||
icon: '!',
|
icon: '!',
|
||||||
tableIcon: '!'
|
tableIcon: '!'
|
||||||
},
|
|
||||||
completed: {
|
|
||||||
color: chalk.green,
|
|
||||||
icon: '✓',
|
|
||||||
tableIcon: '✓'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,27 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "NodeNext",
|
"module": "ESNext",
|
||||||
"lib": ["ES2022"],
|
"lib": ["ES2022"],
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"baseUrl": ".",
|
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"strict": true,
|
"resolveJsonModule": true,
|
||||||
"noImplicitAny": true,
|
"allowJs": false,
|
||||||
"strictNullChecks": true,
|
|
||||||
"strictFunctionTypes": true,
|
|
||||||
"strictBindCallApply": true,
|
|
||||||
"strictPropertyInitialization": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"alwaysStrict": true,
|
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"esModuleInterop": true,
|
"types": ["node"]
|
||||||
"skipLibCheck": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"moduleResolution": "NodeNext",
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"types": ["node"],
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"allowImportingTsExtensions": false
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"]
|
"exclude": ["node_modules", "dist", "tests"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# docs
|
# docs
|
||||||
|
|
||||||
## 0.0.2
|
|
||||||
|
|
||||||
## 0.0.1
|
## 0.0.1
|
||||||
|
|||||||
@@ -83,8 +83,6 @@ Taskmaster uses two primary methods for configuration:
|
|||||||
- `VERTEX_PROJECT_ID`: Your Google Cloud project ID for Vertex AI. Required when using the 'vertex' provider.
|
- `VERTEX_PROJECT_ID`: Your Google Cloud project ID for Vertex AI. Required when using the 'vertex' provider.
|
||||||
- `VERTEX_LOCATION`: Google Cloud region for Vertex AI (e.g., 'us-central1'). Default is 'us-central1'.
|
- `VERTEX_LOCATION`: Google Cloud region for Vertex AI (e.g., 'us-central1'). Default is 'us-central1'.
|
||||||
- `GOOGLE_APPLICATION_CREDENTIALS`: Path to service account credentials JSON file for Google Cloud auth (alternative to API key for Vertex AI).
|
- `GOOGLE_APPLICATION_CREDENTIALS`: Path to service account credentials JSON file for Google Cloud auth (alternative to API key for Vertex AI).
|
||||||
- **Optional Auto-Update Control:**
|
|
||||||
- `TASKMASTER_SKIP_AUTO_UPDATE`: Set to '1' to disable automatic updates. Also automatically disabled in CI environments (when `CI` environment variable is set).
|
|
||||||
|
|
||||||
**Important:** Settings like model ID selections (`main`, `research`, `fallback`), `maxTokens`, `temperature`, `logLevel`, `defaultSubtasks`, `defaultPriority`, and `projectName` are **managed in `.taskmaster/config.json`** (or `.taskmasterconfig` for unmigrated projects), not environment variables.
|
**Important:** Settings like model ID selections (`main`, `research`, `fallback`), `maxTokens`, `temperature`, `logLevel`, `defaultSubtasks`, `defaultPriority`, and `projectName` are **managed in `.taskmaster/config.json`** (or `.taskmasterconfig` for unmigrated projects), not environment variables.
|
||||||
|
|
||||||
|
|||||||
@@ -17,18 +17,37 @@ sidebarTitle: "CLI Commands"
|
|||||||
|
|
||||||
<Accordion title="List Tasks">
|
<Accordion title="List Tasks">
|
||||||
```bash
|
```bash
|
||||||
# List all tasks
|
# List all tasks with enhanced dashboard view
|
||||||
task-master list
|
task-master list
|
||||||
|
# Alias available
|
||||||
|
task-master ls
|
||||||
|
|
||||||
# List tasks with a specific status
|
# List tasks with a specific status (comma-separated multiple statuses supported)
|
||||||
task-master list --status=<status>
|
task-master list --status=<status>
|
||||||
|
task-master list --status=pending,in-progress
|
||||||
|
|
||||||
# List tasks with subtasks
|
# List tasks with subtasks
|
||||||
task-master list --with-subtasks
|
task-master list --with-subtasks
|
||||||
|
|
||||||
# List tasks with a specific status and include subtasks
|
# Filter by tag
|
||||||
task-master list --status=<status> --with-subtasks
|
task-master list --tag=<tag>
|
||||||
|
|
||||||
|
# Output formats: text (default), json, compact
|
||||||
|
task-master list --format=json
|
||||||
|
task-master list --format=compact
|
||||||
|
|
||||||
|
# Suppress output for programmatic use
|
||||||
|
task-master list --silent
|
||||||
|
|
||||||
|
# Specify project directory
|
||||||
|
task-master list --project=/path/to/project
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `list` command now displays:
|
||||||
|
- **Project dashboard** with task statistics and progress bars
|
||||||
|
- **Next recommended task** based on dependencies and priority
|
||||||
|
- **Suggested next steps** for project workflow
|
||||||
|
- **Enhanced table view** with complexity scores and color-coded status
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Show Next Task">
|
<Accordion title="Show Next Task">
|
||||||
@@ -45,9 +64,32 @@ sidebarTitle: "CLI Commands"
|
|||||||
# or
|
# or
|
||||||
task-master show --id=<id>
|
task-master show --id=<id>
|
||||||
|
|
||||||
|
# View multiple tasks at once (comma-separated)
|
||||||
|
task-master show 1,2,3
|
||||||
|
task-master show --id=1,2,3
|
||||||
|
|
||||||
# View a specific subtask (e.g., subtask 2 of task 1)
|
# View a specific subtask (e.g., subtask 2 of task 1)
|
||||||
task-master show 1.2
|
task-master show 1.2
|
||||||
|
|
||||||
|
# Filter subtasks by status
|
||||||
|
task-master show <id> --status=<status>
|
||||||
|
|
||||||
|
# Output formats: text (default), json
|
||||||
|
task-master show <id> --format=json
|
||||||
|
|
||||||
|
# Suppress output for programmatic use
|
||||||
|
task-master show <id> --silent
|
||||||
|
|
||||||
|
# Specify project directory
|
||||||
|
task-master show <id> --project=/path/to/project
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `show` command now features:
|
||||||
|
- **Enhanced task details** with markdown rendering for descriptions
|
||||||
|
- **Structured information display** with tables and sections
|
||||||
|
- **Multiple task support** for viewing several tasks simultaneously
|
||||||
|
- **Subtask filtering** by status
|
||||||
|
- **Suggested actions** for next steps on the task
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Update Tasks">
|
<Accordion title="Update Tasks">
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ To see all tasks in the CLI you can use:
|
|||||||
task-master list
|
task-master list
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `list` command now includes a comprehensive dashboard showing:
|
||||||
|
- Project statistics with completion progress bars
|
||||||
|
- Next recommended task based on dependencies and priority
|
||||||
|
- Suggested workflow steps
|
||||||
|
- Enhanced task table with complexity indicators
|
||||||
|
|
||||||
To see all implementation details of an individual task, including subtasks and testing strategy, you can use Show Task:
|
To see all implementation details of an individual task, including subtasks and testing strategy, you can use Show Task:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docs",
|
"name": "docs",
|
||||||
"version": "0.0.2",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Task Master documentation powered by Mintlify",
|
"description": "Task Master documentation powered by Mintlify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,23 +1,5 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
## 0.25.0-rc.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- [#1201](https://github.com/eyaltoledano/claude-task-master/pull/1201) [`83af314`](https://github.com/eyaltoledano/claude-task-master/commit/83af314879fc0e563581161c60d2bd089899313e) Thanks [@losolosol](https://github.com/losolosol)! - Added a Start Build button to the VSCODE Task Properties Right Panel
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [[`137ef36`](https://github.com/eyaltoledano/claude-task-master/commit/137ef362789a9cdfdb1925e35e0438c1fa6c69ee)]:
|
|
||||||
- task-master-ai@0.27.0-rc.0
|
|
||||||
|
|
||||||
## 0.24.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [[`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`df26c65`](https://github.com/eyaltoledano/claude-task-master/commit/df26c65632000874a73504963b08f18c46283144), [`37af0f1`](https://github.com/eyaltoledano/claude-task-master/commit/37af0f191227a68d119b7f89a377bf932ee3ac66), [`c4f92f6`](https://github.com/eyaltoledano/claude-task-master/commit/c4f92f6a0aee3435c56eb8d27d9aa9204284833e), [`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`4dad2fd`](https://github.com/eyaltoledano/claude-task-master/commit/4dad2fd613ceac56a65ae9d3c1c03092b8860ac9)]:
|
|
||||||
- task-master-ai@0.26.0
|
|
||||||
|
|
||||||
## 0.24.2-rc.1
|
## 0.24.2-rc.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"displayName": "TaskMaster",
|
"displayName": "TaskMaster",
|
||||||
"description": "A visual Kanban board interface for TaskMaster projects in VS Code",
|
"description": "A visual Kanban board interface for TaskMaster projects in VS Code",
|
||||||
"version": "0.25.0-rc.0",
|
"version": "0.24.2-rc.1",
|
||||||
"publisher": "Hamster",
|
"publisher": "Hamster",
|
||||||
"icon": "assets/icon.png",
|
"icon": "assets/icon.png",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -240,7 +240,7 @@
|
|||||||
"check-types": "tsc --noEmit"
|
"check-types": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"task-master-ai": "*"
|
"task-master-ai": "0.26.0-rc.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
|
|||||||
@@ -18,17 +18,7 @@ export default {
|
|||||||
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
|
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
|
||||||
|
|
||||||
// Transform files
|
// Transform files
|
||||||
preset: 'ts-jest/presets/default-esm',
|
transform: {},
|
||||||
extensionsToTreatAsEsm: ['.ts'],
|
|
||||||
moduleFileExtensions: ['js', 'ts', 'json', 'node'],
|
|
||||||
transform: {
|
|
||||||
'^.+\\.ts$': [
|
|
||||||
'ts-jest',
|
|
||||||
{
|
|
||||||
useESM: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Disable transformations for node_modules
|
// Disable transformations for node_modules
|
||||||
transformIgnorePatterns: ['/node_modules/'],
|
transformIgnorePatterns: ['/node_modules/'],
|
||||||
@@ -37,7 +27,6 @@ export default {
|
|||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^@/(.*)$': '<rootDir>/$1'
|
'^@/(.*)$': '<rootDir>/$1'
|
||||||
},
|
},
|
||||||
resolver: '<rootDir>/jest.resolver.cjs',
|
|
||||||
|
|
||||||
// Setup module aliases
|
// Setup module aliases
|
||||||
moduleDirectories: ['node_modules', '<rootDir>'],
|
moduleDirectories: ['node_modules', '<rootDir>'],
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
const { defaultResolver } = require('jest-resolve');
|
|
||||||
module.exports = function customResolver(request, options) {
|
|
||||||
const resolve = options.defaultResolver || defaultResolver;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return resolve(request, options);
|
|
||||||
} catch (error) {
|
|
||||||
if (request.startsWith('.') && request.endsWith('.js')) {
|
|
||||||
try {
|
|
||||||
return resolve(request.replace(/\.js$/, '.ts'), options);
|
|
||||||
} catch (tsError) {
|
|
||||||
tsError.cause = tsError.cause ?? error;
|
|
||||||
throw tsError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
69
output.txt
Normal file
69
output.txt
Normal file
File diff suppressed because one or more lines are too long
2484
package-lock.json
generated
2484
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "task-master-ai",
|
"name": "task-master-ai",
|
||||||
"version": "0.27.0-rc.1",
|
"version": "0.26.0-rc.1",
|
||||||
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
|
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -11,12 +11,20 @@
|
|||||||
},
|
},
|
||||||
"workspaces": ["apps/*", "packages/*", "."],
|
"workspaces": ["apps/*", "packages/*", "."],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:build-config && cross-env NODE_ENV=production tsdown",
|
"build": "npm run build:build-config && tsup",
|
||||||
"dev": "tsdown --watch",
|
"dev": "tsup --watch='packages/*/src/**/*' --watch='apps/cli/src/**/*' --watch='bin/**/*' --watch='mcp-server/**/*'",
|
||||||
"turbo:dev": "turbo dev",
|
"turbo:dev": "turbo dev",
|
||||||
"turbo:build": "turbo build",
|
"turbo:build": "turbo build",
|
||||||
"turbo:typecheck": "turbo typecheck",
|
"turbo:typecheck": "turbo typecheck",
|
||||||
|
"dev:main": "tsup --watch --onSuccess 'echo \"📦 Main package built\" && npm link'",
|
||||||
|
"dev:legacy": "npm run build:build-config && concurrently -n \"core,cli,main\" -c \"blue,green,yellow\" \"npm run dev:core\" \"npm run dev:cli\" \"npm run dev:main\"",
|
||||||
|
"dev:core": "npm run dev -w @tm/core",
|
||||||
|
"dev:cli": "npm run dev -w @tm/cli",
|
||||||
|
"build:packages": "turbo build --filter='./packages/*' --filter='./apps/*'",
|
||||||
|
"build:packages:parallel": "turbo build --filter='./packages/*' --filter='./apps/*'",
|
||||||
"build:build-config": "npm run build -w @tm/build-config",
|
"build:build-config": "npm run build -w @tm/build-config",
|
||||||
|
"build:core": "npm run build -w @tm/core",
|
||||||
|
"build:cli": "npm run build -w @tm/cli",
|
||||||
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
||||||
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit",
|
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit",
|
||||||
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration",
|
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration",
|
||||||
@@ -65,6 +73,7 @@
|
|||||||
"@inquirer/search": "^3.0.15",
|
"@inquirer/search": "^3.0.15",
|
||||||
"@openrouter/ai-sdk-provider": "^0.4.5",
|
"@openrouter/ai-sdk-provider": "^0.4.5",
|
||||||
"@streamparser/json": "^0.0.22",
|
"@streamparser/json": "^0.0.22",
|
||||||
|
"@tm/cli": "*",
|
||||||
"ai": "^4.3.10",
|
"ai": "^4.3.10",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"ajv-formats": "^3.0.1",
|
"ajv-formats": "^3.0.1",
|
||||||
@@ -120,7 +129,6 @@
|
|||||||
"whatwg-url": "^11.0.0"
|
"whatwg-url": "^11.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tm/cli": "*",
|
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@changesets/changelog-github": "^0.5.1",
|
"@changesets/changelog-github": "^0.5.1",
|
||||||
"@changesets/cli": "^2.28.1",
|
"@changesets/cli": "^2.28.1",
|
||||||
@@ -135,8 +143,7 @@
|
|||||||
"mock-fs": "^5.5.0",
|
"mock-fs": "^5.5.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"supertest": "^7.1.0",
|
"supertest": "^7.1.0",
|
||||||
"ts-jest": "^29.4.2",
|
"tsup": "^8.5.0",
|
||||||
"tsdown": "^0.15.2",
|
|
||||||
"tsx": "^4.16.2",
|
"tsx": "^4.16.2",
|
||||||
"turbo": "^2.5.6",
|
"turbo": "^2.5.6",
|
||||||
"typescript": "^5.9.2"
|
"typescript": "^5.9.2"
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Shared build configuration for Task Master monorepo",
|
"description": "Shared build configuration for Task Master monorepo",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"main": "./dist/tsup.base.js",
|
||||||
"main": "./dist/tsdown.base.js",
|
"types": "./dist/tsup.base.d.ts",
|
||||||
"types": "./src/tsdown.base.ts",
|
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./src/tsdown.base.ts",
|
"types": "./dist/tsup.base.d.ts",
|
||||||
"import": "./dist/tsdown.base.js"
|
"import": "./dist/tsup.base.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": ["dist", "src"],
|
"files": ["dist", "src"],
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Base tsdown configuration for Task Master monorepo
|
|
||||||
* Provides shared configuration that can be extended by individual packages
|
|
||||||
*/
|
|
||||||
import type { UserConfig } from 'tsdown';
|
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
|
||||||
const isDevelopment = !isProduction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Environment helpers
|
|
||||||
*/
|
|
||||||
export const env = {
|
|
||||||
isProduction,
|
|
||||||
isDevelopment,
|
|
||||||
NODE_ENV: process.env.NODE_ENV || 'development'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base tsdown configuration for all packages
|
|
||||||
* Since everything gets bundled into root dist/ anyway, use consistent settings
|
|
||||||
*/
|
|
||||||
export const baseConfig: Partial<UserConfig> = {
|
|
||||||
sourcemap: isDevelopment,
|
|
||||||
format: 'esm',
|
|
||||||
platform: 'node',
|
|
||||||
dts: isDevelopment,
|
|
||||||
minify: isProduction,
|
|
||||||
treeshake: isProduction,
|
|
||||||
// Keep all npm dependencies external (available via node_modules)
|
|
||||||
external: [/^[^@./]/, /^@(?!tm\/)/]
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility function to merge configurations
|
|
||||||
* Simplified for tsdown usage
|
|
||||||
*/
|
|
||||||
export function mergeConfig(
|
|
||||||
base: Partial<UserConfig>,
|
|
||||||
overrides: Partial<UserConfig>
|
|
||||||
): Partial<UserConfig> {
|
|
||||||
return {
|
|
||||||
...base,
|
|
||||||
...overrides
|
|
||||||
};
|
|
||||||
}
|
|
||||||
101
packages/build-config/src/tsup.base.ts
Normal file
101
packages/build-config/src/tsup.base.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* Base tsup configuration for Task Master monorepo
|
||||||
|
* Provides shared configuration that can be extended by individual packages
|
||||||
|
*/
|
||||||
|
import type { Options } from 'tsup';
|
||||||
|
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isDevelopment = !isProduction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment helpers
|
||||||
|
*/
|
||||||
|
export const env = {
|
||||||
|
isProduction,
|
||||||
|
isDevelopment,
|
||||||
|
NODE_ENV: process.env.NODE_ENV || 'development'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base tsup configuration for all packages
|
||||||
|
* Since everything gets bundled into root dist/ anyway, use consistent settings
|
||||||
|
*/
|
||||||
|
export const baseConfig: Partial<Options> = {
|
||||||
|
format: ['esm'],
|
||||||
|
target: 'node18',
|
||||||
|
sourcemap: isDevelopment,
|
||||||
|
clean: true,
|
||||||
|
dts: false,
|
||||||
|
minify: isProduction,
|
||||||
|
treeshake: isProduction,
|
||||||
|
splitting: false,
|
||||||
|
// Don't bundle any other dependencies (auto-external all node_modules)
|
||||||
|
external: [/^[^./]/],
|
||||||
|
esbuildOptions(options) {
|
||||||
|
options.platform = 'node';
|
||||||
|
// Allow importing TypeScript from JavaScript
|
||||||
|
options.resolveExtensions = ['.ts', '.js', '.mjs', '.json'];
|
||||||
|
// Better source mapping in development only
|
||||||
|
options.sourcesContent = isDevelopment;
|
||||||
|
// Keep original names for better debugging in development
|
||||||
|
options.keepNames = isDevelopment;
|
||||||
|
},
|
||||||
|
// Watch mode configuration for development
|
||||||
|
watch: false
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy external modules list - kept for backwards compatibility
|
||||||
|
* Note: When using tsup-node, this is not needed as it automatically
|
||||||
|
* excludes dependencies and peerDependencies from package.json
|
||||||
|
*/
|
||||||
|
export const commonExternals = [
|
||||||
|
// Native Node.js modules (for cases where tsup is used instead of tsup-node)
|
||||||
|
'fs',
|
||||||
|
'path',
|
||||||
|
'child_process',
|
||||||
|
'crypto',
|
||||||
|
'os',
|
||||||
|
'url',
|
||||||
|
'util',
|
||||||
|
'stream',
|
||||||
|
'http',
|
||||||
|
'https',
|
||||||
|
'events',
|
||||||
|
'assert',
|
||||||
|
'buffer',
|
||||||
|
'querystring',
|
||||||
|
'readline',
|
||||||
|
'zlib',
|
||||||
|
'tty',
|
||||||
|
'net',
|
||||||
|
'dgram',
|
||||||
|
'dns',
|
||||||
|
'tls',
|
||||||
|
'cluster',
|
||||||
|
'process',
|
||||||
|
'module'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function to merge configurations
|
||||||
|
* Simplified for tsup-node usage
|
||||||
|
*/
|
||||||
|
export function mergeConfig(
|
||||||
|
baseConfig: Partial<Options>,
|
||||||
|
overrides: Partial<Options>
|
||||||
|
): Options {
|
||||||
|
return {
|
||||||
|
...baseConfig,
|
||||||
|
...overrides,
|
||||||
|
// Merge esbuildOptions
|
||||||
|
esbuildOptions(options, context) {
|
||||||
|
if (baseConfig.esbuildOptions) {
|
||||||
|
baseConfig.esbuildOptions(options, context);
|
||||||
|
}
|
||||||
|
if (overrides.esbuildOptions) {
|
||||||
|
overrides.esbuildOptions(options, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as Options;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tm/core",
|
"name": "@tm/core",
|
||||||
"version": "0.26.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Core library for Task Master - TypeScript task management system",
|
"description": "Core library for Task Master - TypeScript task management system",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ export type OutputFormat = (typeof OUTPUT_FORMATS)[number];
|
|||||||
*/
|
*/
|
||||||
export const STATUS_ICONS: Record<TaskStatus, string> = {
|
export const STATUS_ICONS: Record<TaskStatus, string> = {
|
||||||
done: '✓',
|
done: '✓',
|
||||||
completed: '✓',
|
|
||||||
'in-progress': '►',
|
'in-progress': '►',
|
||||||
blocked: '⭕',
|
blocked: '⭕',
|
||||||
pending: '○',
|
pending: '○',
|
||||||
@@ -72,6 +71,5 @@ export const STATUS_COLORS: Record<TaskStatus, string> = {
|
|||||||
deferred: 'gray',
|
deferred: 'gray',
|
||||||
cancelled: 'red',
|
cancelled: 'red',
|
||||||
blocked: 'magenta',
|
blocked: 'magenta',
|
||||||
review: 'cyan',
|
review: 'cyan'
|
||||||
completed: 'green'
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -17,14 +17,6 @@ export interface IStorage {
|
|||||||
*/
|
*/
|
||||||
loadTasks(tag?: string): Promise<Task[]>;
|
loadTasks(tag?: string): Promise<Task[]>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a single task by ID
|
|
||||||
* @param taskId - ID of the task to load
|
|
||||||
* @param tag - Optional tag context for the task
|
|
||||||
* @returns Promise that resolves to the task or null if not found
|
|
||||||
*/
|
|
||||||
loadTask(taskId: string, tag?: string): Promise<Task | null>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save tasks to storage, replacing existing tasks
|
* Save tasks to storage, replacing existing tasks
|
||||||
* @param tasks - Array of tasks to save
|
* @param tasks - Array of tasks to save
|
||||||
@@ -183,7 +175,6 @@ export abstract class BaseStorage implements IStorage {
|
|||||||
|
|
||||||
// Abstract methods that must be implemented by concrete classes
|
// Abstract methods that must be implemented by concrete classes
|
||||||
abstract loadTasks(tag?: string): Promise<Task[]>;
|
abstract loadTasks(tag?: string): Promise<Task[]>;
|
||||||
abstract loadTask(taskId: string, tag?: string): Promise<Task | null>;
|
|
||||||
abstract saveTasks(tasks: Task[], tag?: string): Promise<void>;
|
abstract saveTasks(tasks: Task[], tag?: string): Promise<void>;
|
||||||
abstract appendTasks(tasks: Task[], tag?: string): Promise<void>;
|
abstract appendTasks(tasks: Task[], tag?: string): Promise<void>;
|
||||||
abstract updateTask(
|
abstract updateTask(
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export class TaskMapper {
|
|||||||
/**
|
/**
|
||||||
* Maps database status to internal status
|
* Maps database status to internal status
|
||||||
*/
|
*/
|
||||||
static mapStatus(
|
private static mapStatus(
|
||||||
status: Database['public']['Enums']['task_status']
|
status: Database['public']['Enums']['task_status']
|
||||||
): Task['status'] {
|
): Task['status'] {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
|||||||
@@ -3,30 +3,6 @@ import { Task } from '../types/index.js';
|
|||||||
import { Database } from '../types/database.types.js';
|
import { Database } from '../types/database.types.js';
|
||||||
import { TaskMapper } from '../mappers/TaskMapper.js';
|
import { TaskMapper } from '../mappers/TaskMapper.js';
|
||||||
import { AuthManager } from '../auth/auth-manager.js';
|
import { AuthManager } from '../auth/auth-manager.js';
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
// Zod schema for task status validation
|
|
||||||
const TaskStatusSchema = z.enum([
|
|
||||||
'pending',
|
|
||||||
'in-progress',
|
|
||||||
'done',
|
|
||||||
'review',
|
|
||||||
'deferred',
|
|
||||||
'cancelled',
|
|
||||||
'blocked'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Zod schema for task updates
|
|
||||||
const TaskUpdateSchema = z
|
|
||||||
.object({
|
|
||||||
title: z.string().min(1).optional(),
|
|
||||||
description: z.string().optional(),
|
|
||||||
status: TaskStatusSchema.optional(),
|
|
||||||
priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
|
|
||||||
details: z.string().optional(),
|
|
||||||
testStrategy: z.string().optional()
|
|
||||||
})
|
|
||||||
.partial();
|
|
||||||
|
|
||||||
export class SupabaseTaskRepository {
|
export class SupabaseTaskRepository {
|
||||||
constructor(private supabase: SupabaseClient<Database>) {}
|
constructor(private supabase: SupabaseClient<Database>) {}
|
||||||
@@ -84,22 +60,12 @@ export class SupabaseTaskRepository {
|
|||||||
return TaskMapper.mapDatabaseTasksToTasks(tasks, depsData || []);
|
return TaskMapper.mapDatabaseTasksToTasks(tasks, depsData || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTask(_projectId: string, taskId: string): Promise<Task | null> {
|
async getTask(accountId: string, taskId: string): Promise<Task | null> {
|
||||||
// Get the current context to determine briefId (projectId not used in Supabase context)
|
|
||||||
const authManager = AuthManager.getInstance();
|
|
||||||
const context = authManager.getContext();
|
|
||||||
|
|
||||||
if (!context || !context.briefId) {
|
|
||||||
throw new Error(
|
|
||||||
'No brief selected. Please select a brief first using: tm context brief'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data, error } = await this.supabase
|
const { data, error } = await this.supabase
|
||||||
.from('tasks')
|
.from('tasks')
|
||||||
.select('*')
|
.select('*')
|
||||||
.eq('brief_id', context.briefId)
|
.eq('account_id', accountId)
|
||||||
.eq('display_id', taskId.toUpperCase())
|
.eq('id', taskId)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -141,84 +107,4 @@ export class SupabaseTaskRepository {
|
|||||||
dependenciesByTaskId
|
dependenciesByTaskId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTask(
|
|
||||||
projectId: string,
|
|
||||||
taskId: string,
|
|
||||||
updates: Partial<Task>
|
|
||||||
): Promise<Task> {
|
|
||||||
// Get the current context to determine briefId
|
|
||||||
const authManager = AuthManager.getInstance();
|
|
||||||
const context = authManager.getContext();
|
|
||||||
|
|
||||||
if (!context || !context.briefId) {
|
|
||||||
throw new Error(
|
|
||||||
'No brief selected. Please select a brief first using: tm context brief'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate updates using Zod schema
|
|
||||||
try {
|
|
||||||
TaskUpdateSchema.parse(updates);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof z.ZodError) {
|
|
||||||
const errorMessages = error.errors
|
|
||||||
.map((err) => `${err.path.join('.')}: ${err.message}`)
|
|
||||||
.join(', ');
|
|
||||||
throw new Error(`Invalid task update data: ${errorMessages}`);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert Task fields to database fields - only include fields that actually exist in the database
|
|
||||||
const dbUpdates: any = {};
|
|
||||||
|
|
||||||
if (updates.title !== undefined) dbUpdates.title = updates.title;
|
|
||||||
if (updates.description !== undefined)
|
|
||||||
dbUpdates.description = updates.description;
|
|
||||||
if (updates.status !== undefined)
|
|
||||||
dbUpdates.status = this.mapStatusToDatabase(updates.status);
|
|
||||||
if (updates.priority !== undefined) dbUpdates.priority = updates.priority;
|
|
||||||
// Skip fields that don't exist in database schema: details, testStrategy, etc.
|
|
||||||
|
|
||||||
// Update the task
|
|
||||||
const { error } = await this.supabase
|
|
||||||
.from('tasks')
|
|
||||||
.update(dbUpdates)
|
|
||||||
.eq('brief_id', context.briefId)
|
|
||||||
.eq('display_id', taskId.toUpperCase());
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(`Failed to update task: ${error.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the updated task by fetching it
|
|
||||||
const updatedTask = await this.getTask(projectId, taskId);
|
|
||||||
if (!updatedTask) {
|
|
||||||
throw new Error(`Failed to retrieve updated task ${taskId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps internal status to database status
|
|
||||||
*/
|
|
||||||
private mapStatusToDatabase(
|
|
||||||
status: string
|
|
||||||
): Database['public']['Enums']['task_status'] {
|
|
||||||
switch (status) {
|
|
||||||
case 'pending':
|
|
||||||
return 'todo';
|
|
||||||
case 'in-progress':
|
|
||||||
case 'in_progress': // Accept both formats
|
|
||||||
return 'in_progress';
|
|
||||||
case 'done':
|
|
||||||
return 'done';
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Invalid task status: ${status}. Valid statuses are: pending, in-progress, done`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -360,73 +360,4 @@ export class TaskService {
|
|||||||
async setActiveTag(tag: string): Promise<void> {
|
async setActiveTag(tag: string): Promise<void> {
|
||||||
await this.configManager.setActiveTag(tag);
|
await this.configManager.setActiveTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update task status
|
|
||||||
*/
|
|
||||||
async updateTaskStatus(
|
|
||||||
taskId: string | number,
|
|
||||||
newStatus: TaskStatus,
|
|
||||||
tag?: string
|
|
||||||
): Promise<{
|
|
||||||
success: boolean;
|
|
||||||
oldStatus: TaskStatus;
|
|
||||||
newStatus: TaskStatus;
|
|
||||||
taskId: string;
|
|
||||||
}> {
|
|
||||||
// Ensure we have storage
|
|
||||||
if (!this.storage) {
|
|
||||||
throw new TaskMasterError(
|
|
||||||
'Storage not initialized',
|
|
||||||
ERROR_CODES.STORAGE_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use provided tag or get active tag
|
|
||||||
const activeTag = tag || this.getActiveTag();
|
|
||||||
|
|
||||||
const taskIdStr = String(taskId);
|
|
||||||
|
|
||||||
// TODO: For now, assume it's a regular task and just try to update directly
|
|
||||||
// In the future, we can add subtask support if needed
|
|
||||||
if (taskIdStr.includes('.')) {
|
|
||||||
throw new TaskMasterError(
|
|
||||||
'Subtask status updates not yet supported in API storage',
|
|
||||||
ERROR_CODES.NOT_IMPLEMENTED
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current task to get old status (simple, direct approach)
|
|
||||||
let currentTask: Task | null;
|
|
||||||
try {
|
|
||||||
// Try to get the task directly
|
|
||||||
currentTask = await this.storage.loadTask(taskIdStr, activeTag);
|
|
||||||
} catch (error) {
|
|
||||||
throw new TaskMasterError(
|
|
||||||
`Failed to load task ${taskIdStr}`,
|
|
||||||
ERROR_CODES.TASK_NOT_FOUND,
|
|
||||||
{ taskId: taskIdStr },
|
|
||||||
error as Error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentTask) {
|
|
||||||
throw new TaskMasterError(
|
|
||||||
`Task ${taskIdStr} not found`,
|
|
||||||
ERROR_CODES.TASK_NOT_FOUND
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldStatus = currentTask.status;
|
|
||||||
|
|
||||||
// Simple, direct update - just change the status
|
|
||||||
await this.storage.updateTask(taskIdStr, { status: newStatus }, activeTag);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
oldStatus,
|
|
||||||
newStatus,
|
|
||||||
taskId: taskIdStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,6 +223,14 @@ export class ApiStorage implements IStorage {
|
|||||||
await this.ensureInitialized();
|
await this.ensureInitialized();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (tag) {
|
||||||
|
// Check if task is in tag
|
||||||
|
const tagData = this.tagsCache.get(tag);
|
||||||
|
if (!tagData || !tagData.tasks.includes(taskId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return await this.retryOperation(() =>
|
return await this.retryOperation(() =>
|
||||||
this.repository.getTask(this.projectId, taskId)
|
this.repository.getTask(this.projectId, taskId)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -102,14 +102,6 @@ export class FileStorage implements IStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a single task by ID from the tasks.json file
|
|
||||||
*/
|
|
||||||
async loadTask(taskId: string, tag?: string): Promise<Task | null> {
|
|
||||||
const tasks = await this.loadTasks(tag);
|
|
||||||
return tasks.find((task) => task.id === taskId) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save tasks for a specific tag in the single tasks.json file
|
* Save tasks for a specific tag in the single tasks.json file
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -175,22 +175,6 @@ export class TaskMasterCore {
|
|||||||
await this.configManager.setActiveTag(tag);
|
await this.configManager.setActiveTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update task status
|
|
||||||
*/
|
|
||||||
async updateTaskStatus(
|
|
||||||
taskId: string | number,
|
|
||||||
newStatus: TaskStatus,
|
|
||||||
tag?: string
|
|
||||||
): Promise<{
|
|
||||||
success: boolean;
|
|
||||||
oldStatus: TaskStatus;
|
|
||||||
newStatus: TaskStatus;
|
|
||||||
taskId: string;
|
|
||||||
}> {
|
|
||||||
return this.taskService.updateTaskStatus(taskId, newStatus, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close and cleanup resources
|
* Close and cleanup resources
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ export type TaskStatus =
|
|||||||
| 'deferred'
|
| 'deferred'
|
||||||
| 'cancelled'
|
| 'cancelled'
|
||||||
| 'blocked'
|
| 'blocked'
|
||||||
| 'review'
|
| 'review';
|
||||||
| 'completed';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task priority levels
|
* Task priority levels
|
||||||
|
|||||||
@@ -20,17 +20,14 @@ import {
|
|||||||
ListTasksCommand,
|
ListTasksCommand,
|
||||||
ShowCommand,
|
ShowCommand,
|
||||||
AuthCommand,
|
AuthCommand,
|
||||||
ContextCommand,
|
ContextCommand
|
||||||
SetStatusCommand,
|
|
||||||
checkForUpdate,
|
|
||||||
performAutoUpdate,
|
|
||||||
displayUpgradeNotification
|
|
||||||
} from '@tm/cli';
|
} from '@tm/cli';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
parsePRD,
|
parsePRD,
|
||||||
updateTasks,
|
updateTasks,
|
||||||
generateTaskFiles,
|
generateTaskFiles,
|
||||||
|
setTaskStatus,
|
||||||
listTasks,
|
listTasks,
|
||||||
expandTask,
|
expandTask,
|
||||||
expandAllTasks,
|
expandAllTasks,
|
||||||
@@ -85,7 +82,8 @@ import {
|
|||||||
isConfigFilePresent,
|
isConfigFilePresent,
|
||||||
getAvailableModels,
|
getAvailableModels,
|
||||||
getBaseUrlForRole,
|
getBaseUrlForRole,
|
||||||
getDefaultNumTasks
|
getDefaultNumTasks,
|
||||||
|
getDefaultSubtasks
|
||||||
} from './config-manager.js';
|
} from './config-manager.js';
|
||||||
|
|
||||||
import { CUSTOM_PROVIDERS } from '../../src/constants/providers.js';
|
import { CUSTOM_PROVIDERS } from '../../src/constants/providers.js';
|
||||||
@@ -1686,9 +1684,63 @@ function registerCommands(programInstance) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register the set-status command from @tm/cli
|
// set-status command
|
||||||
// Handles task status updates with proper error handling and validation
|
programInstance
|
||||||
SetStatusCommand.registerOn(programInstance);
|
.command('set-status')
|
||||||
|
.alias('mark')
|
||||||
|
.alias('set')
|
||||||
|
.description('Set the status of a task')
|
||||||
|
.option(
|
||||||
|
'-i, --id <id>',
|
||||||
|
'Task ID (can be comma-separated for multiple tasks)'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-s, --status <status>',
|
||||||
|
`New status (one of: ${TASK_STATUS_OPTIONS.join(', ')})`
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-f, --file <file>',
|
||||||
|
'Path to the tasks file',
|
||||||
|
TASKMASTER_TASKS_FILE
|
||||||
|
)
|
||||||
|
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||||
|
.action(async (options) => {
|
||||||
|
// Initialize TaskMaster
|
||||||
|
const taskMaster = initTaskMaster({
|
||||||
|
tasksPath: options.file || true,
|
||||||
|
tag: options.tag
|
||||||
|
});
|
||||||
|
|
||||||
|
const taskId = options.id;
|
||||||
|
const status = options.status;
|
||||||
|
|
||||||
|
if (!taskId || !status) {
|
||||||
|
console.error(chalk.red('Error: Both --id and --status are required'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidTaskStatus(status)) {
|
||||||
|
console.error(
|
||||||
|
chalk.red(
|
||||||
|
`Error: Invalid status value: ${status}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const tag = taskMaster.getCurrentTag();
|
||||||
|
|
||||||
|
displayCurrentTagIndicator(tag);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
chalk.blue(`Setting status of task(s) ${taskId} to: ${status}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
await setTaskStatus(taskMaster.getTasksPath(), taskId, status, {
|
||||||
|
projectRoot: taskMaster.getProjectRoot(),
|
||||||
|
tag
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// NEW: Register the new list command from @tm/cli
|
// NEW: Register the new list command from @tm/cli
|
||||||
// This command handles all its own configuration and logic
|
// This command handles all its own configuration and logic
|
||||||
@@ -5115,6 +5167,122 @@ function setupCLI() {
|
|||||||
return programInstance;
|
return programInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for newer version of task-master-ai
|
||||||
|
* @returns {Promise<{currentVersion: string, latestVersion: string, needsUpdate: boolean}>}
|
||||||
|
*/
|
||||||
|
async function checkForUpdate() {
|
||||||
|
// Get current version from package.json ONLY
|
||||||
|
const currentVersion = getTaskMasterVersion();
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// Get the latest version from npm registry
|
||||||
|
const options = {
|
||||||
|
hostname: 'registry.npmjs.org',
|
||||||
|
path: '/task-master-ai',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/vnd.npm.install-v1+json' // Lightweight response
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const npmData = JSON.parse(data);
|
||||||
|
const latestVersion = npmData['dist-tags']?.latest || currentVersion;
|
||||||
|
|
||||||
|
// Compare versions
|
||||||
|
const needsUpdate =
|
||||||
|
compareVersions(currentVersion, latestVersion) < 0;
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
currentVersion,
|
||||||
|
latestVersion,
|
||||||
|
needsUpdate
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log('debug', `Error parsing npm response: ${error.message}`);
|
||||||
|
resolve({
|
||||||
|
currentVersion,
|
||||||
|
latestVersion: currentVersion,
|
||||||
|
needsUpdate: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (error) => {
|
||||||
|
log('debug', `Error checking for updates: ${error.message}`);
|
||||||
|
resolve({
|
||||||
|
currentVersion,
|
||||||
|
latestVersion: currentVersion,
|
||||||
|
needsUpdate: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set a timeout to avoid hanging if npm is slow
|
||||||
|
req.setTimeout(3000, () => {
|
||||||
|
req.abort();
|
||||||
|
log('debug', 'Update check timed out');
|
||||||
|
resolve({
|
||||||
|
currentVersion,
|
||||||
|
latestVersion: currentVersion,
|
||||||
|
needsUpdate: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare semantic versions
|
||||||
|
* @param {string} v1 - First version
|
||||||
|
* @param {string} v2 - Second version
|
||||||
|
* @returns {number} -1 if v1 < v2, 0 if v1 = v2, 1 if v1 > v2
|
||||||
|
*/
|
||||||
|
function compareVersions(v1, v2) {
|
||||||
|
const v1Parts = v1.split('.').map((p) => parseInt(p, 10));
|
||||||
|
const v2Parts = v2.split('.').map((p) => parseInt(p, 10));
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
||||||
|
const v1Part = v1Parts[i] || 0;
|
||||||
|
const v2Part = v2Parts[i] || 0;
|
||||||
|
|
||||||
|
if (v1Part < v2Part) return -1;
|
||||||
|
if (v1Part > v2Part) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display upgrade notification message
|
||||||
|
* @param {string} currentVersion - Current version
|
||||||
|
* @param {string} latestVersion - Latest version
|
||||||
|
*/
|
||||||
|
function displayUpgradeNotification(currentVersion, latestVersion) {
|
||||||
|
const message = boxen(
|
||||||
|
`${chalk.blue.bold('Update Available!')} ${chalk.dim(currentVersion)} → ${chalk.green(latestVersion)}\n\n` +
|
||||||
|
`Run ${chalk.cyan('npm i task-master-ai@latest -g')} to update to the latest version with new features and bug fixes.`,
|
||||||
|
{
|
||||||
|
padding: 1,
|
||||||
|
margin: { top: 1, bottom: 1 },
|
||||||
|
borderColor: 'yellow',
|
||||||
|
borderStyle: 'round'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse arguments and run the CLI
|
* Parse arguments and run the CLI
|
||||||
* @param {Array} argv - Command-line arguments
|
* @param {Array} argv - Command-line arguments
|
||||||
@@ -5133,8 +5301,7 @@ async function runCLI(argv = process.argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start the update check in the background - don't await yet
|
// Start the update check in the background - don't await yet
|
||||||
const currentVersion = getTaskMasterVersion();
|
const updateCheckPromise = checkForUpdate();
|
||||||
const updateCheckPromise = checkForUpdate(currentVersion);
|
|
||||||
|
|
||||||
// Setup and parse
|
// Setup and parse
|
||||||
// NOTE: getConfig() might be called during setupCLI->registerCommands if commands need config
|
// NOTE: getConfig() might be called during setupCLI->registerCommands if commands need config
|
||||||
@@ -5145,18 +5312,10 @@ async function runCLI(argv = process.argv) {
|
|||||||
// After command execution, check if an update is available
|
// After command execution, check if an update is available
|
||||||
const updateInfo = await updateCheckPromise;
|
const updateInfo = await updateCheckPromise;
|
||||||
if (updateInfo.needsUpdate) {
|
if (updateInfo.needsUpdate) {
|
||||||
// Display the upgrade notification first
|
|
||||||
displayUpgradeNotification(
|
displayUpgradeNotification(
|
||||||
updateInfo.currentVersion,
|
updateInfo.currentVersion,
|
||||||
updateInfo.latestVersion
|
updateInfo.latestVersion
|
||||||
);
|
);
|
||||||
|
|
||||||
// Then automatically perform the update
|
|
||||||
const updateSuccess = await performAutoUpdate(updateInfo.latestVersion);
|
|
||||||
if (updateSuccess) {
|
|
||||||
// Exit gracefully after successful update
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if migration has occurred and show FYI notice once
|
// Check if migration has occurred and show FYI notice once
|
||||||
@@ -5280,4 +5439,11 @@ export function resolveComplexityReportPath({
|
|||||||
return tag !== 'master' ? base.replace('.json', `_${tag}.json`) : base;
|
return tag !== 'master' ? base.replace('.json', `_${tag}.json`) : base;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { registerCommands, setupCLI, runCLI };
|
export {
|
||||||
|
registerCommands,
|
||||||
|
setupCLI,
|
||||||
|
runCLI,
|
||||||
|
checkForUpdate,
|
||||||
|
compareVersions,
|
||||||
|
displayUpgradeNotification
|
||||||
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ describe('Complex Cross-Tag Scenarios', () => {
|
|||||||
'..',
|
'..',
|
||||||
'..',
|
'..',
|
||||||
'..',
|
'..',
|
||||||
'dist',
|
'bin',
|
||||||
'task-master.js'
|
'task-master.js'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -25,8 +25,6 @@ describe('Complex Cross-Tag Scenarios', () => {
|
|||||||
// Create test directory
|
// Create test directory
|
||||||
testDir = fs.mkdtempSync(path.join(__dirname, 'test-'));
|
testDir = fs.mkdtempSync(path.join(__dirname, 'test-'));
|
||||||
process.chdir(testDir);
|
process.chdir(testDir);
|
||||||
// Keep integration timings deterministic
|
|
||||||
process.env.TASKMASTER_SKIP_AUTO_UPDATE = '1';
|
|
||||||
|
|
||||||
// Initialize task-master
|
// Initialize task-master
|
||||||
execSync(`node ${binPath} init --yes`, {
|
execSync(`node ${binPath} init --yes`, {
|
||||||
@@ -139,7 +137,6 @@ describe('Complex Cross-Tag Scenarios', () => {
|
|||||||
if (testDir && fs.existsSync(testDir)) {
|
if (testDir && fs.existsSync(testDir)) {
|
||||||
fs.rmSync(testDir, { recursive: true, force: true });
|
fs.rmSync(testDir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
delete process.env.TASKMASTER_SKIP_AUTO_UPDATE;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Circular Dependency Detection', () => {
|
describe('Circular Dependency Detection', () => {
|
||||||
@@ -372,7 +369,7 @@ describe('Complex Cross-Tag Scenarios', () => {
|
|||||||
|
|
||||||
fs.writeFileSync(tasksPath, JSON.stringify(largeTaskSet, null, 2));
|
fs.writeFileSync(tasksPath, JSON.stringify(largeTaskSet, null, 2));
|
||||||
// Should complete within reasonable time
|
// Should complete within reasonable time
|
||||||
const timeout = process.env.CI ? 11000 : 6000;
|
const timeout = process.env.CI ? 10000 : 5000;
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
execSync(
|
execSync(
|
||||||
`node ${binPath} move --from=50 --from-tag=master --to-tag=in-progress --with-dependencies`,
|
`node ${binPath} move --from=50 --from-tag=master --to-tag=in-progress --with-dependencies`,
|
||||||
|
|||||||
@@ -280,26 +280,8 @@ describe('Version comparison utility', () => {
|
|||||||
let compareVersions;
|
let compareVersions;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Import from @tm/cli instead of commands.js
|
const commandsModule = await import('../../scripts/modules/commands.js');
|
||||||
const { compareVersions: cv } = await import(
|
compareVersions = commandsModule.compareVersions;
|
||||||
'../../apps/cli/src/utils/auto-update.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a local compareVersions function for testing
|
|
||||||
compareVersions = (v1, v2) => {
|
|
||||||
const v1Parts = v1.split('.').map((p) => parseInt(p, 10));
|
|
||||||
const v2Parts = v2.split('.').map((p) => parseInt(p, 10));
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
|
||||||
const v1Part = v1Parts[i] || 0;
|
|
||||||
const v2Part = v2Parts[i] || 0;
|
|
||||||
|
|
||||||
if (v1Part < v2Part) return -1;
|
|
||||||
if (v1Part > v2Part) return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('compareVersions correctly compares semantic versions', () => {
|
test('compareVersions correctly compares semantic versions', () => {
|
||||||
@@ -321,9 +303,8 @@ describe('Update check functionality', () => {
|
|||||||
let consoleLogSpy;
|
let consoleLogSpy;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Import from @tm/cli instead of commands.js
|
const commandsModule = await import('../../scripts/modules/commands.js');
|
||||||
const cliModule = await import('../../apps/cli/src/utils/auto-update.js');
|
displayUpgradeNotification = commandsModule.displayUpgradeNotification;
|
||||||
displayUpgradeNotification = cliModule.displayUpgradeNotification;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import { defineConfig } from 'tsdown';
|
|
||||||
import { baseConfig, mergeConfig } from '@tm/build-config';
|
|
||||||
import { load as dotenvLoad } from 'dotenv-mono';
|
|
||||||
|
|
||||||
dotenvLoad();
|
|
||||||
|
|
||||||
// Get all TM_PUBLIC_* env variables for build-time injection
|
|
||||||
const getBuildTimeEnvs = () => {
|
|
||||||
const envs: Record<string, string> = {};
|
|
||||||
for (const [key, value] of Object.entries(process.env)) {
|
|
||||||
if (key.startsWith('TM_PUBLIC_')) {
|
|
||||||
// Return the actual value, not JSON.stringify'd
|
|
||||||
envs[key] = value || '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return envs;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default defineConfig(
|
|
||||||
mergeConfig(baseConfig, {
|
|
||||||
entry: {
|
|
||||||
'task-master': 'scripts/dev.js',
|
|
||||||
'mcp-server': 'mcp-server/server.js'
|
|
||||||
},
|
|
||||||
outDir: 'dist',
|
|
||||||
copy: ['public'],
|
|
||||||
ignoreWatch: ['node_modules', 'dist', 'tests'],
|
|
||||||
// Bundle only our workspace packages, keep npm dependencies external
|
|
||||||
noExternal: [/^@tm\//],
|
|
||||||
env: getBuildTimeEnvs()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
97
tsup.config.ts
Normal file
97
tsup.config.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { defineConfig } from 'tsup';
|
||||||
|
import { baseConfig, mergeConfig } from '@tm/build-config';
|
||||||
|
import { load as dotenvLoad } from 'dotenv-mono';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
dotenvLoad();
|
||||||
|
|
||||||
|
// Get all TM_PUBLIC_* env variables for build-time injection
|
||||||
|
const getBuildTimeEnvs = () => {
|
||||||
|
const envs: Record<string, string> = {};
|
||||||
|
for (const [key, value] of Object.entries(process.env)) {
|
||||||
|
if (key.startsWith('TM_PUBLIC_')) {
|
||||||
|
// Return the actual value, not JSON.stringify'd
|
||||||
|
envs[key] = value || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envs;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineConfig(
|
||||||
|
mergeConfig(baseConfig, {
|
||||||
|
entry: {
|
||||||
|
'task-master': 'scripts/dev.js',
|
||||||
|
'mcp-server': 'mcp-server/server.js'
|
||||||
|
},
|
||||||
|
outDir: 'dist',
|
||||||
|
publicDir: 'public',
|
||||||
|
// Override the base config's external to bundle our workspace packages
|
||||||
|
noExternal: [/^@tm\//],
|
||||||
|
external: [
|
||||||
|
/^@supabase\//, // Keep Supabase external to avoid dynamic require issues
|
||||||
|
'marked',
|
||||||
|
'marked-terminal'
|
||||||
|
],
|
||||||
|
env: getBuildTimeEnvs(),
|
||||||
|
esbuildOptions(options) {
|
||||||
|
// Set up path aliases for workspace packages
|
||||||
|
options.alias = {
|
||||||
|
'@tm/core': path.resolve(__dirname, 'packages/tm-core/src/index.ts'),
|
||||||
|
'@tm/core/auth': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/auth/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/storage': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/storage/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/config': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/config/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/providers': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/providers/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/services': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/services/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/errors': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/errors/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/logger': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/logger/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/types': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/types/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/interfaces': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/interfaces/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/core/utils': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/tm-core/src/utils/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/cli': path.resolve(__dirname, 'apps/cli/src/index.ts'),
|
||||||
|
'@tm/cli/commands': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'apps/cli/src/commands/index.ts'
|
||||||
|
),
|
||||||
|
'@tm/cli/utils': path.resolve(__dirname, 'apps/cli/src/utils/index.ts'),
|
||||||
|
'@tm/cli/ui': path.resolve(__dirname, 'apps/cli/src/ui/index.ts'),
|
||||||
|
'@tm/build-config': path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'packages/build-config/src/tsup.base.ts'
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user