Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ on:
branches: [main]

jobs:
docs-consistency:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check docs consistency
run: ./scripts/check-doc-consistency.sh

lint:
runs-on: ubuntu-latest
steps:
Expand Down
5 changes: 1 addition & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- `make test-quick`: fast local checks during iteration.
- `make lint`: runs formatting and static analysis (`go fmt`, `go vet`, `staticcheck`, optional `golangci-lint`).
- `go test ./...`: baseline CI-style test run.
- `./scripts/check-doc-consistency.sh`: validates Markdown/doc sync rules used by CI.


## Coding Style & Naming Conventions

Expand Down Expand Up @@ -94,11 +94,8 @@ Brand names and logos are trademarks of their respective owners; usage here indi
<!-- TASKWING_COMMANDS_START -->

- taskwing bootstrap
- taskwing goal "<goal>"
- taskwing ask "<query>"
- taskwing task
- taskwing plan status
- taskwing slash
- taskwing mcp
- taskwing doctor
- taskwing config
Expand Down
9 changes: 1 addition & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,11 @@ TaskWing is a local-first AI knowledge layer. It extracts architectural decision
cmd/ # Cobra CLI commands
├── root.go # Base command, global flags (--json, --verbose, --preview, --quiet)
├── bootstrap.go # Auto-generate knowledge from repo
├── goal.go # Goal-first flow: clarify -> generate -> activate
├── knowledge.go # View stored project knowledge nodes
├── plan.go # Plan lifecycle management
├── task.go # Task lifecycle management
├── slash.go # Slash command content for assistants
├── mcp_server.go # MCP server for AI tool integration
├── doctor.go # Diagnostics and integration repair
├── config.go # Provider and runtime configuration
├── start.go # Local API/dashboard runtime
├── hook.go # Hook handlers used by assistant integrations
└── version.go # Version output

Expand Down Expand Up @@ -235,7 +231,7 @@ Increment when:

**NOT MINOR**: Internal refactors, new internal modules, code reorganization

Examples: new `taskwing goal` command, new `--format` flag, adding Gemini provider
Examples: new `taskwing config` subcommand, new `--format` flag, adding Gemini provider

### MAJOR (X.0.0) - Breaking changes only

Expand Down Expand Up @@ -353,11 +349,8 @@ Brand names and logos are trademarks of their respective owners; usage here indi

<!-- TASKWING_COMMANDS_START -->
- `taskwing bootstrap`
- `taskwing goal "<goal>"`
- `taskwing ask "<query>"`
- `taskwing task`
- `taskwing plan status`
- `taskwing slash`
- `taskwing mcp`
- `taskwing doctor`
- `taskwing config`
Expand Down
5 changes: 0 additions & 5 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,9 @@ The system is composed of a CLI tool with an embedded MCP server and a web dashb
| Command | Description |
| :------------------- | :---------------------------------------- |
| `taskwing bootstrap` | Initialize project memory |
| `taskwing goal` | Create and activate a plan |
| `taskwing plan` | Manage development plans |
| `taskwing task` | Manage execution tasks |
| `taskwing start` | Start API/watch/dashboard services |
| `taskwing slash` | Output slash command prompts for AI tools |

### Frontend (`dashboard/`)

Expand Down Expand Up @@ -232,11 +230,8 @@ Brand names and logos are trademarks of their respective owners; usage here indi

<!-- TASKWING_COMMANDS_START -->
- `taskwing bootstrap`
- `taskwing goal "<goal>"`
- `taskwing ask "<query>"`
- `taskwing task`
- `taskwing plan status`
- `taskwing slash`
- `taskwing mcp`
- `taskwing doctor`
- `taskwing config`
Expand Down
10 changes: 2 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,8 @@ taskwing bootstrap
# 2. Connect to your AI tool
taskwing mcp install claude # or: cursor, gemini, codex, copilot, opencode

# 3. Set a goal and go
taskwing goal "Add Stripe billing"
# -> Plan decomposed into 5 executable tasks

# 4. Execute with your AI assistant
# 3. Plan and execute with your AI assistant
/taskwing:plan # Create a plan via MCP
/taskwing:next # Get next task with full context
# ...work...
/taskwing:done # Mark complete, advance to next
Expand Down Expand Up @@ -252,11 +249,8 @@ Or configure interactively: `taskwing config`

<!-- TASKWING_COMMANDS_START -->
- `taskwing bootstrap`
- `taskwing goal "<goal>"`
- `taskwing ask "<query>"`
- `taskwing task`
- `taskwing plan status`
- `taskwing slash`
- `taskwing mcp`
- `taskwing doctor`
- `taskwing config`
Expand Down
116 changes: 80 additions & 36 deletions cmd/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
TraceFile: getStringFlag(cmd, "trace-file"),
Verbose: viper.GetBool("verbose"),
Quiet: viper.GetBool("quiet"),
Debug: getBoolFlag(cmd, "debug"),
Debug: getBoolFlag(cmd, "debug"),
}

// Validate flags early - fail fast on contradictions
Expand Down Expand Up @@ -158,28 +158,28 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
// ═══════════════════════════════════════════════════════════════════════
plan := bootstrap.DecidePlan(snapshot, flags)

// Always show plan summary (even in quiet mode, single line)
fmt.Print(bootstrap.FormatPlanSummary(plan, flags.Quiet))

// Handle error mode
// Handle error mode early (before any output)
if plan.Mode == bootstrap.ModeError {
fmt.Print(bootstrap.FormatPlanSummary(plan, flags.Quiet))
return plan.Error
}

// Handle preview mode - show plan and exit
if flags.Preview {
fmt.Println("\n💡 Preview mode - no changes made.")
return nil
}

// Handle NoOp mode
// Handle NoOp mode early
if plan.Mode == bootstrap.ModeNoOp {
fmt.Print(bootstrap.FormatPlanSummary(plan, flags.Quiet))
if !flags.Quiet {
fmt.Println("\n✅ Nothing to do - configuration is up to date.")
}
return nil
}

// Handle preview mode
if flags.Preview {
fmt.Print(bootstrap.FormatPlanSummary(plan, flags.Quiet))
fmt.Println("\n💡 Preview mode - no changes made.")
return nil
}

// ═══════════════════════════════════════════════════════════════════════
// PHASE 3: Execute Plan
// ═══════════════════════════════════════════════════════════════════════
Expand Down Expand Up @@ -213,6 +213,9 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
}
}

// Show plan summary AFTER repo selection so it reflects the chosen scope
fmt.Print(bootstrap.FormatPlanSummary(plan, flags.Quiet))

// Execute actions in order
for _, action := range plan.Actions {
if err := executeAction(cmd.Context(), action, svc, cwd, flags, plan, llmCfg); err != nil {
Expand All @@ -222,24 +225,63 @@ func runBootstrap(cmd *cobra.Command, args []string) error {

// Final success message
if !flags.Quiet {
if len(plan.ManagedDriftAIs) > 0 {
fmt.Printf("managed_drift_fixed: %s\n", strings.Join(plan.ManagedDriftAIs, ", "))
}
if len(plan.UnmanagedDriftAIs) > 0 {
fmt.Printf("unmanaged_drift_detected: %s\n", strings.Join(plan.UnmanagedDriftAIs, ", "))
fmt.Println(" ↳ Run `taskwing doctor --fix --adopt-unmanaged` to claim and repair unmanaged TaskWing-like configs.")
}
if len(plan.GlobalMCPDriftAIs) > 0 {
fmt.Printf("global_mcp_drift_detected: %s\n", strings.Join(plan.GlobalMCPDriftAIs, ", "))
fmt.Println(" ↳ Run `taskwing doctor --fix` to repair global MCP registration.")
}
fmt.Println()
fmt.Println("✅ Bootstrap complete!")
printPostBootstrapSummary()
}

return nil
}

// printPostBootstrapSummary shows a compact knowledge summary after bootstrap
// so users immediately see what was extracted from their codebase.
func printPostBootstrapSummary() {
repo, err := openRepo()
if err != nil {
return // non-fatal: skip summary if repo can't be opened
}
defer repo.Close()

nodes, err := repo.ListNodes("")
if err != nil || len(nodes) == 0 {
return
}

// Count by type using human-readable labels
byType := make(map[string]int)
for _, n := range nodes {
t := n.Type
if t == "" {
t = "unknown"
}
byType[t]++
}

// Build stats with spelled-out type names
var stats []string
typeLabels := map[string]string{
"decision": "decisions", "feature": "features", "constraint": "constraints",
"pattern": "patterns", "plan": "plans", "note": "notes",
"metadata": "metadata", "documentation": "docs",
}
for _, t := range memory.AllNodeTypes() {
if count := byType[t]; count > 0 {
label := typeLabels[t]
if label == "" {
label = t
}
if count == 1 {
// Singularize
label = strings.TrimSuffix(label, "s")
}
stats = append(stats, fmt.Sprintf("%d %s", count, label))
}
}

fmt.Printf("\n Knowledge: %d nodes (%s)\n", len(nodes), strings.Join(stats, ", "))
fmt.Println(" Run 'taskwing knowledge' to explore, or use /taskwing:ask in your AI tool.")
}

// executeAction executes a single bootstrap action.
func executeAction(ctx context.Context, action bootstrap.Action, svc *bootstrap.Service, cwd string, flags bootstrap.Flags, plan *bootstrap.Plan, llmCfg llm.Config) error {
switch action {
Expand Down Expand Up @@ -370,17 +412,13 @@ func executeGenerateAIConfigs(svc *bootstrap.Service, flags bootstrap.Flags, pla
return nil
}

// Generate configs for each target AI
if !flags.Quiet {
fmt.Printf("🔧 Regenerating AI configurations for: %s\n", strings.Join(targetAIs, ", "))
}

// Generate configs (plan summary already showed which AIs are being updated)
if err := svc.RegenerateAIConfigs(flags.Verbose, targetAIs); err != nil {
return fmt.Errorf("regenerate AI configs failed: %w", err)
}

if !flags.Quiet {
fmt.Println("✓ AI configurations regenerated")
fmt.Printf("✓ AI configurations updated: %s\n", strings.Join(targetAIs, ", "))
}
return nil
}
Expand Down Expand Up @@ -515,7 +553,7 @@ func runAgentTUI(ctx context.Context, svc *bootstrap.Service, cwd string, llmCfg
ui.RenderPageHeader("TaskWing Bootstrap", fmt.Sprintf("Using: %s (%s)", llmCfg.Model, llmCfg.Provider))

projectName := filepath.Base(cwd)
allAgents := bootstrap.NewDefaultAgents(llmCfg, cwd)
allAgents := bootstrap.NewDefaultAgents(llmCfg, cwd, nil)
defer core.CloseAgents(allAgents)

// Open repository for checkpoint tracking
Expand Down Expand Up @@ -672,9 +710,11 @@ func runMultiRepoBootstrap(ctx context.Context, svc *bootstrap.Service, ws *proj
fmt.Println("")
ui.RenderPageHeader("TaskWing Multi-Repo Bootstrap", fmt.Sprintf("Workspace: %s | Services: %d", ws.Name, ws.ServiceCount()))

fmt.Printf("📦 Analyzing %d services. Running parallel analysis...\n", ws.ServiceCount())
fmt.Printf("📦 Analyzing %d services...\n", ws.ServiceCount())

findings, relationships, errs, err := svc.RunMultiRepoAnalysis(ctx, ws)
findings, relationships, errs, err := svc.RunMultiRepoAnalysis(ctx, ws, func(name, status string) {
fmt.Printf(" %s: %s\n", name, status)
})
if err != nil {
return err
}
Expand All @@ -686,14 +726,18 @@ func runMultiRepoBootstrap(ctx context.Context, svc *bootstrap.Service, ws *proj
}
}

fmt.Printf("📊 Aggregated: %d findings from %d services\n", len(findings), ws.ServiceCount()-len(errs))

if preview {
fmt.Println("\n💡 This was a preview. Run 'taskwing bootstrap' to save to memory.")
fmt.Printf("\n📊 Preview: %d findings from %d services\n", len(findings), ws.ServiceCount()-len(errs))
fmt.Println("💡 This was a preview. Run 'taskwing bootstrap' to save to memory.")
return nil
}

return svc.IngestDirectly(ctx, findings, relationships, viper.GetBool("quiet"))
if err := svc.IngestDirectly(ctx, findings, relationships, viper.GetBool("quiet")); err != nil {
return err
}

// Don't print completion here -- runBootstrap prints it after all actions finish.
return nil
}

// promptRepoSelection prompts the user to select which repositories to bootstrap.
Expand Down
6 changes: 3 additions & 3 deletions cmd/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ func checkActivePlan() DoctorCheck {
Name: "Active Plan",
Status: "warn",
Message: "No active plan",
Hint: "Run: taskwing goal \"your goal\"",
Hint: "Use /taskwing:plan in your AI tool to create a plan",
}
}

Expand Down Expand Up @@ -516,8 +516,8 @@ func printNextSteps(checks []DoctorCheck) {
fmt.Println()
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: /taskwing:next")
fmt.Println(" 1. Open your AI tool and use /taskwing:plan to create a plan")
fmt.Println(" 2. Run /taskwing:next to start the first task")
} else if !hasSession {
fmt.Println(" 1. Open Claude Code (session will auto-initialize)")
fmt.Println(" 2. Run: /taskwing:next")
Expand Down
Loading
Loading