describe

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 1, 2025 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

cmd/project/describe/describe.go

Index

Constants

This section is empty.

Variables

View Source
var DescribeCmd = &cobra.Command{
	Use:     "describe [-o <output_file>]",
	Example: `  contextvibes project describe -o project_snapshot.md`,
	Args:    cobra.NoArgs,
	RunE: func(cmd *cobra.Command, args []string) error {
		presenter := ui.NewPresenter(cmd.OutOrStdout(), cmd.ErrOrStderr())
		ctx := cmd.Context()

		presenter.Summary("Generating project context description.")

		workDir, err := os.Getwd()
		if err != nil {
			return err
		}

		gitCfg := git.GitClientConfig{
			Logger:                globals.AppLogger,
			DefaultRemoteName:     globals.LoadedAppConfig.Git.DefaultRemote,
			DefaultMainBranchName: globals.LoadedAppConfig.Git.DefaultMainBranch,
			Executor:              globals.ExecClient.UnderlyingExecutor(),
		}
		client, err := git.NewClient(ctx, workDir, gitCfg)
		if err != nil {
			presenter.Error("Failed git init: %v", err)

			return err
		}
		cwd := client.Path()

		includeRes := make(
			[]*regexp.Regexp,
			0,
			len(globals.LoadedAppConfig.Describe.IncludePatterns),
		)
		for _, p := range globals.LoadedAppConfig.Describe.IncludePatterns {
			re, err := regexp.Compile(p)
			if err != nil {
				return fmt.Errorf("invalid include pattern in config '%s': %w", p, err)
			}
			includeRes = append(includeRes, re)
		}

		excludeRes := make(
			[]*regexp.Regexp,
			0,
			len(globals.LoadedAppConfig.Describe.ExcludePatterns),
		)
		for _, p := range globals.LoadedAppConfig.Describe.ExcludePatterns {
			re, err := regexp.Compile(p)
			if err != nil {
				return fmt.Errorf("invalid exclude pattern in config '%s': %w", p, err)
			}
			excludeRes = append(excludeRes, re)
		}

		maxSizeBytes := int64(maxFileSizeKB * 1024)

		var aiExcluder gitignore.GitIgnore
		aiExcludeFilePath := filepath.Join(cwd, ".aiexclude")
		aiExcludeContent, readErr := os.ReadFile(aiExcludeFilePath)
		if readErr == nil {
			aiExcluder = gitignore.New(bytes.NewReader(aiExcludeContent), cwd, nil)
		}

		var outputBuffer bytes.Buffer
		var userPrompt string
		if describePromptFlag != "" {
			userPrompt = describePromptFlag
		} else {
			var promptErr error
			userPrompt, promptErr = presenter.PromptForInput("Enter a prompt for the AI: ")
			if promptErr != nil {
				return promptErr
			}
		}

		if userPrompt == "" {
			return errors.New("prompt cannot be empty")
		}

		fmt.Fprintf(&outputBuffer, "### Prompt\n\n%s\n\n", userPrompt)

		gitStatus, _, statusErr := client.GetStatusShort(ctx)
		if statusErr != nil {
			gitStatus = "Failed to get git status."
		}
		tools.AppendSectionHeader(&outputBuffer, "Git Status (Summary)")
		tools.AppendFencedCodeBlock(&outputBuffer, strings.TrimSpace(gitStatus), "")

		treeOutput, _, treeErr := globals.ExecClient.CaptureOutput(
			ctx,
			workDir,
			"tree",
			"-L",
			"2",
			"-a",
			"-I",
			treeIgnorePattern,
		)
		if treeErr != nil {
			treeOutput = "Could not generate tree view. 'tree' command may not be installed."
		}
		tools.AppendSectionHeader(&outputBuffer, "Project Structure")
		tools.AppendFencedCodeBlock(&outputBuffer, strings.TrimSpace(treeOutput), "")

		tools.AppendSectionHeader(&outputBuffer, "Relevant Code Files")

		gitLsFilesOutput, _, err := client.ListTrackedAndCachedFiles(ctx)
		if err != nil {
			return err
		}
		filesToList := strings.SplitSeq(strings.TrimSpace(gitLsFilesOutput), "\n")

		for file := range filesToList {
			if file == "" {
				continue
			}

			isIncluded := false
			for _, re := range includeRes {
				if re.MatchString(file) {
					isIncluded = true

					break
				}
			}
			if !isIncluded {
				continue
			}

			isExcluded := false
			for _, re := range excludeRes {
				if re.MatchString(file) {
					isExcluded = true

					break
				}
			}
			if isExcluded {
				continue
			}

			if aiExcluder != nil && aiExcluder.Match(file) != nil &&
				aiExcluder.Match(file).Ignore() {
				continue
			}

			info, statErr := os.Stat(file)
			if statErr != nil {
				continue
			}
			if info.Size() > maxSizeBytes {
				continue
			}

			content, readErr := tools.ReadFileContent(file)
			if readErr == nil {
				tools.AppendFileMarkerHeader(&outputBuffer, file)
				outputBuffer.Write(content)
				tools.AppendFileMarkerFooter(&outputBuffer, file)
			}
		}

		if err := tools.WriteBufferToFile(describeOutputFile, &outputBuffer); err != nil {
			return err
		}

		presenter.Success("Successfully generated context file: %s", describeOutputFile)

		return nil
	},
}

DescribeCmd represents the describe command.

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL