From dfe7d7bba2a3fd092b551f75980c97a7e9b6648c Mon Sep 17 00:00:00 2001 From: Joseph Goksu Date: Sun, 8 Mar 2026 23:26:28 +0000 Subject: [PATCH 1/3] refactor: rename slash commands from tw-* to taskwing:* and MCP server to taskwing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standardize naming convention across the entire codebase: - Slash commands: /tw-ask → /taskwing:ask, /tw-next → /taskwing:next, etc. - MCP server name: taskwing-mcp → taskwing - No backward compatibility shims — old names detected as legacy for cleanup --- AGENTS.md | 18 ++++----- CHANGELOG.md | 2 +- CLAUDE.md | 22 +++++------ GEMINI.md | 18 ++++----- README.md | 24 ++++++------ cmd/doctor.go | 6 +-- cmd/goal.go | 2 +- cmd/hook.go | 10 ++--- cmd/mcp_detect.go | 6 +-- cmd/mcp_server.go | 2 +- cmd/plan.go | 2 +- cmd/slash.go | 6 +-- cmd/slash_content.go | 44 +++++++++++----------- docs/PRODUCT_VISION.md | 2 +- docs/PROMPT_FAILURES_LOG.md | 2 +- docs/TUTORIAL.md | 12 +++--- docs/WORKFLOW_CONTRACT_V1.md | 10 ++--- docs/WORKFLOW_PACK.md | 4 +- internal/bootstrap/initializer.go | 30 +++++++-------- internal/bootstrap/integration_health.go | 2 +- internal/bootstrap/mcp_healthcheck_test.go | 4 +- internal/brief/brief.go | 2 +- internal/mcpcfg/naming.go | 4 +- opencode.json | 2 +- 24 files changed, 118 insertions(+), 118 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d295d43..a965117 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,15 +79,15 @@ Brand names and logos are trademarks of their respective owners; usage here indi ### Slash Commands -- /tw-ask - Use when you need to search project knowledge (decisions, patterns, constraints). -- /tw-remember - Use when you want to persist a decision, pattern, or insight to project memory. -- /tw-next - Use when you are ready to start the next approved TaskWing task with full context. -- /tw-done - Use when implementation is verified and you are ready to complete the current task. -- /tw-status - Use when you need current task progress and acceptance criteria status. -- /tw-plan - Use when you need to clarify a goal and build an approved execution plan. -- /tw-debug - Use when an issue requires root-cause-first debugging before proposing fixes. -- /tw-explain - Use when you need a deep explanation of a code symbol and its call graph. -- /tw-simplify - Use when you want to simplify code while preserving behavior. +- /taskwing:ask - Use when you need to search project knowledge (decisions, patterns, constraints). +- /taskwing:remember - Use when you want to persist a decision, pattern, or insight to project memory. +- /taskwing:next - Use when you are ready to start the next approved TaskWing task with full context. +- /taskwing:done - Use when implementation is verified and you are ready to complete the current task. +- /taskwing:status - Use when you need current task progress and acceptance criteria status. +- /taskwing:plan - Use when you need to clarify a goal and build an approved execution plan. +- /taskwing:debug - Use when an issue requires root-cause-first debugging before proposing fixes. +- /taskwing:explain - Use when you need a deep explanation of a code symbol and its call graph. +- /taskwing:simplify - Use when you want to simplify code while preserving behavior. ### Core Commands diff --git a/CHANGELOG.md b/CHANGELOG.md index 1de8a08..ae28bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - "TaskWing helps turn a goal into executed tasks with persistent context across AI sessions." - Updated slash and MCP prompt contracts to unified `task` and `plan` action-based interfaces. - Purged stale/outdated architecture documentation that no longer matches shipped behavior. -- Reworked `/tw-plan`, `/tw-next`, `/tw-done`, and `/tw-debug` prompts as explicit process contracts with hard gates and refusal fallbacks. +- Reworked `/taskwing:plan`, `/taskwing:next`, `/taskwing:done`, and `/taskwing:debug` prompts as explicit process contracts with hard gates and refusal fallbacks. - Updated slash command descriptions to trigger-focused "Use when ..." phrasing across assistant command generation. - Session initialization output now injects TaskWing Workflow Contract v1 for hook-enabled assistants. diff --git a/CLAUDE.md b/CLAUDE.md index c0ef0a1..c3fedea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,7 +15,7 @@ Two MCP servers are available for testing: | MCP Server | Binary | Use Case | | ------------------------ | ------------------------------ | ----------------------------------- | -| `taskwing-mcp` | Production (`taskwing`) | Stable features, production testing | +| `taskwing` | Production (`taskwing`) | Stable features, production testing | | `taskwing-local-dev-mcp` | Development (`./bin/taskwing`) | Testing new/changed features | ### Testing New Features @@ -28,7 +28,7 @@ When developing new MCP tools or modifying existing ones: ### Important: MCP Server Caching -The production MCP server (`taskwing-mcp`) uses the installed Homebrew binary. Changes to code are **NOT reflected** until: +The production MCP server (`taskwing`) uses the installed Homebrew binary. Changes to code are **NOT reflected** until: - You rebuild: `make build && brew reinstall taskwing` (if using Homebrew) - Or test with: `go run . mcp` directly @@ -339,15 +339,15 @@ Brand names and logos are trademarks of their respective owners; usage here indi ### Slash Commands -- /tw-ask - Use when you need to search project knowledge (decisions, patterns, constraints). -- /tw-remember - Use when you want to persist a decision, pattern, or insight to project memory. -- /tw-next - Use when you are ready to start the next approved TaskWing task with full context. -- /tw-done - Use when implementation is verified and you are ready to complete the current task. -- /tw-status - Use when you need current task progress and acceptance criteria status. -- /tw-plan - Use when you need to clarify a goal and build an approved execution plan. -- /tw-debug - Use when an issue requires root-cause-first debugging before proposing fixes. -- /tw-explain - Use when you need a deep explanation of a code symbol and its call graph. -- /tw-simplify - Use when you want to simplify code while preserving behavior. +- /taskwing:ask - Use when you need to search project knowledge (decisions, patterns, constraints). +- /taskwing:remember - Use when you want to persist a decision, pattern, or insight to project memory. +- /taskwing:next - Use when you are ready to start the next approved TaskWing task with full context. +- /taskwing:done - Use when implementation is verified and you are ready to complete the current task. +- /taskwing:status - Use when you need current task progress and acceptance criteria status. +- /taskwing:plan - Use when you need to clarify a goal and build an approved execution plan. +- /taskwing:debug - Use when an issue requires root-cause-first debugging before proposing fixes. +- /taskwing:explain - Use when you need a deep explanation of a code symbol and its call graph. +- /taskwing:simplify - Use when you want to simplify code while preserving behavior. ### Core Commands diff --git a/GEMINI.md b/GEMINI.md index ee46cae..bb5fbe4 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -218,15 +218,15 @@ Brand names and logos are trademarks of their respective owners; usage here indi ### Slash Commands -- /tw-ask - Use when you need to search project knowledge (decisions, patterns, constraints). -- /tw-remember - Use when you want to persist a decision, pattern, or insight to project memory. -- /tw-next - Use when you are ready to start the next approved TaskWing task with full context. -- /tw-done - Use when implementation is verified and you are ready to complete the current task. -- /tw-status - Use when you need current task progress and acceptance criteria status. -- /tw-plan - Use when you need to clarify a goal and build an approved execution plan. -- /tw-debug - Use when an issue requires root-cause-first debugging before proposing fixes. -- /tw-explain - Use when you need a deep explanation of a code symbol and its call graph. -- /tw-simplify - Use when you want to simplify code while preserving behavior. +- /taskwing:ask - Use when you need to search project knowledge (decisions, patterns, constraints). +- /taskwing:remember - Use when you want to persist a decision, pattern, or insight to project memory. +- /taskwing:next - Use when you are ready to start the next approved TaskWing task with full context. +- /taskwing:done - Use when implementation is verified and you are ready to complete the current task. +- /taskwing:status - Use when you need current task progress and acceptance criteria status. +- /taskwing:plan - Use when you need to clarify a goal and build an approved execution plan. +- /taskwing:debug - Use when an issue requires root-cause-first debugging before proposing fixes. +- /taskwing:explain - Use when you need a deep explanation of a code symbol and its call graph. +- /taskwing:simplify - Use when you want to simplify code while preserving behavior. ### Core Commands diff --git a/README.md b/README.md index 57206ef..2a19052 100644 --- a/README.md +++ b/README.md @@ -116,9 +116,9 @@ taskwing goal "Add Stripe billing" # → Plan decomposed into 5 executable tasks # 3. Execute with your AI assistant -/tw-next # Get next task with full context +/taskwing:next # Get next task with full context # ...work... -/tw-done # Mark complete, advance to next +/taskwing:done # Mark complete, advance to next ``` ## MCP Tools @@ -141,7 +141,7 @@ Add to your AI tool's MCP config: ```json { "mcpServers": { - "taskwing-mcp": { + "taskwing": { "command": "taskwing", "args": ["mcp"] } @@ -155,15 +155,15 @@ Once connected, use these slash commands from your AI assistant: | Command | When to use | |:--------|:------------| -| `/tw-ask` | Search project knowledge (decisions, patterns, constraints) | -| `/tw-remember` | Persist a decision, pattern, or insight to project memory | -| `/tw-next` | Start the next approved task with full context | -| `/tw-done` | Complete the current task after verification | -| `/tw-status` | Check current task progress and acceptance criteria | -| `/tw-plan` | Clarify a goal and build an approved execution plan | -| `/tw-debug` | Root-cause-first debugging before proposing fixes | -| `/tw-explain` | Deep explanation of a code symbol and its call graph | -| `/tw-simplify` | Simplify code while preserving behavior | +| `/taskwing:ask` | Search project knowledge (decisions, patterns, constraints) | +| `/taskwing:remember` | Persist a decision, pattern, or insight to project memory | +| `/taskwing:next` | Start the next approved task with full context | +| `/taskwing:done` | Complete the current task after verification | +| `/taskwing:status` | Check current task progress and acceptance criteria | +| `/taskwing:plan` | Clarify a goal and build an approved execution plan | +| `/taskwing:debug` | Root-cause-first debugging before proposing fixes | +| `/taskwing:explain` | Deep explanation of a code symbol and its call graph | +| `/taskwing:simplify` | Simplify code while preserving behavior | ## Core Commands diff --git a/cmd/doctor.go b/cmd/doctor.go index acfa4a0..a84fe26 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -517,12 +517,12 @@ func printNextSteps(checks []DoctorCheck) { fmt.Println("Next steps:") if !hasActivePlan { fmt.Println(" 1. Create and activate plan: taskwing goal \"your development goal\"") - fmt.Println(" 2. Open Claude Code and run: /tw-next") + fmt.Println(" 2. Open Claude Code and run: /taskwing:next") } else if !hasSession { fmt.Println(" 1. Open Claude Code (session will auto-initialize)") - fmt.Println(" 2. Run: /tw-next") + fmt.Println(" 2. Run: /taskwing:next") } else { - fmt.Println(" • In Claude Code, run: /tw-next") + fmt.Println(" • In Claude Code, run: /taskwing:next") fmt.Println(" • Tasks will auto-continue until circuit breaker triggers") } } diff --git a/cmd/goal.go b/cmd/goal.go index 922fdac..0fcdd15 100644 --- a/cmd/goal.go +++ b/cmd/goal.go @@ -151,7 +151,7 @@ func runGoal(cmd *cobra.Command, args []string) error { fmt.Printf("Plan created and activated: %s (%d task(s))\n", genRes.PlanID, len(genRes.Tasks)) fmt.Println("Next:") - fmt.Println(" 1. In your AI tool, run /tw-next") + fmt.Println(" 1. In your AI tool, run /taskwing:next") fmt.Println(" 2. Or use MCP tool task with action=next") return nil } diff --git a/cmd/hook.go b/cmd/hook.go index dbf73c7..f529ccc 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -209,7 +209,7 @@ func runContinueCheck(maxTasks, maxMinutes int) error { _ = saveHookSession(session) return outputHookResponse(HookResponse{ - Reason: fmt.Sprintf("Sentinel circuit breaker: Critical deviation detected in previous task. %s\n\nReview the changes before proceeding. Use /tw-next to continue after review.", session.LastDeviationSummary), + Reason: fmt.Sprintf("Sentinel circuit breaker: Critical deviation detected in previous task. %s\n\nReview the changes before proceeding. Use /taskwing:next to continue after review.", session.LastDeviationSummary), }) } @@ -232,11 +232,11 @@ func runContinueCheck(maxTasks, maxMinutes int) error { if err != nil { if isMissingProjectMemoryError(err) { return outputHookResponse(HookResponse{ - Reason: "No project memory found. Run 'taskwing bootstrap' to initialize project memory, or use /tw-next to continue manually.", + Reason: "No project memory found. Run 'taskwing bootstrap' to initialize project memory, or use /taskwing:next to continue manually.", }) } return outputHookResponse(HookResponse{ - Reason: fmt.Sprintf("Could not open repository: %v. Use /tw-next to continue manually.", err), + Reason: fmt.Sprintf("Could not open repository: %v. Use /taskwing:next to continue manually.", err), }) } defer func() { _ = repo.Close() }() @@ -332,7 +332,7 @@ func runContinueCheck(maxTasks, maxMinutes int) error { blockDecision := "block" return outputHookResponse(HookResponse{ Decision: &blockDecision, - Reason: fmt.Sprintf("Continue to task %d/%d: %s\n\n%s\n\nIf auto-continue fails, use /tw-next to proceed manually.", session.TasksCompleted+1, len(activePlan.Tasks), nextTask.Title, contextStr), + Reason: fmt.Sprintf("Continue to task %d/%d: %s\n\n%s\n\nIf auto-continue fails, use /taskwing:next to proceed manually.", session.TasksCompleted+1, len(activePlan.Tasks), nextTask.Title, contextStr), }) } @@ -415,7 +415,7 @@ Circuit breakers are configured in .claude/settings.json (defaults: %d tasks, %d %s -Use /tw-next to start the first task, or it will auto-continue after each task. +Use /taskwing:next to start the first task, or it will auto-continue after each task. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ `, session.SessionID, session.StartedAt.Format("15:04:05"), planInfo, DefaultMaxTasksPerSession, DefaultMaxSessionMinutes, workflowContractBanner) diff --git a/cmd/mcp_detect.go b/cmd/mcp_detect.go index f267f72..39a3eff 100644 --- a/cmd/mcp_detect.go +++ b/cmd/mcp_detect.go @@ -36,7 +36,7 @@ func detectExistingMCPConfigs() []string { return found } -// detectClaudeMCP checks if Claude Code CLI has taskwing-mcp configured. +// detectClaudeMCP checks if Claude Code CLI has taskwing configured. func detectClaudeMCP() bool { // First check if claude CLI is available _, err := exec.LookPath("claude") @@ -56,7 +56,7 @@ func detectClaudeMCP() bool { return mcpcfg.ContainsCanonicalServerName(string(output)) } -// detectGeminiMCP checks if Gemini CLI has taskwing-mcp configured. +// detectGeminiMCP checks if Gemini CLI has taskwing configured. func detectGeminiMCP() bool { // First check if gemini CLI is available _, err := exec.LookPath("gemini") @@ -78,7 +78,7 @@ func detectGeminiMCP() bool { return mcpcfg.ContainsCanonicalServerName(string(output)) } -// detectCodexMCP checks if Codex CLI has taskwing-mcp configured. +// detectCodexMCP checks if Codex CLI has taskwing configured. func detectCodexMCP() bool { // First check if codex CLI is available _, err := exec.LookPath("codex") diff --git a/cmd/mcp_server.go b/cmd/mcp_server.go index c7193b3..0214348 100644 --- a/cmd/mcp_server.go +++ b/cmd/mcp_server.go @@ -146,7 +146,7 @@ func runMCPServer(ctx context.Context) error { // Create MCP server impl := &mcpsdk.Implementation{ - Name: "taskwing-mcp", + Name: "taskwing", Version: version, } diff --git a/cmd/plan.go b/cmd/plan.go index 73d99f0..30e3274 100644 --- a/cmd/plan.go +++ b/cmd/plan.go @@ -689,5 +689,5 @@ func printPlanView(plan *task.Plan) { fmt.Println("Next steps:") fmt.Println(" • taskwing task list --plan " + plan.ID) - fmt.Println(" • /tw-next") + fmt.Println(" • /taskwing:next") } diff --git a/cmd/slash.go b/cmd/slash.go index ed9c766..8c963e8 100644 --- a/cmd/slash.go +++ b/cmd/slash.go @@ -34,9 +34,9 @@ This command is called dynamically by AI assistant slash commands to ensure the content always matches the installed CLI version. Example: - taskwing slash next # Output /tw-next content - taskwing slash done # Output /tw-done content - taskwing slash plan # Output /tw-plan content`, + taskwing slash next # Output /taskwing:next content + taskwing slash done # Output /taskwing:done content + taskwing slash plan # Output /taskwing:plan content`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return cmd.Help() diff --git a/cmd/slash_content.go b/cmd/slash_content.go index 86cdce9..2fe1556 100644 --- a/cmd/slash_content.go +++ b/cmd/slash_content.go @@ -3,7 +3,7 @@ Copyright © 2025 Joseph Goksu josephgoksu@gmail.com */ package cmd -// slashNextContent is the prompt content for /tw-next +// slashNextContent is the prompt content for /taskwing:next const slashNextContent = `# Start Next TaskWing Task with Full Context ## TaskWing Workflow Contract v1 (Always On) @@ -117,7 +117,7 @@ taskwing plan status # Check active plan progress ` + "```" + ` ` -// slashDoneContent is the prompt content for /tw-done +// slashDoneContent is the prompt content for /taskwing:done const slashDoneContent = `# Complete Task with Architecture-Aware Summary ## TaskWing Workflow Contract v1 (Always On) @@ -197,7 +197,7 @@ Display: [Summary report] Recorded in TaskWing memory. -Use /tw-next to continue with next priority task. +Use /taskwing:next to continue with next priority task. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ` + "```" + ` @@ -207,7 +207,7 @@ taskwing task complete TASK_ID ` + "```" + ` ` -// slashStatusContent is the prompt content for /tw-status +// slashStatusContent is the prompt content for /taskwing:status const slashStatusContent = `# Show Current Task Status This is a read-only status command. Do not use it to bypass plan, verification, or debug gates. @@ -220,7 +220,7 @@ Call MCP tool ` + "`task`" + ` with action ` + "`current`" + `: If no active task: ` + "```" + ` -No active task. Use /tw-next to start the next priority task. +No active task. Use /taskwing:next to start the next priority task. ` + "```" + ` ## Step 2: Display Status @@ -242,8 +242,8 @@ Scope: [scope] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Commands: - /tw-done - Complete this task - /tw-ask - Fetch more context + /taskwing:done - Complete this task + /taskwing:ask - Fetch more context ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ` + "```" + ` @@ -254,12 +254,12 @@ taskwing plan list ` + "```" + ` ` -// slashPlanContent is the prompt content for /tw-plan +// slashPlanContent is the prompt content for /taskwing:plan const slashPlanContent = `# Create Development Plan with Goal -**Usage:** ` + "`/tw-plan `" + ` or ` + "`/tw-plan --batch `" + ` +**Usage:** ` + "`/taskwing:plan `" + ` or ` + "`/taskwing:plan --batch `" + ` -**Example:** ` + "`/tw-plan Add Stripe billing integration`" + ` +**Example:** ` + "`/taskwing:plan Add Stripe billing integration`" + ` ## TaskWing Workflow Contract v1 (Always On) 1. No implementation before a clarified and approved plan/task checkpoint. @@ -365,7 +365,7 @@ Display the generated plan: 📋 Plan saved and set as active. **Next steps:** -- Run /tw-next to start working on the first task +- Run /taskwing:next to start working on the first task ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ` + "```" + ` @@ -502,7 +502,7 @@ After all phases are expanded, call MCP tool ` + "`plan`" + ` with action=finali 📋 Plan saved and set as active. **Next steps:** -- Run /tw-next to start working on the first task +- Run /taskwing:next to start working on the first task ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ` + "```" + ` @@ -516,10 +516,10 @@ taskwing plan new --non-interactive "Your goal description" # Headless mode ` + "```" + ` ` -// slashSimplifyContent is the prompt content for /tw-simplify +// slashSimplifyContent is the prompt content for /taskwing:simplify const slashSimplifyContent = `# Simplify Code -**Usage:** ` + "`/tw-simplify [file_path or paste code]`" + ` +**Usage:** ` + "`/taskwing:simplify [file_path or paste code]`" + ` Reduce code complexity while preserving behavior. This command is optimization-only and must not bypass planning, verification, or debugging gates. @@ -575,12 +575,12 @@ Ask if the user wants to apply the changes to the file. ` + "```" + ` ` -// slashDebugContent is the prompt content for /tw-debug +// slashDebugContent is the prompt content for /taskwing:debug const slashDebugContent = `# Debug Issue -**Usage:** ` + "`/tw-debug `" + ` +**Usage:** ` + "`/taskwing:debug `" + ` -**Example:** ` + "`/tw-debug API returns 500 on /users endpoint`" + ` +**Example:** ` + "`/taskwing:debug API returns 500 on /users endpoint`" + ` ## TaskWing Workflow Contract v1 (Always On) 1. No implementation before a clarified and approved plan/task checkpoint. @@ -663,12 +663,12 @@ taskwing plan status ` + "```" + ` ` -// slashExplainContent is the prompt content for /tw-explain +// slashExplainContent is the prompt content for /taskwing:explain const slashExplainContent = `# Explain Code Symbol -**Usage:** ` + "`/tw-explain `" + ` +**Usage:** ` + "`/taskwing:explain `" + ` -**Example:** ` + "`/tw-explain NewAskApp`" + ` +**Example:** ` + "`/taskwing:explain NewAskApp`" + ` Get a deep-dive explanation of a code symbol including its purpose, usage patterns, and call graph. This is an analysis command and must not be used to bypass planning, verification, or debug gates. @@ -736,7 +736,7 @@ taskwing mcp ` + "```" + ` ` -// slashAskContent is the prompt content for /tw-ask +// slashAskContent is the prompt content for /taskwing:ask const slashAskContent = `# Project Knowledge Brief This is a context-priming command and must not be used to bypass planning, verification, or debug gates. @@ -756,7 +756,7 @@ If you need broader coverage, run: Present the returned summary and top results to prime the conversation with project knowledge. ` -// slashRememberContent is the prompt content for /tw-remember +// slashRememberContent is the prompt content for /taskwing:remember const slashRememberContent = `# Store Knowledge in Project Memory This is a persistence command and must not be used to bypass planning, verification, or debug gates. diff --git a/docs/PRODUCT_VISION.md b/docs/PRODUCT_VISION.md index 822b4cf..a5e988e 100644 --- a/docs/PRODUCT_VISION.md +++ b/docs/PRODUCT_VISION.md @@ -40,7 +40,7 @@ Brand names and logos are trademarks of their respective owners; usage here indi ```text ┌─────────────────────────────────────────────────────────┐ │ USER INTERFACE │ -│ taskwing goal "..." │ /tw-next │ /tw-done │ +│ taskwing goal "..." │ /taskwing:next │ /taskwing:done │ └─────────────────────────────────────────────────────────┘ │ ▼ diff --git a/docs/PROMPT_FAILURES_LOG.md b/docs/PROMPT_FAILURES_LOG.md index 11ce2cc..6d6de30 100644 --- a/docs/PROMPT_FAILURES_LOG.md +++ b/docs/PROMPT_FAILURES_LOG.md @@ -6,7 +6,7 @@ Track prompt-contract failures and close the loop with monthly fixes. | Date (UTC) | Command | Failure Type | User Impact | Root Cause | Fix Shipped | Release | |------------|---------|--------------|-------------|------------|-------------|---------| -| YYYY-MM-DD | /tw-* | gate bypass / sequencing / ambiguity / missing refusal | low/med/high | short summary | yes/no + PR | vX.Y.Z | +| YYYY-MM-DD | /taskwing:* | gate bypass / sequencing / ambiguity / missing refusal | low/med/high | short summary | yes/no + PR | vX.Y.Z | ## Failure Taxonomy diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md index bf6fae4..ae1e5e5 100644 --- a/docs/TUTORIAL.md +++ b/docs/TUTORIAL.md @@ -49,29 +49,29 @@ taskwing goal "Add user authentication" In your AI tool: ```text -/tw-next +/taskwing:next ``` When done: ```text -/tw-done +/taskwing:done ``` Check current status: ```text -/tw-status +/taskwing:status ``` ## 3.5. First-Run Success Loop (<15 minutes) Use this minimum loop to validate TaskWing end-to-end: -1. `/tw-plan ` and approve the clarified checkpoint -2. `/tw-next` and approve the implementation checkpoint +1. `/taskwing:plan ` and approve the clarified checkpoint +2. `/taskwing:next` and approve the implementation checkpoint 3. Make a scoped change -4. `/tw-done` with fresh verification evidence +4. `/taskwing:done` with fresh verification evidence If you complete this loop once, your setup is healthy and your assistant workflow is aligned with TaskWing contracts. diff --git a/docs/WORKFLOW_CONTRACT_V1.md b/docs/WORKFLOW_CONTRACT_V1.md index 06bd799..d4c4f48 100644 --- a/docs/WORKFLOW_CONTRACT_V1.md +++ b/docs/WORKFLOW_CONTRACT_V1.md @@ -5,10 +5,10 @@ This contract defines non-negotiable behavior gates for TaskWing-guided executio ## Scope Applies to slash flows and hook-driven sessions: -- `/tw-plan` -- `/tw-next` -- `/tw-done` -- `/tw-debug` +- `/taskwing:plan` +- `/taskwing:next` +- `/taskwing:done` +- `/taskwing:debug` ## Gate 1: Plan/Task Checkpoint Before Implementation @@ -47,5 +47,5 @@ KPI: ## Operating Policy - These gates are hard blockers for core workflow commands. -- Commands that are primarily read-only (`/tw-ask`, `/tw-status`, `/tw-explain`, `/tw-simplify`) remain lightweight but must not bypass these gates. +- Commands that are primarily read-only (`/taskwing:ask`, `/taskwing:status`, `/taskwing:explain`, `/taskwing:simplify`) remain lightweight but must not bypass these gates. - Prompt regressions against this contract are release blockers. diff --git a/docs/WORKFLOW_PACK.md b/docs/WORKFLOW_PACK.md index b3f1ddc..3b72e60 100644 --- a/docs/WORKFLOW_PACK.md +++ b/docs/WORKFLOW_PACK.md @@ -19,9 +19,9 @@ Get users to one visible success loop in under 15 minutes: 1. `taskwing bootstrap` 2. `taskwing goal ""` -3. `/tw-next` +3. `/taskwing:next` 4. Implement scoped change -5. `/tw-done` +5. `/taskwing:done` Expected first-run success signal: - One completed task with explicit verification evidence. diff --git a/internal/bootstrap/initializer.go b/internal/bootstrap/initializer.go index 934552e..f509d79 100644 --- a/internal/bootstrap/initializer.go +++ b/internal/bootstrap/initializer.go @@ -263,18 +263,18 @@ type SlashCommand struct { // SlashCommands is the canonical list of slash commands generated by TaskWing. // When this list changes, the version hash changes, triggering updates on next bootstrap. var SlashCommands = []SlashCommand{ - {"tw-ask", "ask", "Use when you need to search project knowledge (decisions, patterns, constraints)."}, - {"tw-remember", "remember", "Use when you want to persist a decision, pattern, or insight to project memory."}, - {"tw-next", "next", "Use when you are ready to start the next approved TaskWing task with full context."}, - {"tw-done", "done", "Use when implementation is verified and you are ready to complete the current task."}, - {"tw-status", "status", "Use when you need current task progress and acceptance criteria status."}, - {"tw-plan", "plan", "Use when you need to clarify a goal and build an approved execution plan."}, - {"tw-debug", "debug", "Use when an issue requires root-cause-first debugging before proposing fixes."}, - {"tw-explain", "explain", "Use when you need a deep explanation of a code symbol and its call graph."}, - {"tw-simplify", "simplify", "Use when you want to simplify code while preserving behavior."}, + {"taskwing:ask", "ask", "Use when you need to search project knowledge (decisions, patterns, constraints)."}, + {"taskwing:remember", "remember", "Use when you want to persist a decision, pattern, or insight to project memory."}, + {"taskwing:next", "next", "Use when you are ready to start the next approved TaskWing task with full context."}, + {"taskwing:done", "done", "Use when implementation is verified and you are ready to complete the current task."}, + {"taskwing:status", "status", "Use when you need current task progress and acceptance criteria status."}, + {"taskwing:plan", "plan", "Use when you need to clarify a goal and build an approved execution plan."}, + {"taskwing:debug", "debug", "Use when an issue requires root-cause-first debugging before proposing fixes."}, + {"taskwing:explain", "explain", "Use when you need a deep explanation of a code symbol and its call graph."}, + {"taskwing:simplify", "simplify", "Use when you want to simplify code while preserving behavior."}, } -// SlashCommandNames returns slash command names (without /tw- prefix), in canonical order. +// SlashCommandNames returns slash command names (without /taskwing: prefix), in canonical order. func SlashCommandNames() []string { names := make([]string, 0, len(SlashCommands)) for _, cmd := range SlashCommands { @@ -526,7 +526,7 @@ func (i *Initializer) createSingleFileInstructions(aiName string, verbose bool) if !hasMarker { if entries, err := os.ReadDir(legacyDir); err == nil { for _, e := range entries { - if strings.HasPrefix(e.Name(), "tw-") && strings.HasSuffix(e.Name(), ".md") { + if (strings.HasPrefix(e.Name(), "tw-") || strings.HasPrefix(e.Name(), "taskwing:")) && strings.HasSuffix(e.Name(), ".md") { looksLikeOurs = true break } @@ -585,7 +585,7 @@ func (i *Initializer) createSingleFileInstructions(aiName string, verbose bool) sb.WriteString("```json\n") sb.WriteString(`{ "servers": { - "taskwing-mcp": { + "taskwing": { "command": "taskwing", "args": ["mcp"] } @@ -596,9 +596,9 @@ func (i *Initializer) createSingleFileInstructions(aiName string, verbose bool) sb.WriteString("### Usage\n\n") sb.WriteString("With MCP configured, you can use TaskWing tools via:\n") - sb.WriteString("- `@mcp taskwing-mcp ask \"query\"` - Search project knowledge\n") - sb.WriteString("- `@mcp taskwing-mcp task {\\\"action\\\":\\\"next\\\"}` - Get next task from plan (session_id auto-derived in MCP session)\n") - sb.WriteString("- `@mcp taskwing-mcp remember \"content\"` - Store knowledge\n") + sb.WriteString("- `@mcp taskwing ask \"query\"` - Search project knowledge\n") + sb.WriteString("- `@mcp taskwing task {\\\"action\\\":\\\"next\\\"}` - Get next task from plan (session_id auto-derived in MCP session)\n") + sb.WriteString("- `@mcp taskwing remember \"content\"` - Store knowledge\n") if err := os.WriteFile(filePath, []byte(sb.String()), 0644); err != nil { // Rollback: restore legacy backup if write fails diff --git a/internal/bootstrap/integration_health.go b/internal/bootstrap/integration_health.go index 28b0b1b..e8ba307 100644 --- a/internal/bootstrap/integration_health.go +++ b/internal/bootstrap/integration_health.go @@ -170,7 +170,7 @@ func EvaluateIntegration(basePath, aiName string, globalMCPExists bool) Integrat Component: AIComponentMCPGlobal, Ownership: OwnershipNone, Status: ComponentStatusMissing, - Reason: "global taskwing-mcp registration missing", + Reason: "global taskwing MCP registration missing", AutoFixable: true, MutatesGlobal: true, AdoptRequired: false, diff --git a/internal/bootstrap/mcp_healthcheck_test.go b/internal/bootstrap/mcp_healthcheck_test.go index 2fa315b..c97e1b5 100644 --- a/internal/bootstrap/mcp_healthcheck_test.go +++ b/internal/bootstrap/mcp_healthcheck_test.go @@ -317,7 +317,7 @@ func TestClaudeDriftDetection(t *testing.T) { Component: AIComponentMCPGlobal, Ownership: OwnershipNone, Status: ComponentStatusMissing, - Reason: "global taskwing-mcp registration missing", + Reason: "global taskwing MCP registration missing", AutoFixable: true, MutatesGlobal: true, }, @@ -374,7 +374,7 @@ func TestClaudeDriftDetection(t *testing.T) { AI: "claude", GlobalMCPDrift: true, Issues: []IntegrationIssue{ - {AI: "claude", Component: AIComponentMCPGlobal, Status: ComponentStatusMissing, Reason: "global taskwing-mcp registration missing", MutatesGlobal: true}, + {AI: "claude", Component: AIComponentMCPGlobal, Status: ComponentStatusMissing, Reason: "global taskwing MCP registration missing", MutatesGlobal: true}, {AI: "claude", Component: AIComponentCommands, Status: ComponentStatusStale, Reason: "managed marker version mismatch"}, {AI: "claude", Component: AIComponentHooks, Status: ComponentStatusInvalid, Reason: "required Stop hook missing"}, }, diff --git a/internal/brief/brief.go b/internal/brief/brief.go index 3ee6a3b..ea6f5d4 100644 --- a/internal/brief/brief.go +++ b/internal/brief/brief.go @@ -16,7 +16,7 @@ import ( // No node IDs, file paths, or embeddings are included. // // This function is used by: -// - /tw-ask slash command (project knowledge brief) +// - /taskwing:ask slash command (project knowledge brief) // - SessionStart hook auto-injection func GenerateCompactBrief(repo *memory.Repository) (string, error) { nodes, err := repo.ListNodes("") diff --git a/internal/mcpcfg/naming.go b/internal/mcpcfg/naming.go index c7ec57b..0e969a8 100644 --- a/internal/mcpcfg/naming.go +++ b/internal/mcpcfg/naming.go @@ -5,7 +5,7 @@ import ( "unicode" ) -const CanonicalServerName = "taskwing-mcp" +const CanonicalServerName = "taskwing" func IsCanonicalServerName(name string) bool { return strings.TrimSpace(strings.ToLower(name)) == CanonicalServerName @@ -16,7 +16,7 @@ func IsLegacyServerName(name string) bool { if normalized == "" || normalized == CanonicalServerName { return false } - if normalized == "taskwing" { + if normalized == "taskwing-mcp" { return true } return strings.HasPrefix(normalized, "taskwing-mcp-") || strings.HasPrefix(normalized, "taskwing-") diff --git a/opencode.json b/opencode.json index d742f60..5f0a391 100644 --- a/opencode.json +++ b/opencode.json @@ -1,7 +1,7 @@ { "$schema": "https://opencode.ai/config.json", "mcp": { - "taskwing-mcp": { + "taskwing": { "type": "local", "command": [ "/Users/josephgoksu/Development/products/taskWing-cli/bin/taskwing", From c4da094c9140c0ac3a96d7cc7153a63cee6c035d Mon Sep 17 00:00:00 2001 From: Joseph Goksu Date: Mon, 9 Mar 2026 00:24:47 +0000 Subject: [PATCH 2/3] fix(bootstrap): use subdirectory for namespaced slash commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix three critical runtime bugs identified by Greptile review: 1. macOS filesystem failure: colons in filenames (taskwing:ask.md) are invalid on HFS+/APFS. Now uses subdirectory approach: .claude/commands/taskwing/ask.md → /taskwing:ask 2. OpenCode validation failure: regex rejects colons. Now uses SlashCmd field (ask, next, etc.) instead of BaseName for OpenCode filenames. 3. Migration cleanup: managedSlashCommandBases() now includes legacy tw-* names so old files are properly pruned during upgrade. Also fixes misleading comment on SlashCommandNames(). --- internal/bootstrap/initializer.go | 89 +++++++++++++++++----- internal/bootstrap/integration_health.go | 20 ++++- internal/bootstrap/mcp_healthcheck_test.go | 14 ++-- 3 files changed, 95 insertions(+), 28 deletions(-) diff --git a/internal/bootstrap/initializer.go b/internal/bootstrap/initializer.go index f509d79..8f0a722 100644 --- a/internal/bootstrap/initializer.go +++ b/internal/bootstrap/initializer.go @@ -274,7 +274,7 @@ var SlashCommands = []SlashCommand{ {"taskwing:simplify", "simplify", "Use when you want to simplify code while preserving behavior."}, } -// SlashCommandNames returns slash command names (without /taskwing: prefix), in canonical order. +// SlashCommandNames returns slash command short names (e.g., "ask", "next", "done"), in canonical order. func SlashCommandNames() []string { names := make([]string, 0, len(SlashCommands)) for _, cmd := range SlashCommands { @@ -373,31 +373,38 @@ func ExpectedCommandCount() int { return len(SlashCommands) } +// slashCommandNamespace is the subdirectory name used for namespaced slash commands. +// e.g., .claude/commands/taskwing/ask.md → /taskwing:ask +const slashCommandNamespace = "taskwing" + func expectedSlashCommandFiles(ext string) map[string]struct{} { expected := make(map[string]struct{}, len(SlashCommands)) for _, cmd := range SlashCommands { - expected[cmd.BaseName+ext] = struct{}{} + // Files live in the taskwing/ subdirectory, keyed by SlashCmd + expected[cmd.SlashCmd+ext] = struct{}{} } return expected } func managedSlashCommandBases() map[string]struct{} { - managed := make(map[string]struct{}, len(SlashCommands)) + managed := make(map[string]struct{}, len(SlashCommands)*2) for _, cmd := range SlashCommands { - managed[cmd.BaseName] = struct{}{} + managed[cmd.SlashCmd] = struct{}{} + // Also recognize legacy tw-* names for migration cleanup + managed["tw-"+cmd.SlashCmd] = struct{}{} } return managed } func pruneStaleSlashCommands(commandsDir, ext string, verbose bool) error { + managedBases := managedSlashCommandBases() + + // Prune legacy flat tw-* files from the commands directory root entries, err := os.ReadDir(commandsDir) if err != nil { return fmt.Errorf("read commands dir: %w", err) } - expected := expectedSlashCommandFiles(ext) - managedBases := managedSlashCommandBases() - for _, e := range entries { if e.IsDir() { continue @@ -407,19 +414,53 @@ func pruneStaleSlashCommands(commandsDir, ext string, verbose bool) error { continue } base := strings.TrimSuffix(name, ext) + // Only prune files we recognize as managed (including legacy tw-* names) if _, known := managedBases[base]; !known { continue } + // Legacy tw-* files should always be removed (replaced by taskwing/ subdir) + if strings.HasPrefix(base, "tw-") { + fullPath := filepath.Join(commandsDir, name) + if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("remove legacy slash command %s: %w", name, err) + } + if verbose { + fmt.Printf(" ✓ Removed legacy command %s\n", name) + } + } + } + + // Prune stale files inside the taskwing/ namespace subdirectory + nsDir := filepath.Join(commandsDir, slashCommandNamespace) + nsEntries, err := os.ReadDir(nsDir) + if err != nil { + // Subdirectory may not exist yet (first install) — not an error + return nil + } + + expected := expectedSlashCommandFiles(ext) + for _, e := range nsEntries { + if e.IsDir() { + continue + } + name := e.Name() + if filepath.Ext(name) != ext { + continue + } if _, keep := expected[name]; keep { continue } + base := strings.TrimSuffix(name, ext) + if _, known := managedBases[base]; !known { + continue + } - fullPath := filepath.Join(commandsDir, name) + fullPath := filepath.Join(nsDir, name) if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) { return fmt.Errorf("remove stale slash command %s: %w", name, err) } if verbose { - fmt.Printf(" ✓ Removed stale command %s\n", filepath.Join(commandsDir, name)) + fmt.Printf(" ✓ Removed stale command %s/%s\n", slashCommandNamespace, name) } } @@ -462,17 +503,24 @@ func (i *Initializer) CreateSlashCommands(aiName string, verbose bool) error { isTOML := cfg.fileExt == ".toml" + // Create namespace subdirectory (e.g., .claude/commands/taskwing/) + // This produces namespaced commands like /taskwing:ask, /taskwing:next + nsDir := filepath.Join(commandsDir, slashCommandNamespace) + if err := os.MkdirAll(nsDir, 0755); err != nil { + return fmt.Errorf("create namespace dir %s: %w", slashCommandNamespace, err) + } + for _, cmd := range SlashCommands { var content, fileName string if isTOML { - fileName = cmd.BaseName + ".toml" + fileName = cmd.SlashCmd + ".toml" content = fmt.Sprintf(`description = "%s" prompt = """!{taskwing slash %s}""" `, cmd.Description, cmd.SlashCmd) } else { - fileName = cmd.BaseName + ".md" + fileName = cmd.SlashCmd + ".md" content = fmt.Sprintf(`--- description: %s --- @@ -480,12 +528,12 @@ description: %s `, cmd.Description, cmd.SlashCmd) } - filePath := filepath.Join(commandsDir, fileName) + filePath := filepath.Join(nsDir, fileName) if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { return fmt.Errorf("create %s: %w", fileName, err) } if verbose { - fmt.Printf(" ✓ Created %s/%s\n", cfg.commandsDir, fileName) + fmt.Printf(" ✓ Created %s/%s/%s\n", cfg.commandsDir, slashCommandNamespace, fileName) } } @@ -526,7 +574,7 @@ func (i *Initializer) createSingleFileInstructions(aiName string, verbose bool) if !hasMarker { if entries, err := os.ReadDir(legacyDir); err == nil { for _, e := range entries { - if (strings.HasPrefix(e.Name(), "tw-") || strings.HasPrefix(e.Name(), "taskwing:")) && strings.HasSuffix(e.Name(), ".md") { + if (strings.HasPrefix(e.Name(), "tw-") || e.Name() == slashCommandNamespace) && (e.IsDir() || strings.HasSuffix(e.Name(), ".md")) { looksLikeOurs = true break } @@ -659,10 +707,11 @@ func (i *Initializer) createOpenCodeCommands(aiName string, verbose bool) error // Generate a command for each slash command // OpenCode format: .opencode/commands/.md with description frontmatter + // OpenCode uses flat filenames (no subdirectory namespace), so we use SlashCmd directly for _, cmd := range SlashCommands { - // Validate command name against OpenCode requirements - if !openCodeSkillNameRegex.MatchString(cmd.BaseName) { - return fmt.Errorf("invalid OpenCode command name '%s': must match ^[a-z0-9]+(-[a-z0-9]+)*$ (lowercase alphanumeric with hyphens)", cmd.BaseName) + // Validate command name against OpenCode requirements (SlashCmd, not BaseName) + if !openCodeSkillNameRegex.MatchString(cmd.SlashCmd) { + return fmt.Errorf("invalid OpenCode command name '%s': must match ^[a-z0-9]+(-[a-z0-9]+)*$ (lowercase alphanumeric with hyphens)", cmd.SlashCmd) } // OpenCode command format: YAML frontmatter with description only @@ -675,13 +724,13 @@ description: %s `, cmd.Description, cmd.SlashCmd) // Write .md file directly in commands directory - filePath := filepath.Join(commandsDir, cmd.BaseName+".md") + filePath := filepath.Join(commandsDir, cmd.SlashCmd+".md") if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { - return fmt.Errorf("create %s.md: %w", cmd.BaseName, err) + return fmt.Errorf("create %s.md: %w", cmd.SlashCmd, err) } if verbose { - fmt.Printf(" ✓ Created %s/%s.md\n", cfg.commandsDir, cmd.BaseName) + fmt.Printf(" ✓ Created %s/%s.md\n", cfg.commandsDir, cmd.SlashCmd) } } diff --git a/internal/bootstrap/integration_health.go b/internal/bootstrap/integration_health.go index e8ba307..f3f8d45 100644 --- a/internal/bootstrap/integration_health.go +++ b/internal/bootstrap/integration_health.go @@ -367,8 +367,9 @@ func evalCommandsComponent(basePath, aiName string, cfg aiHelperConfig) (Compone } expected := expectedSlashCommandFiles(cfg.fileExt) + + // Check for legacy flat tw-* files in commands root entries, _ := os.ReadDir(commandsDir) - actual := map[string]struct{}{} commandFileCount := 0 taskwingLike := false for _, entry := range entries { @@ -378,13 +379,28 @@ func evalCommandsComponent(basePath, aiName string, cfg aiHelperConfig) (Compone name := entry.Name() if strings.HasSuffix(name, cfg.fileExt) { commandFileCount++ - actual[name] = struct{}{} if strings.HasPrefix(strings.TrimSuffix(name, cfg.fileExt), "tw-") { taskwingLike = true } } } + // Check the taskwing/ namespace subdirectory for expected command files + actual := map[string]struct{}{} + nsDir := filepath.Join(commandsDir, slashCommandNamespace) + nsEntries, _ := os.ReadDir(nsDir) + for _, entry := range nsEntries { + if entry.IsDir() { + continue + } + name := entry.Name() + if strings.HasSuffix(name, cfg.fileExt) { + commandFileCount++ + actual[name] = struct{}{} + taskwingLike = true + } + } + missing := make([]string, 0) for name := range expected { if _, ok := actual[name]; !ok { diff --git a/internal/bootstrap/mcp_healthcheck_test.go b/internal/bootstrap/mcp_healthcheck_test.go index c97e1b5..74e42b1 100644 --- a/internal/bootstrap/mcp_healthcheck_test.go +++ b/internal/bootstrap/mcp_healthcheck_test.go @@ -207,7 +207,8 @@ func TestClaudeDriftDetection(t *testing.T) { t.Run("managed_commands_stale_version_triggers_drift", func(t *testing.T) { basePath := t.TempDir() commandsDir := filepath.Join(basePath, ".claude", "commands") - if err := os.MkdirAll(commandsDir, 0o755); err != nil { + nsDir := filepath.Join(commandsDir, slashCommandNamespace) + if err := os.MkdirAll(nsDir, 0o755); err != nil { t.Fatal(err) } @@ -217,9 +218,9 @@ func TestClaudeDriftDetection(t *testing.T) { t.Fatal(err) } - // Write all expected command files so the version check is reached + // Write all expected command files in the taskwing/ subdirectory for name := range expectedSlashCommandFiles(".md") { - if err := os.WriteFile(filepath.Join(commandsDir, name), []byte("test"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(nsDir, name), []byte("test"), 0o644); err != nil { t.Fatal(err) } } @@ -253,7 +254,8 @@ func TestClaudeDriftDetection(t *testing.T) { t.Run("local_configured_but_global_mcp_missing_triggers_drift", func(t *testing.T) { basePath := t.TempDir() commandsDir := filepath.Join(basePath, ".claude", "commands") - if err := os.MkdirAll(commandsDir, 0o755); err != nil { + nsDir := filepath.Join(commandsDir, slashCommandNamespace) + if err := os.MkdirAll(nsDir, 0o755); err != nil { t.Fatal(err) } @@ -264,9 +266,9 @@ func TestClaudeDriftDetection(t *testing.T) { t.Fatal(err) } - // Write expected slash command files + // Write expected slash command files in the taskwing/ subdirectory for name := range expectedSlashCommandFiles(".md") { - if err := os.WriteFile(filepath.Join(commandsDir, name), []byte("test"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(nsDir, name), []byte("test"), 0o644); err != nil { t.Fatal(err) } } From ceb2a82996592c4334f66fa6550ff092bd3e2223 Mon Sep 17 00:00:00 2001 From: Joseph Goksu Date: Mon, 9 Mar 2026 00:42:54 +0000 Subject: [PATCH 3/3] fix(config): remove hardcoded dev path from opencode.json Replace absolute dev binary path with portable "taskwing" command name so the config works on any machine. --- opencode.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opencode.json b/opencode.json index 5f0a391..8346e65 100644 --- a/opencode.json +++ b/opencode.json @@ -4,7 +4,7 @@ "taskwing": { "type": "local", "command": [ - "/Users/josephgoksu/Development/products/taskWing-cli/bin/taskwing", + "taskwing", "mcp" ], "timeout": 5000