Skip to content

Commit 3293462

Browse files
committed
cleanup: harden contracts, unify doctor health checks, and fix lint
1 parent 91c6434 commit 3293462

52 files changed

Lines changed: 3949 additions & 1661 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,17 +307,18 @@ TaskWing provides project memory for AI assistants via MCP tools and slash comma
307307
|------|-------------|
308308
| `recall` | Retrieve project knowledge (decisions, patterns, constraints) |
309309
| `task` | Unified task lifecycle (next, current, start, complete) |
310-
| `plan` | Plan management (clarify, decompose, expand, generate, finalize, audit) |
310+
| `plan` | Plan management (clarify, generate, audit) |
311311
| `code` | Code intelligence (find, search, explain, callers, impact, simplify) |
312312
| `debug` | Diagnose issues systematically with AI-powered analysis |
313313
| `remember` | Store knowledge in project memory |
314314

315315
### CLI Commands
316316
```bash
317-
taskwing bootstrap # Initialize project memory (first-time setup)
318-
taskwing goal "goal" # Create and activate a development plan
319-
taskwing task list # List tasks from active plan
320-
taskwing plan status # Show progress of active plan
317+
tw bootstrap # Initialize project memory (first-time setup)
318+
tw context "query" # Search knowledge semantically
319+
tw add "content" # Add knowledge to memory
320+
tw plan new "goal" # Create development plan
321+
tw task list # List tasks from active plan
321322
```
322323

323324
### Autonomous Task Execution (Hooks)

GEMINI.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,17 +200,18 @@ TaskWing provides project memory for AI assistants via MCP tools and slash comma
200200
|------|-------------|
201201
| `recall` | Retrieve project knowledge (decisions, patterns, constraints) |
202202
| `task` | Unified task lifecycle (next, current, start, complete) |
203-
| `plan` | Plan management (clarify, decompose, expand, generate, finalize, audit) |
203+
| `plan` | Plan management (clarify, generate, audit) |
204204
| `code` | Code intelligence (find, search, explain, callers, impact, simplify) |
205205
| `debug` | Diagnose issues systematically with AI-powered analysis |
206206
| `remember` | Store knowledge in project memory |
207207

208208
### CLI Commands
209209
```bash
210-
taskwing bootstrap # Initialize project memory (first-time setup)
211-
taskwing goal "goal" # Create and activate a development plan
212-
taskwing task list # List tasks from active plan
213-
taskwing plan status # Show progress of active plan
210+
tw bootstrap # Initialize project memory (first-time setup)
211+
tw context "query" # Search knowledge semantically
212+
tw add "content" # Add knowledge to memory
213+
tw plan new "goal" # Create development plan
214+
tw task list # List tasks from active plan
214215
```
215216

216217
### Autonomous Task Execution (Hooks)

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ taskwing goal "Add Stripe billing"
2121
/tw-done
2222
```
2323

24+
## Bootstrap and Repair Model
25+
26+
- `taskwing bootstrap` auto-repairs only TaskWing-managed local AI assets.
27+
- If drift is unmanaged (marker missing/user-owned), bootstrap warns and does not mutate files.
28+
- Global MCP repair is explicit via `taskwing doctor --fix`.
29+
- Unmanaged adoption is explicit via `taskwing doctor --fix --adopt-unmanaged`.
30+
31+
Recovery examples:
32+
33+
```bash
34+
# Inspect current health
35+
taskwing doctor
36+
37+
# Apply safe repairs for managed drift and MCP config
38+
taskwing doctor --fix --yes
39+
40+
# Explicitly adopt unmanaged TaskWing-like files, then repair
41+
taskwing doctor --fix --adopt-unmanaged --yes --ai claude
42+
```
43+
2444
## What TaskWing Does
2545

2646
- Stores architecture decisions, constraints, and patterns in local project memory.
@@ -44,7 +64,7 @@ taskwing goal "Add Stripe billing"
4464
```json
4565
{
4666
"mcpServers": {
47-
"taskwing": {
67+
"taskwing-mcp": {
4868
"command": "taskwing",
4969
"args": ["mcp"]
5070
}

cmd/bootstrap.go

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,14 @@ var bootstrapCmd = &cobra.Command{
3636
Bootstrap analyzes your codebase to extract architectural knowledge:
3737
• Creates .taskwing/ directory structure
3838
• Sets up AI assistant integration (Claude, Cursor, etc.)
39+
• Auto-repairs managed local AI config drift
3940
• Indexes code symbols (functions, types, etc.)
4041
• Analyzes code patterns and architecture (requires API key)
4142
• Extracts decisions and understands WHY choices were made
4243
44+
Bootstrap does NOT adopt unmanaged AI config automatically and does NOT mutate
45+
global MCP in run mode. Use "taskwing doctor --fix" for explicit repair flows.
46+
4347
Requires: OPENAI_API_KEY or TASKWING_LLM_APIKEY environment variable.
4448
4549
Use --skip-analyze for CI/testing (deterministic, no LLM).`,
@@ -132,25 +136,22 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
132136
// ═══════════════════════════════════════════════════════════════════════
133137
// PHASE 1: Probe Environment (no side effects)
134138
// ═══════════════════════════════════════════════════════════════════════
139+
existingGlobalAIs := detectExistingMCPConfigs()
140+
globalMCPSet := make(map[string]bool, len(existingGlobalAIs))
141+
for _, ai := range existingGlobalAIs {
142+
globalMCPSet[ai] = true
143+
}
144+
previousDetector := bootstrap.GlobalMCPDetector
145+
bootstrap.GlobalMCPDetector = func(aiName string) bool {
146+
return globalMCPSet[aiName]
147+
}
148+
defer func() { bootstrap.GlobalMCPDetector = previousDetector }()
149+
135150
snapshot, err := bootstrap.ProbeEnvironment(cwd)
136151
if err != nil {
137152
return fmt.Errorf("probe environment: %w", err)
138153
}
139154

140-
// Enhance snapshot with global MCP detection (uses existing helper)
141-
existingGlobalAIs := detectExistingMCPConfigs()
142-
if len(existingGlobalAIs) > 0 {
143-
snapshot.HasAnyGlobalMCP = true
144-
snapshot.GlobalMCPAIs = existingGlobalAIs
145-
// Update AI health with global MCP info
146-
for _, ai := range existingGlobalAIs {
147-
if health, ok := snapshot.AIHealth[ai]; ok {
148-
health.GlobalMCPExists = true
149-
snapshot.AIHealth[ai] = health
150-
}
151-
}
152-
}
153-
154155
// ═══════════════════════════════════════════════════════════════════════
155156
// PHASE 2: Decide Plan (pure function, deterministic)
156157
// ═══════════════════════════════════════════════════════════════════════
@@ -203,6 +204,17 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
203204

204205
// Final success message
205206
if !flags.Quiet {
207+
if len(plan.ManagedDriftAIs) > 0 {
208+
fmt.Printf("managed_drift_fixed: %s\n", strings.Join(plan.ManagedDriftAIs, ", "))
209+
}
210+
if len(plan.UnmanagedDriftAIs) > 0 {
211+
fmt.Printf("unmanaged_drift_detected: %s\n", strings.Join(plan.UnmanagedDriftAIs, ", "))
212+
fmt.Println(" ↳ Run `taskwing doctor --fix --adopt-unmanaged` to claim and repair unmanaged TaskWing-like configs.")
213+
}
214+
if len(plan.GlobalMCPDriftAIs) > 0 {
215+
fmt.Printf("global_mcp_drift_detected: %s\n", strings.Join(plan.GlobalMCPDriftAIs, ", "))
216+
fmt.Println(" ↳ Run `taskwing doctor --fix` to repair global MCP registration.")
217+
}
206218
fmt.Println()
207219
fmt.Println("✅ Bootstrap complete!")
208220
}

cmd/bootstrap_test.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,11 @@ func TestInstallMCPServers_OpenCode(t *testing.T) {
4545
t.Fatal("MCP section is nil")
4646
}
4747

48-
// Server name should include project directory name
49-
expectedServerName := "taskwing-mcp-" + filepath.Base(tmpDir)
48+
// Server name must be canonical (strict naming policy)
49+
expectedServerName := "taskwing-mcp"
5050
serverCfg, ok := config.MCP[expectedServerName]
5151
if !ok {
52-
// Check if any taskwing-mcp server exists
53-
found := false
54-
for name := range config.MCP {
55-
if len(name) >= 12 && name[:12] == "taskwing-mcp" {
56-
found = true
57-
serverCfg = config.MCP[name]
58-
break
59-
}
60-
}
61-
if !found {
62-
t.Fatalf("No taskwing-mcp server entry found in MCP section. Got: %v", config.MCP)
63-
}
52+
t.Fatalf("Canonical taskwing-mcp server entry missing in MCP section. Got: %v", config.MCP)
6453
}
6554

6655
// Verify type is "local"

0 commit comments

Comments
 (0)