Compare commits
No commits in common. "1e495551e7e949cab368bdc9e43b2fd855f91086" and "1c790976ab415720aace8804362068f5cd7c073d" have entirely different histories.
1e495551e7
...
1c790976ab
@ -14,7 +14,7 @@ import (
|
|||||||
"gmgauthier.com/grokkit/internal/git"
|
"gmgauthier.com/grokkit/internal/git"
|
||||||
"gmgauthier.com/grokkit/internal/grok"
|
"gmgauthier.com/grokkit/internal/grok"
|
||||||
"gmgauthier.com/grokkit/internal/linter"
|
"gmgauthier.com/grokkit/internal/linter"
|
||||||
"gmgauthier.com/grokkit/internal/logger"
|
"gmgauthier.com/grokkit/internal/logger" // note: we use package-level funcs
|
||||||
"gmgauthier.com/grokkit/internal/prompts"
|
"gmgauthier.com/grokkit/internal/prompts"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,15 +30,22 @@ Uses language-specific prompts discovered in .grokkit/prompts/ or ~/.config/grok
|
|||||||
}
|
}
|
||||||
output := viper.GetString("output")
|
output := viper.GetString("output")
|
||||||
if output == "" {
|
if output == "" {
|
||||||
|
// Default to project-local context file inside .grokkit/
|
||||||
output = filepath.Join(".grokkit", "analysis.md")
|
output = filepath.Join(".grokkit", "analysis.md")
|
||||||
}
|
}
|
||||||
|
// Fixed: config.GetModel takes (commandName, flagModel)
|
||||||
model := config.GetModel("analyze", viper.GetString("model"))
|
model := config.GetModel("analyze", viper.GetString("model"))
|
||||||
yes := viper.GetBool("yes")
|
yes := viper.GetBool("yes")
|
||||||
|
|
||||||
if !git.IsRepo() {
|
// Logger (use the package-level logger as done in other commands)
|
||||||
|
// (remove the old "log := logger.Get()")
|
||||||
|
|
||||||
|
// Safety check
|
||||||
|
if !git.IsRepo() { // IsRepo takes no arguments
|
||||||
logger.Warn("Not inside a git repository. Git metadata in report will be limited.")
|
logger.Warn("Not inside a git repository. Git metadata in report will be limited.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Discover source files
|
||||||
files, err := discoverSourceFiles(dir)
|
files, err := discoverSourceFiles(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to discover source files", "dir", dir, "error", err)
|
logger.Error("Failed to discover source files", "dir", dir, "error", err)
|
||||||
@ -49,11 +56,13 @@ Uses language-specific prompts discovered in .grokkit/prompts/ or ~/.config/grok
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Detect primary language
|
||||||
lang := linter.DetectPrimaryLanguage(files)
|
lang := linter.DetectPrimaryLanguage(files)
|
||||||
if lang == "" {
|
if lang == "" {
|
||||||
lang = "unknown"
|
lang = "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. Load language-specific prompt (project → global)
|
||||||
promptPath, promptContent, err := prompts.LoadAnalysisPrompt(dir, lang)
|
promptPath, promptContent, err := prompts.LoadAnalysisPrompt(dir, lang)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error: Could not find analysis prompt for language '%s'.\n\n", lang)
|
fmt.Printf("Error: Could not find analysis prompt for language '%s'.\n\n", lang)
|
||||||
@ -65,21 +74,27 @@ Uses language-specific prompts discovered in .grokkit/prompts/ or ~/.config/grok
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
logger.Info("Loaded analysis prompt", "language", lang, "path", promptPath)
|
logger.Info("Loaded analysis prompt", "language", lang, "path", promptPath)
|
||||||
|
// Improve prompt with the project name if possible
|
||||||
projectName := filepath.Base(dir)
|
projectName := filepath.Base(dir)
|
||||||
if projectName == "." {
|
if projectName == "." {
|
||||||
projectName = "Current Project"
|
projectName = "Current Project"
|
||||||
}
|
}
|
||||||
promptContent = strings.Replace(promptContent, "[Inferred Project Name]", projectName, 1)
|
promptContent = strings.Replace(promptContent, "[Inferred Project Name]", projectName, 1)
|
||||||
|
|
||||||
|
// 4. Build rich project context
|
||||||
context := buildProjectContext(dir, files)
|
context := buildProjectContext(dir, files)
|
||||||
|
|
||||||
|
// 5. Call Grok — use the exact working pattern you provided
|
||||||
messages := []map[string]string{
|
messages := []map[string]string{
|
||||||
{"role": "system", "content": promptContent},
|
{"role": "system", "content": promptContent},
|
||||||
{"role": "user", "content": fmt.Sprintf("Analyze this %s project and generate the full educational Markdown report now:\n\n%s", lang, context)},
|
{"role": "user", "content": fmt.Sprintf("Analyze this %s project and generate the full educational Markdown report now:\n\n%s", lang, context)},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fixed: NewClient() + Stream() (or StreamSilent if you prefer no live output)
|
||||||
|
// For a long report, StreamSilent is usually better (no live printing)
|
||||||
report := grok.NewClient().StreamSilent(messages, model)
|
report := grok.NewClient().StreamSilent(messages, model)
|
||||||
|
|
||||||
|
// 6. Transactional preview + confirmation
|
||||||
if !yes {
|
if !yes {
|
||||||
fmt.Println("\n=== Proposed Analysis Report Preview (first 60 lines) ===")
|
fmt.Println("\n=== Proposed Analysis Report Preview (first 60 lines) ===")
|
||||||
previewLines(report, 60)
|
previewLines(report, 60)
|
||||||
@ -95,15 +110,18 @@ Uses language-specific prompts discovered in .grokkit/prompts/ or ~/.config/grok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 7. Output
|
||||||
if output == "-" {
|
if output == "-" {
|
||||||
fmt.Println(report)
|
fmt.Println(report)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure .grokkit directory exists
|
||||||
if err := os.MkdirAll(".grokkit", 0755); err != nil {
|
if err := os.MkdirAll(".grokkit", 0755); err != nil {
|
||||||
logger.Error("Failed to create .grokkit directory", "error", err)
|
logger.Error("Failed to create .grokkit directory", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
// Write the file
|
||||||
if err := os.WriteFile(output, []byte(report), 0644); err != nil {
|
if err := os.WriteFile(output, []byte(report), 0644); err != nil {
|
||||||
logger.Error("Failed to write report", "file", output, "error", err)
|
logger.Error("Failed to write report", "file", output, "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -120,6 +138,8 @@ func init() {
|
|||||||
analyzeCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
|
analyzeCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// discoverSourceFiles walks the directory and collects all supported source files.
|
||||||
|
// It skips common noise directories but DOES descend into cmd/, internal/, etc.
|
||||||
func discoverSourceFiles(root string) ([]string, error) {
|
func discoverSourceFiles(root string) ([]string, error) {
|
||||||
var files []string
|
var files []string
|
||||||
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||||
@ -127,15 +147,17 @@ func discoverSourceFiles(root string) ([]string, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip common hidden/noise directories but still allow descent into source dirs
|
||||||
name := info.Name()
|
name := info.Name()
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
if strings.HasPrefix(name, ".") && name != "." && name != ".." ||
|
if strings.HasPrefix(name, ".") && name != "." && name != ".." || // skip .git, .grokkit (except we want .grokkit/prompts? but for source we skip)
|
||||||
name == "node_modules" || name == "vendor" || name == "build" || name == "dist" {
|
name == "node_modules" || name == "vendor" || name == "build" || name == "dist" {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this is a supported source file
|
||||||
if _, detectErr := linter.DetectLanguage(path); detectErr == nil {
|
if _, detectErr := linter.DetectLanguage(path); detectErr == nil {
|
||||||
files = append(files, path)
|
files = append(files, path)
|
||||||
}
|
}
|
||||||
@ -144,6 +166,7 @@ func discoverSourceFiles(root string) ([]string, error) {
|
|||||||
return files, err
|
return files, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// previewLines prints the first N lines of the report (unchanged)
|
||||||
func previewLines(content string, n int) {
|
func previewLines(content string, n int) {
|
||||||
scanner := bufio.NewScanner(strings.NewReader(content))
|
scanner := bufio.NewScanner(strings.NewReader(content))
|
||||||
for i := 0; i < n && scanner.Scan(); i++ {
|
for i := 0; i < n && scanner.Scan(); i++ {
|
||||||
@ -154,13 +177,11 @@ func previewLines(content string, n int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildProjectContext — small improvement for better context (optional but nice)
|
||||||
func buildProjectContext(dir string, files []string) string {
|
func buildProjectContext(dir string, files []string) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString("Project Root: " + dir + "\n\n")
|
sb.WriteString("Project Root: " + dir + "\n\n")
|
||||||
_, err := fmt.Fprintf(&sb, "Total source files discovered: %d\n\n", len(files))
|
sb.WriteString(fmt.Sprintf("Total source files discovered: %d\n\n", len(files)))
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteString("Key files (top-level view):\n")
|
sb.WriteString("Key files (top-level view):\n")
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
@ -173,6 +194,7 @@ func buildProjectContext(dir string, files []string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Git remotes
|
||||||
if out, err := git.Run([]string{"remote", "-v"}); err == nil && out != "" {
|
if out, err := git.Run([]string{"remote", "-v"}); err == nil && out != "" {
|
||||||
sb.WriteString("\nGit Remotes:\n" + out + "\n")
|
sb.WriteString("\nGit Remotes:\n" + out + "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
31
cmd/edit.go
31
cmd/edit.go
@ -35,6 +35,7 @@ var editCmd = &cobra.Command{
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // intentional file read from user input
|
||||||
original, err := os.ReadFile(filePath)
|
original, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to read file", "file", filePath, "error", err)
|
logger.Error("failed to read file", "file", filePath, "error", err)
|
||||||
@ -45,28 +46,14 @@ var editCmd = &cobra.Command{
|
|||||||
cleanedOriginal := removeLastModifiedComments(string(original))
|
cleanedOriginal := removeLastModifiedComments(string(original))
|
||||||
|
|
||||||
client := grok.NewClient()
|
client := grok.NewClient()
|
||||||
isMarkdown := filepath.Ext(filePath) == ".md"
|
messages := []map[string]string{
|
||||||
var messages []map[string]string
|
{"role": "system", "content": "You are an expert programmer. Remove all unnecessary comments including last modified timestamps and ownership comments. Return only the cleaned code with no explanations, no markdown, no extra text."},
|
||||||
if isMarkdown {
|
{"role": "user", "content": fmt.Sprintf("File: %s\n\nOriginal content:\n%s\n\nTask: %s", filepath.Base(filePath), cleanedOriginal, instruction)},
|
||||||
messages = []map[string]string{
|
|
||||||
{"role": "system", "content": "You are an expert technical writer. Edit markdown content clearly and concisely. Return only the edited markdown with no explanations, no markdown formatting, no extra text."},
|
|
||||||
{"role": "user", "content": fmt.Sprintf("File: %s\n\nOriginal content:\n%s\n\nTask: %s", filepath.Base(filePath), cleanedOriginal, instruction)},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
messages = []map[string]string{
|
|
||||||
{"role": "system", "content": "You are an expert programmer. Remove all unnecessary comments including last modified timestamps and ownership comments. Return only the cleaned code with no explanations, no markdown, no extra text."},
|
|
||||||
{"role": "user", "content": fmt.Sprintf("File: %s\n\nOriginal content:\n%s\n\nTask: %s", filepath.Base(filePath), cleanedOriginal, instruction)},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
color.Yellow("Asking Grok to %s...\n", instruction)
|
color.Yellow("Asking Grok to %s...\n", instruction)
|
||||||
raw := client.StreamSilent(messages, model)
|
raw := client.StreamSilent(messages, model)
|
||||||
var newContent string
|
newContent := grok.CleanCodeResponse(raw)
|
||||||
if isMarkdown {
|
|
||||||
newContent = strings.TrimSpace(raw)
|
|
||||||
} else {
|
|
||||||
newContent = grok.CleanCodeResponse(raw)
|
|
||||||
}
|
|
||||||
color.Green("✓ Response received")
|
color.Green("✓ Response received")
|
||||||
|
|
||||||
color.Cyan("\nProposed changes:")
|
color.Cyan("\nProposed changes:")
|
||||||
@ -103,11 +90,13 @@ var editCmd = &cobra.Command{
|
|||||||
func removeLastModifiedComments(content string) string {
|
func removeLastModifiedComments(content string) string {
|
||||||
lines := strings.Split(content, "\n")
|
lines := strings.Split(content, "\n")
|
||||||
cleanedLines := make([]string, 0, len(lines))
|
cleanedLines := make([]string, 0, len(lines))
|
||||||
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
trimmed := strings.TrimSpace(line)
|
if strings.Contains(line, "Last modified") {
|
||||||
if !strings.Contains(strings.ToLower(trimmed), "last modified") {
|
continue
|
||||||
cleanedLines = append(cleanedLines, line)
|
|
||||||
}
|
}
|
||||||
|
cleanedLines = append(cleanedLines, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(cleanedLines, "\n")
|
return strings.Join(cleanedLines, "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user