Spaces:
Paused
Paused
| package lib | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| "log" | |
| "os" | |
| "path/filepath" | |
| "plandex-cli/api" | |
| "plandex-cli/auth" | |
| "plandex-cli/format" | |
| "plandex-cli/fs" | |
| "plandex-cli/term" | |
| "plandex-cli/types" | |
| "strconv" | |
| "strings" | |
| shared "plandex-shared" | |
| "github.com/fatih/color" | |
| "github.com/olekukonko/tablewriter" | |
| ) | |
| var CurrentProjectId string | |
| var CurrentPlanId string | |
| var CurrentBranch string | |
| var HomeCurrentProjectDir string | |
| var HomeCurrentPlanPath string | |
| func MustResolveOrCreateProject() { | |
| resolveProject(true, true) | |
| } | |
| func MustResolveProject() { | |
| resolveProject(true, false) | |
| } | |
| func MaybeResolveProject() { | |
| resolveProject(false, false) | |
| } | |
| func resolveProject(mustResolve, shouldCreate bool) { | |
| if fs.PlandexDir == "" { | |
| var err error | |
| if shouldCreate { | |
| _, _, err = fs.FindOrCreatePlandex() | |
| } else { | |
| fs.FindPlandexDir() | |
| } | |
| if err != nil && mustResolve { | |
| term.OutputErrorAndExit("error finding or creating plandex: %v", err) | |
| } | |
| } | |
| if (fs.PlandexDir == "" || fs.ProjectRoot == "") && mustResolve { | |
| fmt.Printf( | |
| "🤷♂️ No plans in current directory\nTry %s to create a plan or %s to see plans in nearby directories\n", | |
| color.New(color.Bold, term.ColorHiCyan).Sprint("plandex new"), | |
| color.New(color.Bold, term.ColorHiCyan).Sprint("plandex plans")) | |
| os.Exit(0) | |
| } | |
| if fs.PlandexDir == "" { | |
| return | |
| } | |
| MigrateLegacyProjectFile(auth.Current.UserId) | |
| // check if projects-v2.json exists in PlandexDir | |
| path := filepath.Join(fs.PlandexDir, "projects-v2.json") | |
| _, err := os.Stat(path) | |
| if os.IsNotExist(err) { | |
| log.Println("projects-v2.json does not exist") | |
| log.Println("Initializing project") | |
| mustInitProject(nil) | |
| } else if err != nil { | |
| term.OutputErrorAndExit("error checking if projects-v2.json exists: %v", err) | |
| } | |
| var settings *types.CurrentProjectSettings | |
| var loadProjectSettings func() | |
| loadProjectSettings = func() { | |
| // read projects-v2.json | |
| bytes, err := os.ReadFile(path) | |
| if err != nil { | |
| term.OutputErrorAndExit("error reading projects-v2.json: %v", err) | |
| } | |
| var settingsByAccount types.CurrentProjectSettingsByAccount | |
| err = json.Unmarshal(bytes, &settingsByAccount) | |
| if err != nil { | |
| term.OutputErrorAndExit("error unmarshalling projects-v2.json: %v", err) | |
| } | |
| settings = settingsByAccount[auth.Current.UserId] | |
| if settings == nil { | |
| mustInitProject(&settingsByAccount) | |
| loadProjectSettings() | |
| } | |
| } | |
| loadProjectSettings() | |
| CurrentProjectId = settings.Id | |
| MigrateLegacyCurrentPlanFile(auth.Current.UserId) | |
| HomeCurrentProjectDir = filepath.Join(fs.HomePlandexDir, CurrentProjectId) | |
| HomeCurrentPlanPath = filepath.Join(HomeCurrentProjectDir, "current-plans-v2.json") | |
| err = os.MkdirAll(HomeCurrentProjectDir, os.ModePerm) | |
| if err != nil { | |
| term.OutputErrorAndExit("error creating project dir: %v", err) | |
| } | |
| MustLoadCurrentPlan() | |
| MigrateLegacyPlanSettingsFile(auth.Current.UserId) | |
| } | |
| func MustLoadCurrentPlan() { | |
| if CurrentProjectId == "" { | |
| term.OutputErrorAndExit("No current project") | |
| } | |
| // Check if the file exists | |
| _, err := os.Stat(HomeCurrentPlanPath) | |
| if os.IsNotExist(err) { | |
| return | |
| } else if err != nil { | |
| term.OutputErrorAndExit("error checking if current-plans-v2.json exists: %v", err) | |
| } | |
| // Read the contents of the file | |
| fileBytes, err := os.ReadFile(HomeCurrentPlanPath) | |
| if err != nil { | |
| term.OutputErrorAndExit("error reading current-plans-v2.json: %v", err) | |
| } | |
| var currentPlansByAccount types.CurrentPlanSettingsByAccount | |
| err = json.Unmarshal(fileBytes, ¤tPlansByAccount) | |
| if err != nil { | |
| term.OutputErrorAndExit("error unmarshalling current-plans-v2.json: %v", err) | |
| } | |
| currentPlan := currentPlansByAccount[auth.Current.UserId] | |
| if currentPlan != nil { | |
| CurrentPlanId = currentPlan.Id | |
| } | |
| if CurrentPlanId != "" { | |
| err = loadCurrentBranch() | |
| if err != nil { | |
| term.OutputErrorAndExit("error loading current branch: %v", err) | |
| } | |
| if CurrentBranch == "" { | |
| err = WriteCurrentBranch("main") | |
| if err != nil { | |
| term.OutputErrorAndExit("error setting current branch: %v", err) | |
| } | |
| } | |
| } | |
| } | |
| func loadCurrentBranch() error { | |
| // Load plan-specific settings | |
| if CurrentPlanId == "" { | |
| return fmt.Errorf("no current plan") | |
| } | |
| path := filepath.Join(HomeCurrentProjectDir, CurrentPlanId, "settings-v2.json") | |
| // Check if the file exists | |
| _, err := os.Stat(path) | |
| if os.IsNotExist(err) { | |
| return nil | |
| } else if err != nil { | |
| return fmt.Errorf("error checking if settings-v2.json exists: %v", err) | |
| } | |
| fileBytes, err := os.ReadFile(path) | |
| if err != nil { | |
| term.OutputErrorAndExit("error reading settings-v2.json: %v", err) | |
| } | |
| var settingsByAccount types.PlanSettingsByAccount | |
| err = json.Unmarshal(fileBytes, &settingsByAccount) | |
| if err != nil { | |
| term.OutputErrorAndExit("error unmarshalling settings-v2.json: %v", err) | |
| } | |
| settings := settingsByAccount[auth.Current.UserId] | |
| if settings == nil { | |
| return nil | |
| } | |
| CurrentBranch = settings.Branch | |
| return nil | |
| } | |
| func GetCurrentPlanTable(plan *shared.Plan, currentBranchesByPlanId map[string]*shared.Branch, onlyCols []string) string { | |
| b := &strings.Builder{} | |
| table := tablewriter.NewWriter(b) | |
| table.SetAutoWrapText(false) | |
| var cols []string | |
| if onlyCols == nil { | |
| cols = []string{"Current Plan", "Updated", "Created" /*"Branches",*/, "Branch", "Context", "Convo"} | |
| } else { | |
| cols = onlyCols | |
| } | |
| table.SetHeader(cols) | |
| name := color.New(color.Bold, term.ColorHiGreen).Sprint(plan.Name) | |
| branch := currentBranchesByPlanId[CurrentPlanId] | |
| var row []string | |
| for _, col := range cols { | |
| switch col { | |
| case "Current Plan": | |
| row = append(row, name) | |
| case "Updated": | |
| row = append(row, format.Time(plan.UpdatedAt)) | |
| case "Created": | |
| row = append(row, format.Time(plan.CreatedAt)) | |
| case "Branch": | |
| row = append(row, CurrentBranch) | |
| case "Context": | |
| row = append(row, strconv.Itoa(branch.ContextTokens)+" 🪙") | |
| case "Convo": | |
| row = append(row, strconv.Itoa(branch.ConvoTokens)+" 🪙") | |
| } | |
| } | |
| style := []tablewriter.Colors{ | |
| {tablewriter.FgGreenColor, tablewriter.Bold}, | |
| } | |
| table.Rich(row, style) | |
| table.Render() | |
| return b.String() | |
| } | |
| func mustInitProject(existingSettings *types.CurrentProjectSettingsByAccount) { | |
| res, apiErr := api.Client.CreateProject(shared.CreateProjectRequest{Name: filepath.Base(fs.ProjectRoot)}) | |
| if apiErr != nil { | |
| term.OutputErrorAndExit("error creating project: %v", apiErr.Msg) | |
| } | |
| log.Println("Project created:", res.Id) | |
| CurrentProjectId = res.Id | |
| var settingsByAccount types.CurrentProjectSettingsByAccount | |
| if existingSettings != nil { | |
| settingsByAccount = *existingSettings | |
| } else { | |
| settingsByAccount = types.CurrentProjectSettingsByAccount{} | |
| } | |
| settingsByAccount[auth.Current.UserId] = &types.CurrentProjectSettings{ | |
| Id: CurrentProjectId, | |
| } | |
| // write projects-v2.json | |
| path := filepath.Join(fs.PlandexDir, "projects-v2.json") | |
| bytes, err := json.Marshal(settingsByAccount) | |
| if err != nil { | |
| term.OutputErrorAndExit("error marshalling project settings: %v", err) | |
| } | |
| err = os.WriteFile(path, bytes, os.ModePerm) | |
| if err != nil { | |
| term.OutputErrorAndExit("error writing projects-v2.json: %v", err) | |
| } | |
| log.Println("Wrote projects-v2.json") | |
| // write current-plans-v2.json to PlandexHomeDir/[projectId]/current-plans-v2.json | |
| dir := filepath.Join(fs.HomePlandexDir, CurrentProjectId) | |
| err = os.MkdirAll(dir, os.ModePerm) | |
| if err != nil { | |
| term.OutputErrorAndExit("error creating project dir: %v", err) | |
| } | |
| path = filepath.Join(dir, "current-plans-v2.json") | |
| bytes, err = json.Marshal(types.CurrentPlanSettingsByAccount{ | |
| auth.Current.UserId: &types.CurrentPlanSettings{ | |
| Id: "", | |
| }, | |
| }) | |
| if err != nil { | |
| term.OutputErrorAndExit("error marshalling plan settings: %v", err) | |
| } | |
| err = os.WriteFile(path, bytes, os.ModePerm) | |
| if err != nil { | |
| term.OutputErrorAndExit("error writing current-plans-v2.json: %v", err) | |
| } | |
| log.Println("Wrote current-plans-v2.json") | |
| } | |