Skip to content

feat: implement workflow (wip)#1198

Closed
Crunchyman-ralph wants to merge 1 commit intonextfrom
ralph/feat-workflow-from-next
Closed

feat: implement workflow (wip)#1198
Crunchyman-ralph wants to merge 1 commit intonextfrom
ralph/feat-workflow-from-next

Conversation

@Crunchyman-ralph
Copy link
Collaborator

@Crunchyman-ralph Crunchyman-ralph commented Sep 11, 2025

What type of PR is this?

  • 🐛 Bug fix
  • ✨ Feature
  • 🔌 Integration
  • 📝 Docs
  • 🧹 Refactor
  • Other:

Description

Related Issues

How to Test This

# Example commands or steps

Expected result:

Contributor Checklist

  • Created changeset: npm run changeset
  • Tests pass: npm test
  • Format check passes: npm run format-check (or npm run format to fix)
  • Addressed CodeRabbit comments (if any)
  • Linked related issues (if any)
  • Manually tested the changes

Changelog Entry


For Maintainers

  • PR title follows conventional commits
  • Target branch correct
  • Labels added
  • Milestone assigned (if applicable)

Summary by CodeRabbit

  • New Features
    • Introduced a workflow engine with new CLI commands: workflow (wf) with start/run, list, status/info (with watch, JSON/compact/text), and stop/kill.
    • Added interactive CLI enhancements: spinners, confirmations, and tabular output.
    • Core now exposes a workflow service for managing task executions.
  • Refactor
    • Centralized auto-registration of CLI commands; removed legacy registration paths.
  • Chores
    • Added new workflow package and dependencies; updated dev/build/typecheck scripts across the repo.
  • Documentation
    • Added workflow engine README.
  • Tests
    • Added test configuration for the workflow engine.

@changeset-bot
Copy link

changeset-bot bot commented Sep 11, 2025

⚠️ No Changeset found

Latest commit: 7c1d059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

Walkthrough

Adds a new workflow engine package and integrates it into core and CLI. Introduces workflow CLI commands (start, list, stop, status) and a command registry. Removes legacy registerOn methods. Updates build/typecheck scripts and core build config. Adds UI helpers (with duplicate definitions). Adjusts scripts to use centralized registration.

Changes

Cohort / File(s) Summary of Changes
CLI command registry and APIs
apps/cli/src/commands/index.ts, apps/cli/src/commands/auth.command.ts, apps/cli/src/commands/list.command.ts, scripts/modules/commands.js, apps/cli/src/index.ts
Added centralized registerAllCommands; removed static registerOn from AuthCommand and ListTasksCommand; updated script to use registry; re-export registry and WorkflowCommand.
New workflow parent command and barrel
apps/cli/src/commands/workflow.command.ts, apps/cli/src/commands/workflow/index.ts
Added WorkflowCommand with subcommand wiring and aliases; barrel re-exports workflow subcommands.
Workflow CLI subcommands
apps/cli/src/commands/workflow/workflow-start.command.ts, apps/cli/src/commands/workflow/workflow-list.command.ts, apps/cli/src/commands/workflow/workflow-status.command.ts, apps/cli/src/commands/workflow/workflow-stop.command.ts
Added Start, List, Status, Stop commands with options, rendering, manager initialization, event handling, and cleanup; each exposes static register.
CLI UI utilities
apps/cli/src/utils/ui.ts
Added displaySpinner, confirm, createTable; code duplicated causing redeclarations.
Root scripts and monorepo wiring
package.json, apps/cli/package.json
Added dev/build scripts for workflow-engine; updated dev/build/typecheck pipelines; added dependency @tm/workflow-engine to CLI.
Core workflow integration
packages/tm-core/src/index.ts, packages/tm-core/src/task-master-core.ts, packages/tm-core/src/workflow/index.ts, packages/tm-core/src/workflow/workflow-service.ts, packages/tm-core/package.json, packages/tm-core/tsup.config.ts
Exposed WorkflowService/types; integrated WorkflowService into TaskMasterCore (options, lifecycle, getter); added workflow exports and deps; overhauled build config to include workflow, dual formats, env injection.
New workflow-engine package - manifests/config
packages/workflow-engine/package.json, packages/workflow-engine/tsconfig.json, packages/workflow-engine/tsup.config.ts, packages/workflow-engine/vitest.config.ts, packages/workflow-engine/README.md
Introduced new package with exports, scripts, build/test configs, and README.
Workflow-engine types and errors
packages/workflow-engine/src/types/..., packages/workflow-engine/src/errors/..., packages/workflow-engine/src/index.ts
Added core type definitions and error classes; barrel exports; aliased WorkflowEngine to TaskExecutionManager.
Workflow-engine state management
packages/workflow-engine/src/state/index.ts, packages/workflow-engine/src/state/workflow-state-manager.ts
Added persistent state manager with registry, queries, events recording, cleanup utilities; barrel export.
Workflow-engine process management
packages/workflow-engine/src/process/index.ts, packages/workflow-engine/src/process/process-sandbox.ts
Added ProcessSandbox to spawn/manage Claude processes with timeouts, events, cleanup; barrel export.
Workflow-engine worktree management
packages/workflow-engine/src/worktree/index.ts, packages/workflow-engine/src/worktree/worktree-manager.ts
Added WorktreeManager to create/remove/list/lock/unlock git worktrees; barrel export.
Workflow-engine execution manager
packages/workflow-engine/src/task-execution/index.ts, packages/workflow-engine/src/task-execution/task-execution-manager.ts
Added TaskExecutionManager coordinating worktrees, processes, state; public API for lifecycle and queries; barrel export.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI (WorkflowStartCommand)
  participant Core as TaskMasterCore
  participant WS as WorkflowService
  participant TEM as TaskExecutionManager
  participant WT as WorktreeManager
  participant PS as ProcessSandbox
  participant SM as WorkflowStateManager

  User->>CLI: workflow start <taskId> [options]
  CLI->>Core: initialize(projectRoot, workflow config)
  Core->>WS: initialize()
  WS->>TEM: initialize()
  TEM->>SM: loadState()
  CLI->>Core: get task by id
  CLI->>WS: start(taskId, {branch, timeout, env})
  WS->>TEM: startTaskExecution(task, options)
  TEM->>WT: createWorktree(taskId, branch)
  TEM->>PS: startProcess(workflowId, taskId, prompt, {cwd, timeout, env})
  PS-->>TEM: process.started(pid)
  TEM->>SM: registerWorkflow(context)
  TEM-->>WS: workflowId
  WS-->>CLI: workflowId
  CLI-->>User: Print workflowId, next steps
Loading
sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI (WorkflowStatusCommand)
  participant TEM as TaskExecutionManager
  participant SM as WorkflowStateManager

  User->>CLI: workflow status <workflowId> [--watch]
  CLI->>TEM: getWorkflowStatus(workflowId)
  TEM->>SM: getWorkflow(workflowId)
  SM-->>TEM: context
  TEM-->>CLI: context
  alt watch mode
    loop every 2s until final
      CLI->>TEM: getWorkflowStatus()
      TEM->>SM: getWorkflow()
      SM-->>TEM: context
      TEM-->>CLI: context
      CLI-->>User: refresh display
    end
  else once
    CLI-->>User: print status
  end
Loading
sequenceDiagram
  autonumber
  participant Main as scripts/modules/commands.js
  participant CLI as @tm/cli
  participant Reg as registerAllCommands
  participant Cmds as Command Classes

  Main->>CLI: import registerAllCommands
  Main->>Reg: registerAllCommands(program)
  Reg->>Cmds: iterate [ListTasks, Auth, Workflow]
  Cmds-->>Reg: static register(program)
  Reg-->>Main: commands attached to program
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • eyaltoledano

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly states the main intent—adding workflow support—which aligns with the changeset (new @tm/workflow-engine package, core workflow exports, and CLI workflow commands); it is concise and uses the "feat:" prefix. The "(wip)" suffix is noisy but does not make the title incorrect or unrelated to the changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ralph/feat-workflow-from-next

Comment @coderabbitai help to get the list of available commands and usage tips.

@Crunchyman-ralph Crunchyman-ralph mentioned this pull request Sep 11, 2025
16 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 95

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
apps/cli/src/utils/ui.ts (1)

1-385: Fix Biome formatting to unblock CI.

CI reports Biome format issues for this file. Run: npm run format.

package.json (1)

24-26: Add workflow typecheck to the aggregate script.

New package isn’t included in the root typecheck.

-    "typecheck": "npm run typecheck:core && npm run typecheck:cli",
+    "typecheck": "npm run typecheck:core && npm run typecheck:workflow && npm run typecheck:cli",
     "typecheck:core": "cd packages/tm-core && npm run typecheck",
+    "typecheck:workflow": "cd packages/workflow-engine && npm run typecheck",
     "typecheck:cli": "cd apps/cli && npm run typecheck",
packages/tm-core/package.json (1)

10-13: Consider adding "require" condition for the root export.

If any CJS consumers import "@tm/core", missing "require" under "." with an exports map can break resolution. Add it if CJS usage exists.

".": {
  "types": "./src/index.ts",
- "import": "./dist/index.js"
+ "import": "./dist/index.js",
+ "require": "./dist/index.js"
},
packages/workflow-engine/src/process/process-sandbox.ts (1)

1-379: CI: Biome format check failing — fix biome.json

biome.json (repo root) contains unknown keys causing Biome to error: files.ignore (line 3), linter.include (line 28), linter.ignore (line 29). Update/remove those keys to match Biome's config schema (e.g., use supported keys like "includes" or the appropriate scanner ignore settings), then run npm run format / npm run format-check.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3eeb195 and 7c1d059.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (38)
  • apps/cli/package.json (1 hunks)
  • apps/cli/src/commands/auth.command.ts (0 hunks)
  • apps/cli/src/commands/index.ts (1 hunks)
  • apps/cli/src/commands/list.command.ts (0 hunks)
  • apps/cli/src/commands/workflow.command.ts (1 hunks)
  • apps/cli/src/commands/workflow/index.ts (1 hunks)
  • apps/cli/src/commands/workflow/workflow-list.command.ts (1 hunks)
  • apps/cli/src/commands/workflow/workflow-start.command.ts (1 hunks)
  • apps/cli/src/commands/workflow/workflow-status.command.ts (1 hunks)
  • apps/cli/src/commands/workflow/workflow-stop.command.ts (1 hunks)
  • apps/cli/src/index.ts (1 hunks)
  • apps/cli/src/utils/ui.ts (1 hunks)
  • package.json (1 hunks)
  • packages/tm-core/package.json (2 hunks)
  • packages/tm-core/src/index.ts (1 hunks)
  • packages/tm-core/src/task-master-core.ts (5 hunks)
  • packages/tm-core/src/workflow/index.ts (1 hunks)
  • packages/tm-core/src/workflow/workflow-service.ts (1 hunks)
  • packages/tm-core/tsup.config.ts (1 hunks)
  • packages/workflow-engine/README.md (1 hunks)
  • packages/workflow-engine/package.json (1 hunks)
  • packages/workflow-engine/src/errors/index.ts (1 hunks)
  • packages/workflow-engine/src/errors/workflow.errors.ts (1 hunks)
  • packages/workflow-engine/src/index.ts (1 hunks)
  • packages/workflow-engine/src/process/index.ts (1 hunks)
  • packages/workflow-engine/src/process/process-sandbox.ts (1 hunks)
  • packages/workflow-engine/src/state/index.ts (1 hunks)
  • packages/workflow-engine/src/state/workflow-state-manager.ts (1 hunks)
  • packages/workflow-engine/src/task-execution/index.ts (1 hunks)
  • packages/workflow-engine/src/task-execution/task-execution-manager.ts (1 hunks)
  • packages/workflow-engine/src/types/index.ts (1 hunks)
  • packages/workflow-engine/src/types/workflow.types.ts (1 hunks)
  • packages/workflow-engine/src/worktree/index.ts (1 hunks)
  • packages/workflow-engine/src/worktree/worktree-manager.ts (1 hunks)
  • packages/workflow-engine/tsconfig.json (1 hunks)
  • packages/workflow-engine/tsup.config.ts (1 hunks)
  • packages/workflow-engine/vitest.config.ts (1 hunks)
  • scripts/modules/commands.js (2 hunks)
💤 Files with no reviewable changes (2)
  • apps/cli/src/commands/auth.command.ts
  • apps/cli/src/commands/list.command.ts
🧰 Additional context used
📓 Path-based instructions (6)
scripts/modules/commands.js

📄 CodeRabbit inference engine (.cursor/rules/ai_services.mdc)

scripts/modules/commands.js: Centralize all LLM calls through generateTextService or generateObjectService.
Do not import or call anything from the old ai-services.js, ai-client-factory.js, or ai-client-utils.js files.
Do not fetch AI-specific parameters (model ID, max tokens, temp) using config-manager.js getters for the AI call. Pass the role instead.
Do not implement fallback or retry logic outside ai-services-unified.js.
Do not handle API key resolution outside the service layer (it uses utils.js internally).
Determine the appropriate role (main, research, fallback) in your core logic and pass it to the service.
Pass the session object (received in the context parameter, especially from direct function wrappers) to the service call when in MCP context.
Use generateTextService and implement robust manual JSON parsing (with Zod validation after parsing) when structured output is needed, as generateObjectService has shown unreliability with some providers/schemas.
Be aware of potential reliability issues with generateObjectService across different providers and complex schemas. Prefer generateTextService + manual parsing as a more robust alternative for structured data needs.

scripts/modules/commands.js: All new user-facing commands should be added to 'scripts/modules/commands.js'.
Use consistent patterns for option naming and help text in CLI commands.
Follow the Commander.js model for subcommand structure in CLI commands.
When using callbacks (like in Commander.js commands), define them separately to allow testing the callback logic independently.
Add help text to the command definition and update 'dev_workflow.mdc' with command reference when adding a new feature.
Follow the established pattern in 'commands.js' for CLI command implementation, using Commander.js for argument parsing, including comprehensive help text and examples, and supporting tagged task context awareness.
Provide clear error messages for common failu...

Files:

  • scripts/modules/commands.js
scripts/modules/*.js

📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)

Each module in scripts/modules/ should be focused on a single responsibility, following the modular architecture (e.g., commands.js for CLI command handling, task-manager.js for task data and core logic, dependency-manager.js for dependency management, ui.js for CLI output formatting, ai-services-unified.js for AI service integration, config-manager.js for configuration management, utils.js for utility functions).

scripts/modules/*.js: Export all core functions, helper functions, and utility methods needed by your new function or command from their respective modules. Explicitly review the module's export block to ensure every required dependency is included.
Pass all required parameters to functions you call within your implementation and verify that direct function parameters match their core function counterparts.
Use consistent file naming conventions: 'task_${id.toString().padStart(3, '0')}.txt', use path.join for composing file paths, and use appropriate file extensions (.txt for tasks, .json for data).
Use structured error objects with code and message properties, include clear error messages, and handle both function-specific and file system errors.
Import all silent mode utilities together from 'scripts/modules/utils.js' and always use isSilentMode() to check global silent mode status. Wrap core function calls within direct functions using enableSilentMode() and disableSilentMode() in a try/finally block if the core function might produce console output.
Core functions should check outputFormat === 'text' before displaying UI elements and use internal logging that respects silent mode.
Design functions to accept dependencies as parameters (dependency injection) and avoid hard-coded dependencies that are difficult to mock.
Keep pure logic separate from I/O operations or UI rendering to allow testing the logic without mocking complex dependencies.
When implementing core logic for new features, do so in 'scripts/modules/' before CLI or MCP interfaces, and d...

Files:

  • scripts/modules/commands.js
scripts/modules/**

📄 CodeRabbit inference engine (.cursor/rules/dev_workflow.mdc)

When using the MCP server, restart it if core logic in scripts/modules or MCP tool/direct function definitions change.

Files:

  • scripts/modules/commands.js
scripts/modules/*

📄 CodeRabbit inference engine (.cursor/rules/tags.mdc)

scripts/modules/*: Every command that reads or writes tasks.json must be tag-aware
All command files must import getCurrentTag from utils.js
Every CLI command that operates on tasks must include the --tag CLI option
All commands must resolve the tag using the pattern: options.tag || getCurrentTag(projectRoot) || 'master'
All commands must find projectRoot with error handling before proceeding
All commands must pass { projectRoot, tag } as context to core functions
MCP direct functions must accept and use a context object containing projectRoot and tag, and pass them to core functions
Do not hard-code tag resolution (e.g., const tag = options.tag || 'master';); always use getCurrentTag
Do not omit the --tag CLI option in commands that operate on tasks
Do not omit the context parameter when calling core functions from commands
Do not call readJSON or writeJSON without passing projectRoot and tag

Files:

  • scripts/modules/commands.js
**/*.js

📄 CodeRabbit inference engine (.cursor/rules/tests.mdc)

**/*.js: Declare and initialize global variables at the top of modules to avoid hoisting issues.
Use proper function declarations to avoid hoisting issues and initialize variables before they are referenced.
Do not reference variables before their declaration in module scope.
Use dynamic imports (import()) to avoid initialization order issues in modules.

Files:

  • scripts/modules/commands.js
package.json

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

Add and update test scripts in package.json to include test, test:watch, test:coverage, test:unit, test:integration, test:e2e, and test:ci

Files:

  • package.json
🧠 Learnings (33)
📓 Common learnings
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/AGENTS.md:0-0
Timestamp: 2025-09-01T09:55:15.061Z
Learning: Applies to assets/.claude/commands/taskmaster-next.md : Create .claude/commands/taskmaster-next.md implementing the 4-step workflow to find and show the next task
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/AGENTS.md:0-0
Timestamp: 2025-09-01T09:55:15.061Z
Learning: Applies to assets/.claude/commands/taskmaster-complete.md : Create .claude/commands/taskmaster-complete.md implementing the 5-step task completion workflow
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T12:30:23.843Z
Learning: Import Task Master's development workflow commands and guidelines; treat the contents of ./.taskmaster/CLAUDE.md as if included in the main CLAUDE.md
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/auth/config.ts:5-7
Timestamp: 2025-09-02T21:51:27.921Z
Learning: The user Crunchyman-ralph prefers not to use node: scheme imports (e.g., 'node:os', 'node:path') for Node.js core modules and considers suggestions to change bare imports to node: scheme as too nitpicky.
📚 Learning: 2025-07-31T22:07:49.716Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/commands.mdc:0-0
Timestamp: 2025-07-31T22:07:49.716Z
Learning: Applies to scripts/modules/commands.js : Export the registerCommands function and keep the CLI setup code clean and maintainable.

Applied to files:

  • apps/cli/src/commands/index.ts
  • apps/cli/src/commands/workflow/index.ts
  • apps/cli/src/index.ts
  • scripts/modules/commands.js
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to jest.config.js : Create and maintain a production-ready Jest configuration in jest.config.js

Applied to files:

  • packages/workflow-engine/vitest.config.ts
📚 Learning: 2025-08-11T12:30:23.843Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T12:30:23.843Z
Learning: Import Task Master's development workflow commands and guidelines; treat the contents of ./.taskmaster/CLAUDE.md as if included in the main CLAUDE.md

Applied to files:

  • packages/workflow-engine/README.md
📚 Learning: 2025-07-18T17:10:53.657Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/glossary.mdc:0-0
Timestamp: 2025-07-18T17:10:53.657Z
Learning: Guide for using Task Master to manage task-driven development workflows with tagged task lists support (dev_workflow.mdc).

Applied to files:

  • packages/workflow-engine/README.md
📚 Learning: 2025-07-18T17:10:53.657Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/glossary.mdc:0-0
Timestamp: 2025-07-18T17:10:53.657Z
Learning: Describes the high-level architecture of the Task Master CLI application, including the new tagged task lists system (architecture.mdc).

Applied to files:

  • packages/workflow-engine/README.md
📚 Learning: 2025-09-01T09:55:15.061Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/AGENTS.md:0-0
Timestamp: 2025-09-01T09:55:15.061Z
Learning: Applies to assets/.claude/commands/taskmaster-complete.md : Create .claude/commands/taskmaster-complete.md implementing the 5-step task completion workflow

Applied to files:

  • packages/workflow-engine/README.md
  • apps/cli/src/commands/workflow/workflow-start.command.ts
📚 Learning: 2025-07-18T17:10:53.657Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/glossary.mdc:0-0
Timestamp: 2025-07-18T17:10:53.657Z
Learning: Guidelines for integrating new features into the Task Master CLI with tagged system considerations (new_features.mdc).

Applied to files:

  • packages/workflow-engine/README.md
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/task-manager.js : Features that create, read, update, or delete tasks belong in 'scripts/modules/task-manager.js'.

Applied to files:

  • packages/workflow-engine/src/task-execution/index.ts
📚 Learning: 2025-07-18T17:07:39.336Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2025-07-18T17:07:39.336Z
Learning: Applies to scripts/modules/task-manager/*.js : Files in scripts/modules/task-manager/ should each handle a specific action related to task management (e.g., add-task.js, expand-task.js), supporting the tagged task lists system and backward compatibility.

Applied to files:

  • packages/workflow-engine/src/task-execution/index.ts
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use consistent formatting for task files, include all task properties in text files, and format dependencies with status indicators.

Applied to files:

  • packages/workflow-engine/src/task-execution/index.ts
📚 Learning: 2025-07-18T17:09:16.839Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/context_gathering.mdc:0-0
Timestamp: 2025-07-18T17:09:16.839Z
Learning: Applies to scripts/modules/utils/fuzzyTaskSearch.js : Export `FuzzyTaskSearch`, `PURPOSE_CATEGORIES`, and `RELEVANCE_THRESHOLDS` from the fuzzy search module.

Applied to files:

  • packages/workflow-engine/src/task-execution/index.ts
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/dependency-manager.js : Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.

Applied to files:

  • packages/workflow-engine/src/task-execution/index.ts
📚 Learning: 2025-07-18T17:18:17.759Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-18T17:18:17.759Z
Learning: Applies to scripts/modules/utils.js : Use tagged task system aware functions for task finding and manipulation, handle both task and subtask operations, and validate task IDs before operations.

Applied to files:

  • packages/workflow-engine/src/task-execution/index.ts
📚 Learning: 2025-09-03T12:20:36.005Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/index.ts:56-57
Timestamp: 2025-09-03T12:20:36.005Z
Learning: The logger functionality in tm-core should only be available through the main package entry point (import { getLogger, createLogger, setGlobalLogger } from 'tm-core'), not as a separate subpath export like other modules such as auth or storage.

Applied to files:

  • packages/tm-core/src/index.ts
📚 Learning: 2025-07-31T20:49:04.638Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#997
File: apps/extension/package.publish.json:2-8
Timestamp: 2025-07-31T20:49:04.638Z
Learning: In the eyaltoledano/claude-task-master repository, the VS Code extension uses a 3-file packaging system where package.json (with name "extension") is for development within the monorepo, while package.publish.json (with name "task-master-hamster") contains the clean manifest for VS Code marketplace publishing. The different names are intentional and serve distinct purposes in the build and publishing workflow.

Applied to files:

  • packages/workflow-engine/package.json
📚 Learning: 2025-09-03T12:16:15.866Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/package.json:13-64
Timestamp: 2025-09-03T12:16:15.866Z
Learning: For internal packages in the claude-task-master project, Crunchyman-ralph prefers pointing package.json "types" entries to src .ts files rather than dist .d.ts files for better developer experience (DX), as the packages are not being exported as SDKs.

Applied to files:

  • packages/workflow-engine/package.json
  • packages/tm-core/package.json
📚 Learning: 2025-08-07T13:00:22.966Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1090
File: apps/extension/package.json:241-243
Timestamp: 2025-08-07T13:00:22.966Z
Learning: In monorepos, local packages should use "*" as the version constraint in package.json dependencies, as recommended by npm. This ensures the local version from within the same workspace is always used, rather than attempting to resolve from external registries. This applies to packages like task-master-ai within the eyaltoledano/claude-task-master monorepo.

Applied to files:

  • packages/workflow-engine/package.json
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/commands.js : Add help text to the command definition and update 'dev_workflow.mdc' with command reference when adding a new feature.

Applied to files:

  • apps/cli/src/commands/workflow.command.ts
📚 Learning: 2025-07-18T17:18:17.759Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-18T17:18:17.759Z
Learning: Applies to {scripts/modules/utils.js,mcp-server/src/core/utils/path-utils.js,mcp-server/src/tools/utils.js} : Keep utilities relevant to their location, export all utility functions in a single statement per file, group related exports together, export configuration constants, do not use default exports, and do not create circular dependencies.

Applied to files:

  • packages/tm-core/package.json
📚 Learning: 2025-09-02T21:51:41.383Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/auth/config.ts:0-0
Timestamp: 2025-09-02T21:51:41.383Z
Learning: In packages/tm-core/src/auth/config.ts, the BASE_DOMAIN configuration intentionally does not include runtime environment variable fallbacks like TM_BASE_DOMAIN or HAMSTER_BASE_URL. The maintainers prefer to keep these capabilities "hush hush" and undocumented, using only the build-time TM_PUBLIC_BASE_DOMAIN and the default value.

Applied to files:

  • packages/tm-core/tsup.config.ts
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/commands.js : Follow the established pattern in 'commands.js' for CLI command implementation, using Commander.js for argument parsing, including comprehensive help text and examples, and supporting tagged task context awareness.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:06:57.833Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_services.mdc:0-0
Timestamp: 2025-07-18T17:06:57.833Z
Learning: Applies to scripts/modules/task-manager/*.js : Do not import or call anything from the old `ai-services.js`, `ai-client-factory.js`, or `ai-client-utils.js` files.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:06:57.833Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_services.mdc:0-0
Timestamp: 2025-07-18T17:06:57.833Z
Learning: Applies to scripts/modules/commands.js : Do not import or call anything from the old `ai-services.js`, `ai-client-factory.js`, or `ai-client-utils.js` files.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:18:17.759Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-18T17:18:17.759Z
Learning: Applies to scripts/modules/utils.js : Place utilities used primarily by the core `task-master` CLI logic and command modules (`scripts/modules/*`) into `scripts/modules/utils.js`.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/commands.js : All new user-facing commands should be added to 'scripts/modules/commands.js'.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:19:27.365Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/.windsurfrules:0-0
Timestamp: 2025-07-18T17:19:27.365Z
Learning: Use the global `task-master` CLI command instead of directly invoking `node scripts/dev.js` for all task management operations.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-31T22:07:49.716Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/commands.mdc:0-0
Timestamp: 2025-07-31T22:07:49.716Z
Learning: Applies to scripts/modules/commands.js : For AI-powered commands that benefit from project context, use the ContextGatherer utility for multi-source context extraction, support task IDs, file paths, custom context, and project tree, implement fuzzy search for automatic task discovery, and display detailed token breakdown for transparency.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:19:27.365Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/.windsurfrules:0-0
Timestamp: 2025-07-18T17:19:27.365Z
Learning: Start new projects by running `task-master init` or `node scripts/dev.js parse-prd --input=<prd-file.txt>` to generate the initial tasks.json.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Extract tasks from PRD documents using AI, create them in the current tag context (defaulting to 'master'), provide clear prompts to guide AI task generation, and validate/clean up AI-generated tasks.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-20T01:35:05.831Z
Learnt from: rtmcrc
PR: eyaltoledano/claude-task-master#933
File: scripts/modules/task-manager/parse-prd.js:226-226
Timestamp: 2025-07-20T01:35:05.831Z
Learning: The parsePRD function in scripts/modules/task-manager/parse-prd.js has a different parameter structure than other task-manager functions - it uses `options` parameter instead of `context` parameter because it generates tasks from PRD documents rather than operating on existing tasks.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:16:32.622Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ui.mdc:0-0
Timestamp: 2025-07-18T17:16:32.622Z
Learning: Applies to scripts/modules/ui.js : Use cli-table3 for table rendering, include colored bold headers, and set appropriate column widths for readability

Applied to files:

  • apps/cli/src/utils/ui.ts
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to package.json : Add and update test scripts in package.json to include test, test:watch, test:coverage, test:unit, test:integration, test:e2e, and test:ci

Applied to files:

  • package.json
🧬 Code graph analysis (15)
apps/cli/src/commands/index.ts (4)
apps/cli/src/index.ts (4)
  • registerAllCommands (13-13)
  • ListTasksCommand (7-7)
  • AuthCommand (8-8)
  • WorkflowCommand (9-9)
apps/cli/src/commands/list.command.ts (1)
  • ListTasksCommand (48-324)
apps/cli/src/commands/auth.command.ts (1)
  • AuthCommand (32-503)
apps/cli/src/commands/workflow.command.ts (1)
  • WorkflowCommand (17-56)
packages/workflow-engine/src/process/process-sandbox.ts (3)
packages/workflow-engine/src/types/workflow.types.ts (3)
  • WorkflowProcess (80-95)
  • WorkflowEventType (65-78)
  • WorkflowEvent (56-63)
packages/workflow-engine/src/errors/workflow.errors.ts (1)
  • ProcessError (26-31)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (1)
  • cleanup (342-351)
packages/workflow-engine/src/types/workflow.types.ts (1)
packages/tm-core/src/workflow/index.ts (7)
  • WorkflowExecutionContext (10-10)
  • WorkflowStatus (11-11)
  • WorkflowEvent (12-12)
  • WorkflowEventType (13-13)
  • WorkflowProcess (14-14)
  • ProcessStatus (15-15)
  • WorktreeInfo (16-16)
apps/cli/src/commands/workflow/workflow-list.command.ts (3)
packages/workflow-engine/src/index.ts (1)
  • TaskExecutionManager (19-19)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (2)
  • TaskExecutionManager (43-433)
  • TaskExecutionManagerConfig (34-37)
packages/workflow-engine/src/types/workflow.types.ts (1)
  • WorkflowExecutionContext (19-44)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (5)
packages/workflow-engine/src/types/workflow.types.ts (4)
  • WorkflowConfig (6-17)
  • WorkflowEvent (56-63)
  • WorkflowExecutionContext (19-44)
  • WorkflowStatus (46-54)
packages/workflow-engine/src/worktree/worktree-manager.ts (2)
  • WorktreeManager (25-351)
  • WorktreeManagerConfig (12-19)
packages/workflow-engine/src/process/process-sandbox.ts (2)
  • ProcessSandbox (41-378)
  • ProcessSandboxConfig (15-24)
packages/workflow-engine/src/state/workflow-state-manager.ts (2)
  • WorkflowStateManager (47-320)
  • WorkflowStateConfig (15-20)
packages/workflow-engine/src/errors/workflow.errors.ts (3)
  • MaxConcurrentWorkflowsError (51-59)
  • WorkflowError (6-17)
  • WorkflowNotFoundError (44-49)
apps/cli/src/commands/workflow.command.ts (4)
apps/cli/src/commands/workflow/workflow-start.command.ts (1)
  • WorkflowStartCommand (32-239)
apps/cli/src/commands/workflow/workflow-list.command.ts (1)
  • WorkflowListCommand (28-253)
apps/cli/src/commands/workflow/workflow-stop.command.ts (1)
  • WorkflowStopCommand (26-260)
apps/cli/src/commands/workflow/workflow-status.command.ts (1)
  • WorkflowStatusCommand (26-339)
packages/tm-core/src/task-master-core.ts (1)
packages/tm-core/src/workflow/workflow-service.ts (2)
  • WorkflowServiceConfig (14-27)
  • WorkflowService (32-218)
packages/workflow-engine/src/worktree/worktree-manager.ts (3)
packages/workflow-engine/src/types/workflow.types.ts (1)
  • WorktreeInfo (104-119)
packages/workflow-engine/src/errors/workflow.errors.ts (1)
  • WorktreeError (19-24)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (1)
  • cleanup (342-351)
packages/tm-core/src/workflow/workflow-service.ts (4)
packages/workflow-engine/src/index.ts (1)
  • TaskExecutionManager (19-19)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (2)
  • TaskExecutionManager (43-433)
  • TaskExecutionManagerConfig (34-37)
packages/tm-core/src/task-master-core.ts (1)
  • getTask (150-152)
packages/workflow-engine/src/types/workflow.types.ts (1)
  • WorkflowExecutionContext (19-44)
scripts/modules/commands.js (2)
apps/cli/src/commands/index.ts (1)
  • registerAllCommands (21-38)
apps/cli/src/index.ts (1)
  • registerAllCommands (13-13)
apps/cli/src/utils/ui.ts (3)
scripts/modules/commands.js (1)
  • message (5329-5338)
tests/unit/ui.test.js (1)
  • chalk (17-17)
scripts/modules/task-manager/tag-management.js (1)
  • headers (612-612)
apps/cli/src/commands/workflow/workflow-start.command.ts (4)
packages/tm-core/src/index.ts (2)
  • TaskMasterCore (8-8)
  • createTaskMasterCore (9-9)
packages/tm-core/src/task-master-core.ts (3)
  • TaskMasterCore (43-223)
  • workflow (210-212)
  • createTaskMasterCore (228-232)
packages/workflow-engine/src/index.ts (1)
  • TaskExecutionManager (19-19)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (2)
  • TaskExecutionManager (43-433)
  • TaskExecutionManagerConfig (34-37)
apps/cli/src/commands/workflow/workflow-stop.command.ts (2)
packages/workflow-engine/src/index.ts (1)
  • TaskExecutionManager (19-19)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (2)
  • TaskExecutionManager (43-433)
  • TaskExecutionManagerConfig (34-37)
apps/cli/src/commands/workflow/workflow-status.command.ts (2)
packages/workflow-engine/src/index.ts (1)
  • TaskExecutionManager (19-19)
packages/workflow-engine/src/task-execution/task-execution-manager.ts (2)
  • TaskExecutionManager (43-433)
  • TaskExecutionManagerConfig (34-37)
packages/workflow-engine/src/state/workflow-state-manager.ts (2)
packages/workflow-engine/src/types/workflow.types.ts (3)
  • WorkflowStatus (46-54)
  • WorkflowExecutionContext (19-44)
  • WorkflowEvent (56-63)
packages/workflow-engine/src/errors/workflow.errors.ts (1)
  • WorkflowError (6-17)
🪛 GitHub Actions: CI
packages/workflow-engine/src/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/worktree/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/tsconfig.json

[error] 1-1: Biome format check failed. Detected formatting differences in this file. Run 'biome format .' to fix. (Triggered by 'npm run format-check').

packages/workflow-engine/src/types/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/process/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/types/workflow.types.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/state/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

apps/cli/src/commands/workflow/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/task-execution/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/tm-core/src/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

apps/cli/src/commands/workflow/workflow-list.command.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/errors/workflow.errors.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/tm-core/src/workflow/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/errors/index.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/tm-core/src/workflow/workflow-service.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

apps/cli/src/utils/ui.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

apps/cli/src/commands/workflow/workflow-start.command.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

apps/cli/src/commands/workflow/workflow-status.command.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

packages/workflow-engine/src/state/workflow-state-manager.ts

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🪛 markdownlint-cli2 (0.17.2)
packages/workflow-engine/README.md

17-17: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


319-319: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


328-328: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


337-337: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


371-371: Files should end with a single newline character

(MD047, single-trailing-newline)

🔇 Additional comments (22)
packages/workflow-engine/vitest.config.ts (2)

1-19: Vitest config looks good.

Node env, globals, and coverage reporters are reasonable defaults.


1-19: No action needed — Vitest scripts present.
packages/workflow-engine/package.json already defines "test": "vitest" and "test:watch": "vitest --watch".

packages/workflow-engine/tsup.config.ts (1)

4-10: Do not add 'src/workflow/index.ts' — file is missing.
packages/workflow-engine/src/workflow/index.ts is not present; either create that file if you intend to publish it, or do not include it in packages/workflow-engine/tsup.config.ts.

Likely an incorrect or invalid review comment.

package.json (1)

14-43: Scripts update aligns with testing guidelines.

test, test:watch, test:coverage, test:unit, test:integration, test:e2e, and test:ci are present.

packages/tm-core/tsup.config.ts (1)

17-49: Overall config changes LGTM.

Build-time TM_PUBLIC_* injection, entries (including workflow), and dual formats are solid.

packages/workflow-engine/src/task-execution/index.ts (1)

1-6: Run Biome formatter and confirm TS moduleResolution

  • packages/workflow-engine/src/task-execution/index.ts already ends with a newline; no EOF-newline change required — run Biome across the repo to pick up any remaining formatting failures.
  • tsconfig: root tsconfig.json and packages/build-config/tsconfig.json (and others) set compilerOptions.moduleResolution = "bundler" (module = "ESNext"), but packages/workflow-engine/tsconfig.json does not set these keys — confirm it extends a base tsconfig that provides "bundler" or explicitly add compilerOptions.moduleResolution = "bundler" (or "nodenext") so the .js import extension resolves correctly.
apps/cli/src/index.ts (2)

9-9: No change required — named export present.
apps/cli/src/commands/workflow.command.ts declares export class WorkflowCommand ... and also export default WorkflowCommand, so the current export { WorkflowCommand } from './commands/workflow.command.js'; is valid.

Likely an incorrect or invalid review comment.


12-14: Registry export is pure — no top-level registration on import
apps/cli/src/commands/index.ts exports registerAllCommands(program: Command) and contains no top-level program.command(...) calls; registration occurs inside the exported function.

packages/tm-core/src/task-master-core.ts (4)

19-22: Workflow integration API surface LGTM.

Importing WorkflowService/Config and exposing partial options is consistent and non-breaking.

Also applies to: 30-31


46-47: Member initialization is sound.

Private member + factory create() ensures proper init before use.

Also applies to: 69-70


207-213: Accessor reads well.

Getter naming and visibility are fine.


219-222: Dispose workflow service on close LGTM.

Cleans up listeners and engine; matches lifecycle expectations.

packages/tm-core/package.json (3)

52-54: Utils subpath export addition LGTM.

Providing a require target for utils improves interop.


55-60: Workflow subpath export LGTM.

Clear pathing for types/import/require.


76-79: Verify dependency intent & pinning

  • "@tm/workflow-engine" is set to "" in packages/tm-core/package.json — replace with a workspace range (e.g. "workspace:") or an explicit semver to avoid registry drift/failures.
  • "chalk" is actually used (packages/tm-core/src/logger/logger.ts:5 — import chalk from 'chalk') — do not remove unless you also remove/replace the usage.
  • "@supabase/supabase-js" (^2.57.0) is imported in packages/tm-core/src/auth/supabase-session-storage.ts, src/storage/api-storage.ts, src/services/organization.service.ts, src/repositories/supabase-task-repository.ts, src/clients/supabase-client.ts — confirm ^2.57.0 is compatible with these auth/storage/client codepaths and run tests.
apps/cli/src/commands/workflow.command.ts (1)

50-54: Registration API LGTM.

Static register pattern aligns with the registry.

apps/cli/src/commands/workflow/workflow-stop.command.ts (1)

147-155: Ensure spinner is stopped in all cases.

If ui.displaySpinner returns a stop/dispose function, use try/finally to avoid a stuck spinner on failure.

If applicable, update like:

-    ui.displaySpinner('Stopping workflow and cleaning up resources...');
-
-    await this.workflowManager!.stopTaskExecution(
-      idToStop,
-      options.force
-    );
+    const stop = ui.displaySpinner('Stopping workflow and cleaning up resources...');
+    try {
+      await this.workflowManager!.stopTaskExecution(idToStop, options.force);
+    } finally {
+      stop?.();
+    }

And similarly for "Stopping all workflows...".

Also applies to: 198-201

apps/cli/src/commands/workflow/workflow-list.command.ts (1)

1-5: CI failing on Biome format.

Run npm run format locally to unblock CI. If issues persist, I can auto-apply fixes.

apps/cli/src/commands/workflow/workflow-start.command.ts (1)

141-148: Confirm timeout units.

defaultTimeout in engine vs per-run timeout here: both appear to be minutes. Verify engine expects minutes to avoid premature/overlong timeouts.

packages/workflow-engine/package.json (2)

8-29: TS export types mapping: ensure tsconfig uses NodeNext/Node16.

Using "exports" with "types" entries requires TS NodeNext/Node16 moduleResolution. Verify repo tsconfig; otherwise types for subpaths may not resolve.


37-39: No runtime circular dependency detected — only type-only imports are used.

workflow-engine only imports types from @tm/core (import type { Task } from '@tm/core' in packages/workflow-engine/src/task-execution/task-execution-manager.ts:130). tm-core's references to @tm/workflow-engine are type-only (packages/tm-core/src/workflow/index.ts:85; packages/tm-core/src/workflow/workflow-service.ts:93).

packages/workflow-engine/src/worktree/worktree-manager.ts (1)

6-8: Keep 'node:' scheme imports (match repo style)

Repo predominantly uses 'node:' scheme imports (30 occurrences) vs bare core imports (10); leave packages/workflow-engine/src/worktree/worktree-manager.ts lines 6–8 unchanged.

Likely an incorrect or invalid review comment.

},
"dependencies": {
"@tm/core": "*",
"@tm/workflow-engine": "*",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use workspace protocol for internal dep instead of "*"

"" makes builds non-reproducible. If you’re using pnpm/yarn workspaces, prefer workspace: for local linking.

Apply:

-    "@tm/workflow-engine": "*",
+    "@tm/workflow-engine": "workspace:*",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@tm/workflow-engine": "*",
"@tm/workflow-engine": "workspace:*",
🤖 Prompt for AI Agents
In apps/cli/package.json around line 31, the dependency version uses "*" which
makes builds non-reproducible; change the version to the workspace protocol by
replacing "*" with "workspace:*" for the internal package "@tm/workflow-engine".
Update the package.json dependency entry accordingly and run your package
manager install (pnpm/yarn) to verify the workspace link resolves.

Comment on lines +21 to +38
export function registerAllCommands(program: Command): void {
// Get all exports from this module
const commands = [
ListTasksCommand,
AuthCommand,
WorkflowCommand
// Add new commands here as they're imported above
];

commands.forEach((CommandClass) => {
if (
'register' in CommandClass &&
typeof CommandClass.register === 'function'
) {
CommandClass.register(program);
}
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Registry mechanism LGTM; tweak comment for clarity.

The comment says “Get all exports from this module” but the list is explicit. Consider rewording to avoid confusion.

- // Get all exports from this module
+ // Register explicitly listed command classes
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function registerAllCommands(program: Command): void {
// Get all exports from this module
const commands = [
ListTasksCommand,
AuthCommand,
WorkflowCommand
// Add new commands here as they're imported above
];
commands.forEach((CommandClass) => {
if (
'register' in CommandClass &&
typeof CommandClass.register === 'function'
) {
CommandClass.register(program);
}
});
}
export function registerAllCommands(program: Command): void {
// Register explicitly listed command classes
const commands = [
ListTasksCommand,
AuthCommand,
WorkflowCommand
// Add new commands here as they're imported above
];
commands.forEach((CommandClass) => {
if (
'register' in CommandClass &&
typeof CommandClass.register === 'function'
) {
CommandClass.register(program);
}
});
}
🤖 Prompt for AI Agents
In apps/cli/src/commands/index.ts around lines 21 to 38, the inline comment "Get
all exports from this module" is misleading because the commands array is
explicit; change the comment to accurately reflect that this is an explicit list
of imported commands (e.g., "List of command classes to register" or "Explicitly
registered commands") so readers won't expect dynamic export collection; update
the comment text accordingly.

Comment on lines +28 to +45
private addSubcommands(): void {
// Start workflow
WorkflowStartCommand.register(this);

// List workflows
WorkflowListCommand.register(this);

// Stop workflow
WorkflowStopCommand.register(this);

// Show workflow status
WorkflowStatusCommand.register(this);

// Alias commands for convenience
this.addCommand(new WorkflowStartCommand('run')); // tm workflow run <task-id>
this.addCommand(new WorkflowStopCommand('kill')); // tm workflow kill <workflow-id>
this.addCommand(new WorkflowStatusCommand('info')); // tm workflow info <workflow-id>
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Route subcommands through tm-core WorkflowService (avoid direct engine usage).

Start/list/stop/status currently instantiate TaskExecutionManager directly (see subcommand files). Prefer tmCore.workflow to keep a single abstraction and future-proof engine internals.

I can provide diffs for workflow-*.command.ts to use:

  • const core = await createTaskMasterCore({ projectPath: projectRoot });
  • const workflow = core.workflow;
  • Replace engine calls (startTaskExecution/stopTaskExecution/etc.) with workflow.start()/stop()/getStatus()/list().
    Would you like me to generate those changes?

⚠️ Potential issue

Fix Chalk usage in subcommands.

workflow-list.command.ts and workflow-status.command.ts use chalk.orange which doesn’t exist in Chalk v5. Use chalk.hex('#FFA500') or chalk.keyword('orange').

Example:

- paused: { icon: '⏸️', color: chalk.orange },
+ paused: { icon: '⏸️', color: chalk.hex('#FFA500') },

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow.command.ts around lines 28 to 45, the review
flags Chalk v5 usage in related subcommand files: update
workflow-list.command.ts and workflow-status.command.ts to stop using the
nonexistent chalk.orange; replace any chalk.orange occurrences with
chalk.hex('#FFA500') or chalk.keyword('orange') (choose one consistent style),
import chalk if missing, and run a quick build/lint to ensure types match and no
remaining chalk.orange usages remain.

Comment on lines +1 to +9
/**
* @fileoverview Workflow Commands
* Exports for all workflow-related CLI commands
*/

export * from './workflow-start.command.js';
export * from './workflow-list.command.js';
export * from './workflow-stop.command.js';
export * from './workflow-status.command.js'; No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Format to satisfy Biome.

Run the formatter; CI shows a Biome format failure on this file.

🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow/index.ts lines 1-9 the file fails Biome
formatting in CI; run the project formatter to fix whitespace/format issues.
Update the file by running the project's Biome formatter (e.g., biome format) or
the repo's formatting npm script (npm run format) so the file is reformatted to
match the code style, then stage and commit the formatted file.


import { Command } from 'commander';
import chalk from 'chalk';
import path from 'node:path';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Use bare Node core import to match maintainer preference.

Per team preference, switch from node: scheme to bare module name.

-import path from 'node:path';
+import path from 'path';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import path from 'node:path';
import path from 'path';
🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow/workflow-list.command.ts around line 8, the
import uses the node: scheme ("node:path"); change it to the bare core module
name ("path") to match project convention — update the import declaration
accordingly and run a quick lint/tsc to ensure no other references rely on the
node: prefix.

Comment on lines +1 to +19
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"dist",
"node_modules",
"**/*.test.ts",
"**/*.spec.ts"
]
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Biome formatting to unblock CI.

CI fails on this file. Run: npm run format.

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: Biome format check failed. Detected formatting differences in this file. Run 'biome format .' to fix. (Triggered by 'npm run format-check').

🤖 Prompt for AI Agents
packages/workflow-engine/tsconfig.json lines 1-19: the file is misformatted
causing CI to fail; run the project's formatter to apply Biome formatting rules
(execute npm run format from the repo root), then stage and commit the
reformatted tsconfig.json so CI picks up the corrected formatting.

Comment on lines +3 to +9
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Optional: add tsbuildinfo and composite for faster, safer incremental builds.

This keeps TS build artifacts package-local and enables project references.

   "compilerOptions": {
     "outDir": "dist",
     "rootDir": "src",
     "declaration": true,
     "declarationMap": true,
-    "sourceMap": true
+    "sourceMap": true,
+    "composite": true,
+    "tsBuildInfoFile": "dist/.tsbuildinfo"
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"composite": true,
"tsBuildInfoFile": "dist/.tsbuildinfo"
},
🤖 Prompt for AI Agents
In packages/workflow-engine/tsconfig.json around lines 3 to 9, enable
project-reference friendly incremental builds by setting "composite": true and
"incremental": true in compilerOptions and add a package-local tsBuildInfo file
(e.g. "tsBuildInfoFile": "./.tsbuildinfo"); ensure "declaration" and
"declarationMap" remain enabled (required by composite) so the project can be
safely used in project references and produce local build artifacts.

Comment on lines +11 to +17
format: ['esm'],
dts: true,
sourcemap: true,
clean: true,
splitting: false,
treeshake: true
}); No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider externalizing node_modules to keep the library lean.

Prevents bundling deps and aligns with tm-core tsup config.

   treeshake: true
-});
+  ,
+  external: [/^[^./]/]
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
format: ['esm'],
dts: true,
sourcemap: true,
clean: true,
splitting: false,
treeshake: true
});
format: ['esm'],
dts: true,
sourcemap: true,
clean: true,
splitting: false,
treeshake: true,
external: [/^[^./]/]
});
🤖 Prompt for AI Agents
In packages/workflow-engine/tsup.config.ts around lines 11 to 17, the config
currently bundles node_modules which inflates the library; modify the tsup
config to externalize dependencies (and peerDependencies) from package.json so
node_modules are not bundled—read package.json deps/peerDeps and set tsup's
external option (or use an array of package names) to exclude them, keeping
existing options (format, dts, sourcemap, clean, splitting, treeshake) intact.

Comment on lines +18 to 20
// Import command registry from @tm/cli
import { registerAllCommands } from '@tm/cli';

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid top-level import of @tm/cli; use dynamic import to prevent init-order/circular issues.

Guidelines recommend dynamic imports in modules. Also ensures graceful behavior if @tm/cli isn’t present.

-import { registerAllCommands } from '@tm/cli';
+// Defer loading the CLI registry to avoid init-order issues
+let __registerAllCommands;
+async function __loadCliRegistry() {
+  if (!__registerAllCommands) {
+    __registerAllCommands = (await import('@tm/cli')).registerAllCommands;
+  }
+  return __registerAllCommands;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Import command registry from @tm/cli
import { registerAllCommands } from '@tm/cli';
// Import command registry from @tm/cli
// Defer loading the CLI registry to avoid init-order issues
let __registerAllCommands;
async function __loadCliRegistry() {
if (!__registerAllCommands) {
__registerAllCommands = (await import('@tm/cli')).registerAllCommands;
}
return __registerAllCommands;
}
🤖 Prompt for AI Agents
In scripts/modules/commands.js around lines 18 to 20, replace the top-level
static import of @tm/cli with a dynamic import to avoid init-order and circular
dependency issues: remove the static import and instead load the module at
runtime using import('@tm/cli') inside an async function or initialization path,
wrap the dynamic import in try/catch to handle the module not being present and
log or no-op gracefully, and then call registerAllCommands from the resolved
module (or export an async initializer that performs this) so nothing depends on
@tm/cli at module-evaluation time.

💡 Verification agent

🧩 Analysis chain

Verify dependency and export wiring for @tm/cli.

Ensure the package that ships scripts/modules declares @tm/cli as a dependency and that apps/cli/src/index.ts re-exports registerAllCommands.

Also applies to: 1740-1743


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Check that the root package (hosting scripts/) depends on @tm/cli
jq -r '.name,.dependencies["@tm/cli"],.devDependencies["@tm/cli"]' package.json
# Verify export exists
rg -n "export\\s+\\{?\\s*registerAllCommands\\s*\\}?\\s+from" apps/cli/src/index.ts || rg -n "export\\s+(const|function)\\s+registerAllCommands" apps/cli/src/index.ts

Length of output: 86


Add @tm/cli to package.json dependencies

Root package "task-master-ai" does not list @tm/cli in package.json, while apps/cli/src/index.ts already re-exports registerAllCommands (apps/cli/src/index.ts:13). Add @tm/cli to the package that ships scripts/modules (root package.json or as a workspace dependency) so the import resolves at build/publish time.

🤖 Prompt for AI Agents
In scripts/modules/commands.js around lines 18 to 20, the import from '@tm/cli'
will fail because the root package.json for task-master-ai does not list @tm/cli
as a dependency; add "@tm/cli" to the package.json dependencies (or as a
workspace dependency) for the package that ships scripts/modules, pinning the
version to the local workspace package (e.g. "workspace:*" or the matching
version from apps/cli) or an appropriate published version, then run your
package manager install to update lockfiles and verify the import resolves at
build/publish time.

Comment on lines +1740 to 1743
// Register all commands from @tm/cli using the command registry
// This automatically registers ListTasksCommand, AuthCommand, and any future commands
registerAllCommands(programInstance);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Guard registry call and surface actionable error if @tm/cli is missing.

Prevents runtime crash with a clearer message.

-// This automatically registers ListTasksCommand, AuthCommand, and any future commands
-registerAllCommands(programInstance);
+// This automatically registers ListTasksCommand, AuthCommand, and any future commands
+try {
+  const registerAllCommands = await __loadCliRegistry();
+  registerAllCommands(programInstance);
+} catch (e) {
+  console.error(chalk.red('Failed to load @tm/cli command registry.'));
+  console.error(chalk.yellow('Ensure @tm/cli is installed and exported correctly.'));
+}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In scripts/modules/commands.js around lines 1740 to 1743, the call
registerAllCommands(programInstance) can throw if the @tm/cli module (or the
registry exporter) is missing; wrap the import/registry call in a try/catch and
detect the specific module-not-found error (or absence of registerAllCommands),
then log or throw an actionable error message instructing the operator to
install @tm/cli (e.g., "Missing dependency '@tm/cli'. Run `npm install @tm/cli`
or check your build."), and bail gracefully (return/exit) instead of allowing an
uncaught runtime crash. Ensure the guard handles both require/import failure and
a falsy/missing export before invoking registerAllCommands.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment on lines +202 to +216
private getStatusDisplay(status: string): string {
const statusMap = {
pending: { icon: '⏳', color: chalk.yellow },
initializing: { icon: '🔄', color: chalk.blue },
running: { icon: '🚀', color: chalk.green },
paused: { icon: '⏸️', color: chalk.orange },
completed: { icon: '✅', color: chalk.green },
failed: { icon: '❌', color: chalk.red },
cancelled: { icon: '🛑', color: chalk.gray },
timeout: { icon: '⏰', color: chalk.red }
};

const statusInfo = statusMap[status as keyof typeof statusMap] || { icon: '❓', color: chalk.white };
return `${statusInfo.icon} ${statusInfo.color(status)}`;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix invalid Chalk color (chalk.orange is not a function).

Chalk has no orange method; this will throw at runtime. Use hex or reuse yellow.

   private getStatusDisplay(status: string): string {
     const statusMap = {
       pending: { icon: '⏳', color: chalk.yellow },
       initializing: { icon: '🔄', color: chalk.blue },
       running: { icon: '🚀', color: chalk.green },
-      paused: { icon: '⏸️', color: chalk.orange },
+      paused: { icon: '⏸️', color: chalk.hex('#FFA500') },
       completed: { icon: '✅', color: chalk.green },
       failed: { icon: '❌', color: chalk.red },
       cancelled: { icon: '🛑', color: chalk.gray },
       timeout: { icon: '⏰', color: chalk.red }
     };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private getStatusDisplay(status: string): string {
const statusMap = {
pending: { icon: '⏳', color: chalk.yellow },
initializing: { icon: '🔄', color: chalk.blue },
running: { icon: '🚀', color: chalk.green },
paused: { icon: '⏸️', color: chalk.orange },
completed: { icon: '✅', color: chalk.green },
failed: { icon: '❌', color: chalk.red },
cancelled: { icon: '🛑', color: chalk.gray },
timeout: { icon: '⏰', color: chalk.red }
};
const statusInfo = statusMap[status as keyof typeof statusMap] || { icon: '❓', color: chalk.white };
return `${statusInfo.icon} ${statusInfo.color(status)}`;
}
private getStatusDisplay(status: string): string {
const statusMap = {
pending: { icon: '⏳', color: chalk.yellow },
initializing: { icon: '🔄', color: chalk.blue },
running: { icon: '🚀', color: chalk.green },
paused: { icon: '⏸️', color: chalk.hex('#FFA500') },
completed: { icon: '✅', color: chalk.green },
failed: { icon: '❌', color: chalk.red },
cancelled: { icon: '🛑', color: chalk.gray },
timeout: { icon: '⏰', color: chalk.red }
};
const statusInfo = statusMap[status as keyof typeof statusMap] || { icon: '❓', color: chalk.white };
return `${statusInfo.icon} ${statusInfo.color(status)}`;
}
🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow/workflow-list.command.ts around lines 202 to
216, the status map uses chalk.orange which doesn't exist and will throw at
runtime; replace chalk.orange with a valid Chalk call (e.g.,
chalk.keyword('orange') or chalk.hex('#FFA500')) or use an existing color like
chalk.yellow, updating the paused entry to use that function so the returned
status string calls a real Chalk color function.

Comment on lines +228 to +240
private formatDuration(start: Date, end: Date): string {
const diff = end.getTime() - start.getTime();
const minutes = Math.floor(diff / (1000 * 60));
const hours = Math.floor(minutes / 60);

if (hours > 0) {
return `${hours}h ${minutes % 60}m`;
} else if (minutes > 0) {
return `${minutes}m`;
} else {
return '<1m';
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Defensive date handling to avoid crashes if dates are strings.

State may revive dates as strings. Coerce to Date before math.

-  private formatDuration(start: Date, end: Date): string {
-    const diff = end.getTime() - start.getTime();
+  private formatDuration(start: Date | string, end: Date | string): string {
+    const s = start instanceof Date ? start : new Date(start);
+    const e = end instanceof Date ? end : new Date(end);
+    const diff = e.getTime() - s.getTime();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private formatDuration(start: Date, end: Date): string {
const diff = end.getTime() - start.getTime();
const minutes = Math.floor(diff / (1000 * 60));
const hours = Math.floor(minutes / 60);
if (hours > 0) {
return `${hours}h ${minutes % 60}m`;
} else if (minutes > 0) {
return `${minutes}m`;
} else {
return '<1m';
}
}
private formatDuration(start: Date | string, end: Date | string): string {
const s = start instanceof Date ? start : new Date(start);
const e = end instanceof Date ? end : new Date(end);
const diff = e.getTime() - s.getTime();
const minutes = Math.floor(diff / (1000 * 60));
const hours = Math.floor(minutes / 60);
if (hours > 0) {
return `${hours}h ${minutes % 60}m`;
} else if (minutes > 0) {
return `${minutes}m`;
} else {
return '<1m';
}
}
🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow/workflow-list.command.ts around lines 228 to
240, the formatDuration function assumes start and end are Date objects and will
crash if they are strings; convert/coerce start and end to Date instances (e.g.,
new Date(start)) before doing getTime(), validate that resulting Dates are valid
(isNaN(date.getTime()) check) and handle invalid/missing dates by returning a
safe fallback like '<invalid>' or '<unknown>' so the function never throws.


import { Command } from 'commander';
import chalk from 'chalk';
import path from 'node:path';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Use bare Node core import to match maintainer preference.

Align with team preference.

-import path from 'node:path';
+import path from 'path';
🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow/workflow-start.command.ts around line 8, the
import uses the prefixed core module "node:path"; change it to the bare Node
core import "path" to match project style and maintainer preference (replace
"import path from 'node:path';" with "import path from 'path';").

Comment on lines +19 to +27
export interface WorkflowStartOptions {
project?: string;
branch?: string;
timeout?: number;
worktreeBase?: string;
claude?: string;
debug?: boolean;
env?: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Timeout option typing is inconsistent with Commander.

Commander provides strings; interface says number. Fix typing to string and keep parseInt at use sites.

 export interface WorkflowStartOptions {
   project?: string;
   branch?: string;
-  timeout?: number;
+  timeout?: string;
   worktreeBase?: string;
   claude?: string;
   debug?: boolean;
   env?: string;
 }

Also applies to: 43-44, 95-97, 141-148

🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow/workflow-start.command.ts around lines 19-27
(and also adjust usages at 43-44, 95-97, 141-148), the
WorkflowStartOptions.timeout is declared as number but Commander passes option
values as strings; change the timeout type to string in the interface and at all
related option declarations/usages, and update code sites that need numeric
timeout to call parseInt (or Number) where the value is consumed so runtime
behavior remains the same.

Comment on lines +91 to +99
ui.displaySpinner('Creating worktree and starting Claude Code process...');

const workflowId = await this.workflowManager!.startTaskExecution(task, {
branchName: options.branch,
timeout: parseInt(options.timeout || '60'),
env
});

const workflow = this.workflowManager!.getWorkflowStatus(workflowId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Ensure spinner stops on both success and error paths.

If ui.displaySpinner() starts a persistent spinner, make sure it is stopped/cleared before printing success lines and in the catch block.

I can adapt this to a withSpinner helper if you share ui utils.

Also applies to: 101-107

🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In apps/cli/src/commands/workflow/workflow-start.command.ts around lines 91-99
(and similarly 101-107), the persistent spinner started by
ui.displaySpinner(...) is not guaranteed to be stopped on either the success or
error path; wrap the workflow start and subsequent status handling in a
try/finally (or use a withSpinner helper) so that ui.stopSpinner() (or the
appropriate ui.clearSpinner()) is called in the finally block before any console
output, and also call it in the catch path if you keep separate error handling,
ensuring the spinner is stopped prior to printing success or error messages.

for (const workflow of workflows) {
const isStale =
workflow.status === 'running' &&
!this.processSandbox.isProcessRunning(`workflow-${workflow.taskId}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Fix incorrect workflow ID in stale workflow detection

The isProcessRunning check uses workflow-${workflow.taskId} but the actual workflow IDs are generated differently by generateWorkflowId() in the state manager (format: workflow-${taskId}-${timestamp}-${random}).

Let me verify the workflow ID format:

Based on the state manager implementation, the fix should be:

       const isStale =
         workflow.status === 'running' &&
-        !this.processSandbox.isProcessRunning(`workflow-${workflow.taskId}`);
+        !this.processSandbox.isProcessRunning(workflowId);

You'll also need to iterate over workflows with their IDs. Consider refactoring the state manager to return a Map or array of tuples instead of just the contexts.


🏁 Script executed:

#!/bin/bash
# Check the generateWorkflowId implementation in the state manager
ast-grep --pattern 'generateWorkflowId($_) {
  $$$
}'

Length of output: 0


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo ">>> Searching for generateWorkflowId occurrences"
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore -S "generateWorkflowId" -C3 || true
  rg -n --hidden --no-ignore -S "generateWorkflowId\(" -C3 || true
else
  grep -RIn "generateWorkflowId" . || true
fi

echo
echo ">>> Searching for workflowId variable/usages"
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore -S "workflowId\b" -C3 || true
else
  grep -RIn "workflowId" . || true
fi

echo
echo ">>> Searching for isProcessRunning calls"
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore -S "isProcessRunning\(" -C3 || true
else
  grep -RIn "isProcessRunning(" . || true
fi

echo
echo ">>> Searching for 'workflow-' string/template usages (shows nearby context)"
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore -S "workflow-" -C1 --max-columns=200 || true
else
  grep -RIn --line-number "workflow-" . || true
fi

echo
echo ">>> Attempting to locate and print task-execution-manager file (first match)"
if command -v rg >/dev/null 2>&1; then
  file=$(rg --files | rg -i "task-execution-manager" | head -n1 || true)
else
  file=$(find . -type f -iname "*task-execution-manager*.ts" | head -n1 || true)
fi

if [ -n "$file" ]; then
  echo "Found: $file"
  echo "----- $file (lines ~360-410) -----"
  sed -n '360,410p' "$file" || true
else
  echo "task-execution-manager file not found by name search"
fi

Length of output: 103989


Fix stale-workflow detection — use actual workflow IDs (not workflow-${taskId})

generateWorkflowId() returns workflow-${taskId}-${timestamp}-${random} (packages/workflow-engine/src/state/workflow-state-manager.ts:315-318). cleanupStaleWorkflows builds workflow-${workflow.taskId} which will not match active process keys; this will miss/incorrectly mark running processes.

Suggested change (still relevant):

      const isStale =
        workflow.status === 'running' &&
-       !this.processSandbox.isProcessRunning(`workflow-${workflow.taskId}`);
+       !this.processSandbox.isProcessRunning(workflowId);

Actions required:

  • Iterate workflows with their IDs (don’t rebuild IDs from taskId). Update the loop in packages/workflow-engine/src/task-execution/task-execution-manager.ts (around lines 390–401) to iterate [workflowId, workflow] so you can call isProcessRunning(workflowId).
  • Update the subsequent updateWorkflowStatus call to use workflowId (not workflow-${workflow.taskId}).
  • Preferably refactor the state manager API to return entries (Map or array of [workflowId, context]) or add a helper (e.g., listWorkflowEntries()) to avoid reconstructing IDs.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/workflow-engine/src/task-execution/task-execution-manager.ts around
lines 390 to 401, the cleanup loop reconstructs process IDs as
`workflow-${workflow.taskId}` which doesn't match actual generated workflow IDs;
change the iteration to use workflow entries (iterate [workflowId, workflow]) so
you can call this.processSandbox.isProcessRunning(workflowId) directly, and pass
workflowId (not the reconstructed string) into updateWorkflowStatus; optionally
adjust the state manager to return Map/entries or add a helper like
listWorkflowEntries() so callers receive [workflowId, context] pairs instead of
rebuilding IDs.

Comment on lines +400 to +401
`workflow-${workflow.taskId}`,
'failed'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Pass correct workflow ID to updateWorkflowStatus

Similar to the issue above, this uses an incorrect workflow ID format.

The workflow ID should be obtained from the state manager's internal map. You need to modify the iteration to get both the workflow ID and context:

-          await this.stateManager.updateWorkflowStatus(
-            `workflow-${workflow.taskId}`,
-            'failed'
-          );
+          // Need to get the actual workflow ID from state manager
+          // This requires enhancing the state manager API

Consider adding a method to the state manager like listWorkflowsWithIds() that returns an array of { id: string, context: WorkflowExecutionContext }.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
packages/workflow-engine/src/task-execution/task-execution-manager.ts around
lines 400-401: the code currently passes a fabricated workflow ID
(`workflow-${workflow.taskId}`) to updateWorkflowStatus; instead iterate the
state manager map to obtain both the real workflow ID and its
WorkflowExecutionContext (e.g., add or use a method like
stateManager.listWorkflowsWithIds() that returns {id, context}[]), then call
updateWorkflowStatus with the real id and 'failed'; implement
listWorkflowsWithIds() on the state manager if missing and replace the current
iteration to destructure { id, context } so the correct id is used.

Comment on lines +1 to +119
/**
* @fileoverview Workflow Engine Types
* Core types for workflow execution system
*/

export interface WorkflowConfig {
/** Maximum number of concurrent workflows */
maxConcurrent: number;
/** Default timeout for workflow execution (minutes) */
defaultTimeout: number;
/** Base directory for worktrees */
worktreeBase: string;
/** Claude Code executable path */
claudeExecutable: string;
/** Enable debug logging */
debug: boolean;
}

export interface WorkflowExecutionContext {
/** Task ID being executed */
taskId: string;
/** Task title for display */
taskTitle: string;
/** Full task description */
taskDescription: string;
/** Task implementation details */
taskDetails?: string;
/** Project root path */
projectRoot: string;
/** Worktree path */
worktreePath: string;
/** Branch name for this workflow */
branchName: string;
/** Process ID of running Claude Code */
processId?: number;
/** Workflow start time */
startedAt: Date;
/** Workflow status */
status: WorkflowStatus;
/** Last activity timestamp */
lastActivity: Date;
/** Execution metadata */
metadata?: Record<string, any>;
}

export type WorkflowStatus =
| 'pending' // Created but not started
| 'initializing' // Setting up worktree/process
| 'running' // Active execution
| 'paused' // Temporarily stopped
| 'completed' // Successfully finished
| 'failed' // Error occurred
| 'cancelled' // User cancelled
| 'timeout'; // Exceeded time limit

export interface WorkflowEvent {
type: WorkflowEventType;
workflowId: string;
taskId: string;
timestamp: Date;
data?: any;
error?: Error;
}

export type WorkflowEventType =
| 'workflow.created'
| 'workflow.started'
| 'workflow.paused'
| 'workflow.resumed'
| 'workflow.completed'
| 'workflow.failed'
| 'workflow.cancelled'
| 'worktree.created'
| 'worktree.deleted'
| 'process.started'
| 'process.stopped'
| 'process.output'
| 'process.error';

export interface WorkflowProcess {
/** Process ID */
pid: number;
/** Command that was executed */
command: string;
/** Command arguments */
args: string[];
/** Working directory */
cwd: string;
/** Environment variables */
env?: Record<string, string>;
/** Process start time */
startedAt: Date;
/** Process status */
status: ProcessStatus;
}

export type ProcessStatus =
| 'starting'
| 'running'
| 'stopped'
| 'crashed'
| 'killed';

export interface WorktreeInfo {
/** Worktree path */
path: string;
/** Branch name */
branch: string;
/** Creation timestamp */
createdAt: Date;
/** Associated task ID */
taskId: string;
/** Git commit hash */
commit?: string;
/** Worktree lock status */
locked: boolean;
/** Lock reason if applicable */
lockReason?: string;
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Type source of truth.
Confirm tm-core re-exports these types rather than defining divergent copies to avoid drift.

If tm-core also defines these, consider re-exporting from a single package (e.g., workflow-engine) and updating imports.

🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In packages/workflow-engine/src/types/workflow.types.ts lines 1-119, the PR
reviewer asks you to ensure these types are the single source of truth and not
duplicated in tm-core; verify whether tm-core currently defines identical types
and if so remove its duplicate definitions and have tm-core re-export these
types from workflow-engine (or move types into a shared package and update
imports accordingly). Update tm-core imports to re-export or import from the
chosen canonical package, run TypeScript build to catch any path/type errors,
and adjust any import paths across the repo so all references use the single
exported type definitions.

💡 Verification agent

🧩 Analysis chain

CI: Biome format check is failing.
Run formatting to satisfy CI.


🏁 Script executed:

#!/bin/bash
npm run format
npm run format-check

Length of output: 2977


CI: Biome format check failing — invalid biome.json keys

Running npm run format fails: biome.json contains unknown keys (files.ignore at line 3; linter.include and linter.ignore at lines 28–29). Fix biome.json to use the Biome schema (e.g., replace include with includes and remove/replace unsupported ignore entries) and re-run formatting.

🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In packages/workflow-engine/src/types/workflow.types.ts lines 1–119: CI fails
due to invalid keys in the repo biome.json (files.ignore and
linter.include/linter.ignore) — open the project's biome.json and fix the schema
to match Biome's expected keys: replace any top-level or nested "include" with
"includes", remove unsupported "files.ignore" and "linter.ignore" entries (or
convert them to the supported "files.excludes" / "linter.excludes" key names if
appropriate), and ensure linter configuration uses the correct "linter" object
keys per Biome docs; save and re-run npm run format to confirm the
formatter/linter passes.

Comment on lines +46 to +55
export type WorkflowStatus =
| 'pending' // Created but not started
| 'initializing' // Setting up worktree/process
| 'running' // Active execution
| 'paused' // Temporarily stopped
| 'completed' // Successfully finished
| 'failed' // Error occurred
| 'cancelled' // User cancelled
| 'timeout'; // Exceeded time limit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Export active workflow status set for consistent reuse.
Avoid scattering the active-status list across modules.

 export type WorkflowStatus = 
   | 'pending'     // Created but not started
   | 'initializing' // Setting up worktree/process
   | 'running'     // Active execution
   | 'paused'      // Temporarily stopped
   | 'completed'   // Successfully finished
   | 'failed'      // Error occurred
   | 'cancelled'   // User cancelled
   | 'timeout';    // Exceeded time limit
+
+export const ACTIVE_WORKFLOW_STATUSES = ['pending','initializing','running','paused'] as const;
+export type ActiveWorkflowStatus = typeof ACTIVE_WORKFLOW_STATUSES[number];
🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In packages/workflow-engine/src/types/workflow.types.ts around lines 46–55, add
and export a single source-of-truth constant (e.g. ACTIVE_WORKFLOW_STATUSES)
containing the set of statuses considered "active" (pending, initializing,
running, paused) so other modules can import it instead of duplicating the list;
make it a readonly array/tuple and export it from this file for consistent reuse
across the codebase.

Comment on lines +88 to +91
cwd: string;
/** Environment variables */
env?: Record<string, string>;
/** Process start time */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix env type to match Node’s ProcessEnv.
process.env values can be string | undefined; current type is too narrow.

-  env?: Record<string, string>;
+  env?: Record<string, string | undefined>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cwd: string;
/** Environment variables */
env?: Record<string, string>;
/** Process start time */
cwd: string;
/** Environment variables */
env?: Record<string, string | undefined>;
/** Process start time */
🧰 Tools
🪛 GitHub Actions: CI

[error] Biome format check failed: code is not formatted according to Biome rules. Step: 'npm run format-check'.

🤖 Prompt for AI Agents
In packages/workflow-engine/src/types/workflow.types.ts around lines 88 to 91,
the env property is currently typed as Record<string, string> which is too
narrow compared to Node’s process.env; update the type to NodeJS.ProcessEnv (or
Record<string, string | undefined>) so environment variable values may be string
or undefined, and adjust any imports/tsconfig types if needed to ensure NodeJS
namespace is available.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment on lines +103 to +107
// Store process references
this.activeProcesses.set(workflowId, workflowProcess);
this.childProcesses.set(workflowId, childProcess);

// Setup process event handlers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Persist taskId mapping at spawn time.

 // Store process references
 this.activeProcesses.set(workflowId, workflowProcess);
 this.childProcesses.set(workflowId, childProcess);
+this.workflowTaskIds.set(workflowId, taskId);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Store process references
this.activeProcesses.set(workflowId, workflowProcess);
this.childProcesses.set(workflowId, childProcess);
// Setup process event handlers
// Store process references
this.activeProcesses.set(workflowId, workflowProcess);
this.childProcesses.set(workflowId, childProcess);
this.workflowTaskIds.set(workflowId, taskId);
// Setup process event handlers
🤖 Prompt for AI Agents
In packages/workflow-engine/src/process/process-sandbox.ts around lines 103 to
107, persist the mapping between the spawned process's taskId and the workflowId
at spawn time. After setting this.activeProcesses and this.childProcesses, add
logic to extract the taskId(s) from the workflowProcess/childProcess and store
taskId -> workflowId in the existing in-memory map (create
this.taskIdToWorkflowId if it doesn't exist). Ensure the mapping is set
synchronously at spawn so later task lookups can resolve the correct workflowId.

Comment on lines +136 to +175
async stopProcess(workflowId: string, force = false): Promise<void> {
const process = this.activeProcesses.get(workflowId);
const childProcess = this.childProcesses.get(workflowId);

if (!process || !childProcess) {
throw new ProcessError(
`No running process found for workflow ${workflowId}`
);
}

try {
// Clear timeout
const timeout = this.timeouts.get(workflowId);
if (timeout) {
clearTimeout(timeout);
this.timeouts.delete(workflowId);
}

// Kill the process
if (force) {
childProcess.kill('SIGKILL');
} else {
childProcess.kill('SIGTERM');

// Give it 5 seconds to gracefully exit, then force kill
setTimeout(() => {
if (!childProcess.killed) {
childProcess.kill('SIGKILL');
}
}, 5000);
}

process.status = 'stopped';

// Emit process stopped event
this.emitEvent('process.stopped', workflowId, process.pid.toString(), {
pid: process.pid,
forced: force
});
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrong taskId in process.stopped event and duplicate emissions; also return before actual stop.

  • You pass pid as taskId (process.pid.toString()), which breaks consumers.
  • stopProcess emits process.stopped and the exit handler emits it again.
  • Doesn’t wait for actual process exit.

Apply this refactor:

-async stopProcess(workflowId: string, force = false): Promise<void> {
-  const process = this.activeProcesses.get(workflowId);
+async stopProcess(workflowId: string, force = false): Promise<void> {
+  const wfProcess = this.activeProcesses.get(workflowId);
   const childProcess = this.childProcesses.get(workflowId);
-
-  if (!process || !childProcess) {
+  if (!wfProcess || !childProcess) {
     throw new ProcessError(
       `No running process found for workflow ${workflowId}`
     );
   }
 
   try {
@@
-    if (force) {
+    if (force) {
       childProcess.kill('SIGKILL');
     } else {
       childProcess.kill('SIGTERM');
 
       // Give it 5 seconds to gracefully exit, then force kill
       setTimeout(() => {
         if (!childProcess.killed) {
           childProcess.kill('SIGKILL');
         }
       }, 5000);
     }
-
-    process.status = 'stopped';
-
-    // Emit process stopped event
-    this.emitEvent('process.stopped', workflowId, process.pid.toString(), {
-      pid: process.pid,
-      forced: force
-    });
+    // Wait for actual exit to avoid duplicate emissions and ensure cleanup
+    await new Promise<void>((resolve) => childProcess.once('exit', () => resolve()));
   } catch (error) {
     throw new ProcessError(
       `Failed to stop process for workflow ${workflowId}`,
-      process.pid,
+      wfProcess.pid,
       error as Error
     );
   }
 }

Additionally, store and use the real taskId (see next comments).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async stopProcess(workflowId: string, force = false): Promise<void> {
const process = this.activeProcesses.get(workflowId);
const childProcess = this.childProcesses.get(workflowId);
if (!process || !childProcess) {
throw new ProcessError(
`No running process found for workflow ${workflowId}`
);
}
try {
// Clear timeout
const timeout = this.timeouts.get(workflowId);
if (timeout) {
clearTimeout(timeout);
this.timeouts.delete(workflowId);
}
// Kill the process
if (force) {
childProcess.kill('SIGKILL');
} else {
childProcess.kill('SIGTERM');
// Give it 5 seconds to gracefully exit, then force kill
setTimeout(() => {
if (!childProcess.killed) {
childProcess.kill('SIGKILL');
}
}, 5000);
}
process.status = 'stopped';
// Emit process stopped event
this.emitEvent('process.stopped', workflowId, process.pid.toString(), {
pid: process.pid,
forced: force
});
} catch (error) {
async stopProcess(workflowId: string, force = false): Promise<void> {
const wfProcess = this.activeProcesses.get(workflowId);
const childProcess = this.childProcesses.get(workflowId);
if (!wfProcess || !childProcess) {
throw new ProcessError(
`No running process found for workflow ${workflowId}`
);
}
try {
// Clear timeout
const timeout = this.timeouts.get(workflowId);
if (timeout) {
clearTimeout(timeout);
this.timeouts.delete(workflowId);
}
// Kill the process
if (force) {
childProcess.kill('SIGKILL');
} else {
childProcess.kill('SIGTERM');
// Give it 5 seconds to gracefully exit, then force kill
setTimeout(() => {
if (!childProcess.killed) {
childProcess.kill('SIGKILL');
}
}, 5000);
}
// Wait for actual exit to avoid duplicate emissions and ensure cleanup
await new Promise<void>((resolve) => childProcess.once('exit', () => resolve()));
} catch (error) {
throw new ProcessError(
`Failed to stop process for workflow ${workflowId}`,
wfProcess.pid,
error as Error
);
}
}
🤖 Prompt for AI Agents
In packages/workflow-engine/src/process/process-sandbox.ts around lines 136 to
175, stopProcess currently emits a process.stopped event with the pid as the
taskId, duplicates the event because the child exit handler also emits it, and
returns before the child actually exits; to fix: use the real taskId (retrieve
it from the stored process object or where taskId is tracked) instead of
process.pid.toString() when emitting, remove or guard the local emit so only the
child's 'exit'/'close' handler emits process.stopped (or set a flag to prevent
double-emission), and make stopProcess return a Promise that awaits the child's
exit/close event (with a timeout fallback) before marking status stopped and
resolving; also ensure timeouts and listeners are cleaned up after exit to avoid
leaks.

Comment on lines +195 to +198
try {
childProcess.stdin?.write(input);
childProcess.stdin?.write('\n');
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Guard stdin writes.
Avoid writing to closed or non-writable streams.

- childProcess.stdin?.write(input);
- childProcess.stdin?.write('\n');
+ if (!childProcess.stdin || !childProcess.stdin.writable) {
+   throw new ProcessError('Process stdin is not writable', childProcess.pid);
+ }
+ childProcess.stdin.write(input + '\n');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
childProcess.stdin?.write(input);
childProcess.stdin?.write('\n');
} catch (error) {
try {
if (!childProcess.stdin || !childProcess.stdin.writable) {
throw new ProcessError('Process stdin is not writable', childProcess.pid);
}
childProcess.stdin.write(input + '\n');
} catch (error) {
🤖 Prompt for AI Agents
In packages/workflow-engine/src/process/process-sandbox.ts around lines 195 to
198, the code writes to childProcess.stdin without verifying the stream is
present and writable; update the block to first check that childProcess.stdin
exists and is writable (e.g., childProcess.stdin && childProcess.stdin.writable
&& !childProcess.stdin.destroyed && !childProcess.stdin.writableEnded) before
calling write, and only perform the writes when those checks pass; keep the
try/catch around the write and otherwise skip or log a warning when stdin is
closed/non-writable.

Comment on lines +252 to +259
private setupProcessHandlers(
workflowId: string,
taskId: string,
childProcess: ChildProcess
): void {
const process = this.activeProcesses.get(workflowId);
if (!process) return;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Avoid shadowing Node’s global process.
Rename local var to reduce confusion and accidental misuse.

-const process = this.activeProcesses.get(workflowId);
-if (!process) return;
+const wfProcess = this.activeProcesses.get(workflowId);
+if (!wfProcess) return;

Apply the rename in this method.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private setupProcessHandlers(
workflowId: string,
taskId: string,
childProcess: ChildProcess
): void {
const process = this.activeProcesses.get(workflowId);
if (!process) return;
private setupProcessHandlers(
workflowId: string,
taskId: string,
childProcess: ChildProcess
): void {
const wfProcess = this.activeProcesses.get(workflowId);
if (!wfProcess) return;
🤖 Prompt for AI Agents
In packages/workflow-engine/src/process/process-sandbox.ts around lines 252 to
259, the local variable named "process" shadows Node's global process; rename
this local variable (for example to "proc" or "activeProcess") and update every
reference inside this setupProcessHandlers method to use the new name so the
global process object is not accidentally shadowed or misused.

Comment on lines +287 to +299
childProcess.on('exit', (code, signal) => {
process.status = code === 0 ? 'stopped' : 'crashed';

this.emitEvent('process.stopped', workflowId, taskId, {
pid: process.pid,
exitCode: code,
signal
});

// Cleanup
this.activeProcesses.delete(workflowId);
this.childProcesses.delete(workflowId);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use correct taskId on exit and apply rename.

-  childProcess.on('exit', (code, signal) => {
-    process.status = code === 0 ? 'stopped' : 'crashed';
+  childProcess.on('exit', (code, signal) => {
+    wfProcess.status = code === 0 ? 'stopped' : 'crashed';
 
-    this.emitEvent('process.stopped', workflowId, taskId, {
-      pid: process.pid,
+    const tId = this.workflowTaskIds.get(workflowId) ?? taskId;
+    this.emitEvent('process.stopped', workflowId, tId, {
+      pid: wfProcess.pid,
       exitCode: code,
       signal
     });
 
     // Cleanup
     this.activeProcesses.delete(workflowId);
     this.childProcesses.delete(workflowId);
+    this.workflowTaskIds.delete(workflowId);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
childProcess.on('exit', (code, signal) => {
process.status = code === 0 ? 'stopped' : 'crashed';
this.emitEvent('process.stopped', workflowId, taskId, {
pid: process.pid,
exitCode: code,
signal
});
// Cleanup
this.activeProcesses.delete(workflowId);
this.childProcesses.delete(workflowId);
childProcess.on('exit', (code, signal) => {
wfProcess.status = code === 0 ? 'stopped' : 'crashed';
const tId = this.workflowTaskIds.get(workflowId) ?? taskId;
this.emitEvent('process.stopped', workflowId, tId, {
pid: wfProcess.pid,
exitCode: code,
signal
});
// Cleanup
this.activeProcesses.delete(workflowId);
this.childProcesses.delete(workflowId);
this.workflowTaskIds.delete(workflowId);
🤖 Prompt for AI Agents
In packages/workflow-engine/src/process/process-sandbox.ts around lines 287-299,
the exit handler is using the wrong taskId and the local variable name `process`
shadows the global process; rename the local variable (e.g., to `proc`) and use
the correct task id from the proc object when emitting the event and updating
status. Specifically, change `process` -> `proc` in the handler, set
`proc.status = code === 0 ? 'stopped' : 'crashed'`, call
`this.emitEvent('process.stopped', workflowId, proc.taskId, { pid: proc.pid,
exitCode: code, signal })`, and ensure the same `proc` identifier is used for
cleanup and any other references in this block.

Comment on lines +266 to +299
private async executeGitCommand(
args: string[],
options: { cwd: string }
): Promise<string> {
return new Promise((resolve, reject) => {
const git = spawn('git', args, {
cwd: options.cwd,
stdio: ['ignore', 'pipe', 'pipe']
});

let stdout = '';
let stderr = '';

git.stdout.on('data', (data) => {
stdout += data.toString();
});

git.stderr.on('data', (data) => {
stderr += data.toString();
});

git.on('close', (code) => {
if (code === 0) {
resolve(stdout);
} else {
reject(new Error(`Git command failed (${code}): ${stderr || stdout}`));
}
});

git.on('error', (error) => {
reject(error);
});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add timeouts to git calls to avoid indefinite hangs.

Long-running git commands can stall the engine. Introduce an optional timeout.

-  private async executeGitCommand(
-    args: string[], 
-    options: { cwd: string }
-  ): Promise<string> {
+  private async executeGitCommand(
+    args: string[],
+    options: { cwd: string; timeoutMs?: number }
+  ): Promise<string> {
     return new Promise((resolve, reject) => {
       const git = spawn('git', args, {
         cwd: options.cwd,
         stdio: ['ignore', 'pipe', 'pipe']
       });
 
       let stdout = '';
       let stderr = '';
+      let killedByTimeout = false;
+      const timer =
+        options.timeoutMs && options.timeoutMs > 0
+          ? setTimeout(() => {
+              killedByTimeout = true;
+              git.kill('SIGKILL');
+            }, options.timeoutMs)
+          : undefined;
 
       git.stdout.on('data', (data) => {
         stdout += data.toString();
       });
 
       git.stderr.on('data', (data) => {
         stderr += data.toString();
       });
 
       git.on('close', (code) => {
+        if (timer) clearTimeout(timer);
         if (code === 0) {
           resolve(stdout);
         } else {
-          reject(new Error(`Git command failed (${code}): ${stderr || stdout}`));
+          const reason = killedByTimeout ? 'timed out' : `exit code ${code}`;
+          reject(new Error(`Git command ${reason}: ${stderr || stdout}`));
         }
       });
 
       git.on('error', (error) => {
+        if (timer) clearTimeout(timer);
         reject(error);
       });
     });
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private async executeGitCommand(
args: string[],
options: { cwd: string }
): Promise<string> {
return new Promise((resolve, reject) => {
const git = spawn('git', args, {
cwd: options.cwd,
stdio: ['ignore', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
git.stdout.on('data', (data) => {
stdout += data.toString();
});
git.stderr.on('data', (data) => {
stderr += data.toString();
});
git.on('close', (code) => {
if (code === 0) {
resolve(stdout);
} else {
reject(new Error(`Git command failed (${code}): ${stderr || stdout}`));
}
});
git.on('error', (error) => {
reject(error);
});
});
}
private async executeGitCommand(
args: string[],
options: { cwd: string; timeoutMs?: number }
): Promise<string> {
return new Promise((resolve, reject) => {
const git = spawn('git', args, {
cwd: options.cwd,
stdio: ['ignore', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
let killedByTimeout = false;
const timer =
options.timeoutMs && options.timeoutMs > 0
? setTimeout(() => {
killedByTimeout = true;
git.kill('SIGKILL');
}, options.timeoutMs)
: undefined;
git.stdout.on('data', (data) => {
stdout += data.toString();
});
git.stderr.on('data', (data) => {
stderr += data.toString();
});
git.on('close', (code) => {
if (timer) clearTimeout(timer);
if (code === 0) {
resolve(stdout);
} else {
const reason = killedByTimeout ? 'timed out' : `exit code ${code}`;
reject(new Error(`Git command ${reason}: ${stderr || stdout}`));
}
});
git.on('error', (error) => {
if (timer) clearTimeout(timer);
reject(error);
});
});
}
🤖 Prompt for AI Agents
In packages/workflow-engine/src/worktree/worktree-manager.ts around lines 266 to
299, the executeGitCommand currently can hang indefinitely; add an optional
timeout parameter (e.g. timeoutMs?: number) to the function signature and
implement a timer that, when reached, kills the spawned git process, removes
listeners, clears buffers, and rejects the Promise with a descriptive timeout
Error; ensure the timer is cleared on normal close/error, that process.kill is
used safely (check if process is still running), and listeners for
stdout/stderr/close/error are cleaned up to avoid leaks.

Comment on lines +311 to +318
private async pathExists(path: string): Promise<boolean> {
try {
await fs.access(path);
return true;
} catch {
return false;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Minor: avoid shadowing imported 'path' name.

Parameter name path hides the imported path module in this scope. Rename for clarity.

-  private async pathExists(path: string): Promise<boolean> {
+  private async pathExists(filePath: string): Promise<boolean> {
     try {
-      await fs.access(path);
+      await fs.access(filePath);
       return true;
     } catch {
       return false;
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private async pathExists(path: string): Promise<boolean> {
try {
await fs.access(path);
return true;
} catch {
return false;
}
}
private async pathExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
🤖 Prompt for AI Agents
In packages/workflow-engine/src/worktree/worktree-manager.ts around lines 311 to
318, the method parameter named "path" shadows the imported path module; rename
the parameter (e.g., to "targetPath" or "filePath") and update the method
signature and all internal references within this function accordingly to avoid
the name collision while keeping the return type Promise<boolean> and behavior
unchanged.

Comment on lines +323 to +335
private completeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {
const branch = partial.branch || 'unknown';
const taskIdMatch = branch.match(/^task\/(.+?)-/);

return {
path: partial.path || '',
branch,
createdAt: partial.createdAt || new Date(),
taskId: taskIdMatch?.[1] || partial.taskId || 'unknown',
commit: partial.commit,
locked: partial.locked || false,
lockReason: partial.lockReason
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Task ID parsing is incorrect for IDs containing dashes.

Regex /^task/(.+?)-/ captures only up to the first dash. With taskId “ABC-123”, you’ll derive “ABC”. This breaks cleanup and lookups.

Apply this diff to derive taskId robustly (prefer last - suffix; fallback to directory name task-):

-  private completeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {
-    const branch = partial.branch || 'unknown';
-    const taskIdMatch = branch.match(/^task\/(.+?)-/);
-    
-    return {
+  private completeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {
+    const branch = partial.branch || 'unknown';
+    const taskId =
+      (/^task\/(.+)-\d+$/.test(branch)
+        ? branch.replace(/^task\/(.+)-\d+$/, '$1')
+        : (() => {
+            const base = path.basename(partial.path || '');
+            const m = base.match(/^task-(.+)$/);
+            return m?.[1];
+          })()) || partial.taskId || 'unknown';
+    return {
       path: partial.path || '',
       branch,
       createdAt: partial.createdAt || new Date(),
-      taskId: taskIdMatch?.[1] || partial.taskId || 'unknown',
+      taskId,
       commit: partial.commit,
       locked: partial.locked || false,
       lockReason: partial.lockReason
     };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private completeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {
const branch = partial.branch || 'unknown';
const taskIdMatch = branch.match(/^task\/(.+?)-/);
return {
path: partial.path || '',
branch,
createdAt: partial.createdAt || new Date(),
taskId: taskIdMatch?.[1] || partial.taskId || 'unknown',
commit: partial.commit,
locked: partial.locked || false,
lockReason: partial.lockReason
};
private completeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {
const branch = partial.branch || 'unknown';
const taskId =
(/^task\/(.+)-\d+$/.test(branch)
? branch.replace(/^task\/(.+)-\d+$/, '$1')
: (() => {
const base = path.basename(partial.path || '');
const m = base.match(/^task-(.+)$/);
return m?.[1];
})()) || partial.taskId || 'unknown';
return {
path: partial.path || '',
branch,
createdAt: partial.createdAt || new Date(),
taskId,
commit: partial.commit,
locked: partial.locked || false,
lockReason: partial.lockReason
};
}
🤖 Prompt for AI Agents
packages/workflow-engine/src/worktree/worktree-manager.ts around lines 323 to
335: the current regex /^task\/(.+?)-/ stops at the first dash and truncates IDs
like "ABC-123"; change the parsing to first try matching a final timestamp-style
suffix and capture everything before it (e.g. /^task\/(.+)-\d+$/ to get
"ABC-123" from "task/ABC-123-161800"), if that fails then fallback to extracting
the ID after a "task-" directory token (e.g. find "task-" and take the remainder
up to a slash or end), and only then fall back to partial.taskId or 'unknown';
update the assignment logic to prefer these new parsed values and keep existing
fallbacks for commit/locked/lockReason.

Comment on lines +341 to +350
private setupCleanupHandlers(): void {
const cleanup = () => {
console.log('Cleaning up worktrees...');
this.cleanupAll(true).catch(console.error);
};

process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
process.on('exit', cleanup);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid async work in 'exit' and prevent double cleanup; use once + beforeExit.

process 'exit' won’t await async work; repeated signals can trigger duplicate cleanup. Add a reentrancy guard and switch to once + beforeExit.

-  private config: WorktreeManagerConfig;
-  private activeWorktrees = new Map<string, WorktreeInfo>();
+  private config: WorktreeManagerConfig;
+  private activeWorktrees = new Map<string, WorktreeInfo>();
+  private cleanupTriggered = false;
-  private setupCleanupHandlers(): void {
-    const cleanup = () => {
-      console.log('Cleaning up worktrees...');
-      this.cleanupAll(true).catch(console.error);
-    };
-
-    process.on('SIGINT', cleanup);
-    process.on('SIGTERM', cleanup);
-    process.on('exit', cleanup);
-  }
+  private setupCleanupHandlers(): void {
+    const cleanup = async () => {
+      if (this.cleanupTriggered) return;
+      this.cleanupTriggered = true;
+      console.log('Cleaning up worktrees...');
+      try {
+        await this.cleanupAll(true);
+      } catch (err) {
+        console.error(err);
+      }
+    };
+    process.once('SIGINT', cleanup);
+    process.once('SIGTERM', cleanup);
+    process.once('beforeExit', cleanup);
+  }

Also applies to: 26-28

🤖 Prompt for AI Agents
In packages/workflow-engine/src/worktree/worktree-manager.ts around lines
341-350 (and also apply the same change to the similar handlers at lines 26-28),
the cleanup handler registers async work on process 'exit' and uses process.on
which allows duplicate invocations; change to use process.once for signals and
'beforeExit' instead of 'exit', and add a reentrancy guard (e.g., an internal
boolean like isCleaningUp) to ensure cleanupAll is only invoked once; for the
'beforeExit' handler ensure you await or call cleanupAll properly (since
beforeExit can be async) and keep the 'exit' handler only for synchronous
finalization or to skip async work entirely.

Comment on lines +343 to +344
console.log('Cleaning up worktrees...');
this.cleanupAll(true).catch(console.error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Prefer a pluggable logger over console. in library code.*

Wire a logger (or accept one via config) so callers control verbosity and sinks.

🤖 Prompt for AI Agents
In packages/workflow-engine/src/worktree/worktree-manager.ts around lines 343 to
344, the code uses console.log and console.error directly which hardcodes
logging; refactor to accept a pluggable logger (e.g., via constructor or config)
and replace console.log with logger.info/debug and console.error with
logger.error, ensuring the logger has the minimal methods used or create a small
adapter that falls back to console; update class initialization to store the
logger and update calls to use it, and add tests or default behavior so existing
callers without a logger get a sensible default.

@Crunchyman-ralph
Copy link
Collaborator Author

went with tdd workflow instead #1289

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant