@tanagram/cli 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -123,7 +123,7 @@ If you have existing hooks, you can merge this hook into your existing config.
123
123
 
124
124
  ## How It Works
125
125
 
126
- 1. **Finds instruction files** - Searches for `AGENTS.md`, `POLICIES.md`, `CLAUDE.md`, and `.cursor/rules/*.mdc` in your git repository
126
+ 1. **Finds instruction files** - Searches for `AGENTS.md`, `POLICIES.md`, `CLAUDE.md`, `BUGBOT.md`, and `.cursor/rules/*.mdc` in your git repository
127
127
  2. **Checks cache** - Loads cached policies and MD5 hashes from `.tanagram/`
128
128
  3. **Auto-syncs** - Detects file changes via MD5 and automatically resyncs if needed
129
129
  4. **LLM extraction** - Uses Claude AI to extract ALL policies from instruction files
package/bin/tanagram CHANGED
Binary file
package/commands/run.go CHANGED
@@ -47,7 +47,7 @@ func Run() error {
47
47
  }
48
48
 
49
49
  if len(instructionFiles) == 0 {
50
- return fmt.Errorf("no instruction files found (looking for AGENTS.md, POLICIES.md, CLAUDE.md, .cursor/rules/*.mdc)")
50
+ return fmt.Errorf("no instruction files found (looking for AGENTS.md, POLICIES.md, CLAUDE.md, BUGBOT.md, .cursor/rules/*.mdc)")
51
51
  }
52
52
 
53
53
  // Load cache
@@ -56,22 +56,21 @@ func Run() error {
56
56
  return fmt.Errorf("failed to load cache: %w", err)
57
57
  }
58
58
 
59
- // Check each file for changes and auto-sync if needed
60
- needsSync := false
59
+ // Check each file for changes and collect which files need syncing
60
+ var filesToSync []string
61
61
  for _, file := range instructionFiles {
62
62
  changed, err := cache.HasChanged(file)
63
63
  if err != nil {
64
64
  return fmt.Errorf("failed to check if %s changed: %w", file, err)
65
65
  }
66
66
  if changed {
67
- needsSync = true
68
- break
67
+ filesToSync = append(filesToSync, file)
69
68
  }
70
69
  }
71
70
 
72
- // Auto-sync if any files changed
73
- if needsSync {
74
- fmt.Printf("\nSyncing policies with LLM (processing %d files in parallel)...\n", len(instructionFiles))
71
+ // Auto-sync only the files that changed
72
+ if len(filesToSync) > 0 {
73
+ fmt.Printf("\nSyncing policies with LLM (processing %d changed file(s) in parallel)...\n", len(filesToSync))
75
74
 
76
75
  ctx := context.Background()
77
76
 
@@ -84,7 +83,7 @@ func Run() error {
84
83
  }
85
84
 
86
85
  // Channel to collect results
87
- results := make(chan syncResult, len(instructionFiles))
86
+ results := make(chan syncResult, len(filesToSync))
88
87
  var wg sync.WaitGroup
89
88
 
90
89
  // Start spinner
@@ -103,15 +102,15 @@ func Run() error {
103
102
  mu.Lock()
104
103
  c := completed
105
104
  mu.Unlock()
106
- fmt.Printf("\r%s Processing files... (%d/%d completed)", chars[i%len(chars)], c, len(instructionFiles))
105
+ fmt.Printf("\r%s Processing files... (%d/%d completed)", chars[i%len(chars)], c, len(filesToSync))
107
106
  i++
108
107
  time.Sleep(100 * time.Millisecond)
109
108
  }
110
109
  }
111
110
  }()
112
111
 
113
- // Launch goroutines for each file
114
- for _, file := range instructionFiles {
112
+ // Launch goroutines only for changed files
113
+ for _, file := range filesToSync {
115
114
  wg.Add(1)
116
115
  go func(file string) {
117
116
  defer wg.Done()
@@ -162,7 +161,7 @@ func Run() error {
162
161
  return fmt.Errorf("failed to save cache: %w", err)
163
162
  }
164
163
 
165
- fmt.Printf("\n✓ Synced %d policies from %d file(s)\n", totalPolicies, len(instructionFiles))
164
+ fmt.Printf("\n✓ Synced %d policies from %d changed file(s)\n", totalPolicies, len(filesToSync))
166
165
  }
167
166
 
168
167
  // Load all policies from cache
package/commands/sync.go CHANGED
@@ -28,7 +28,7 @@ func Sync() error {
28
28
  }
29
29
 
30
30
  if len(instructionFiles) == 0 {
31
- return fmt.Errorf("no instruction files found (looking for AGENTS.md, POLICIES.md, CLAUDE.md, .cursor/rules/*.mdc)")
31
+ return fmt.Errorf("no instruction files found (looking for AGENTS.md, POLICIES.md, CLAUDE.md, BUGBOT.md, .cursor/rules/*.mdc)")
32
32
  }
33
33
 
34
34
  fmt.Printf("Found %d instruction file(s)\n", len(instructionFiles))
@@ -39,8 +39,26 @@ func Sync() error {
39
39
  return fmt.Errorf("failed to load cache: %w", err)
40
40
  }
41
41
 
42
- // Parse and sync each file using LLM in parallel
43
- fmt.Printf("\nSyncing policies with LLM (processing %d files in parallel)...\n", len(instructionFiles))
42
+ // Check which files have changed and need syncing
43
+ var filesToSync []string
44
+ for _, file := range instructionFiles {
45
+ changed, err := cache.HasChanged(file)
46
+ if err != nil {
47
+ return fmt.Errorf("failed to check if %s changed: %w", file, err)
48
+ }
49
+ if changed {
50
+ filesToSync = append(filesToSync, file)
51
+ }
52
+ }
53
+
54
+ // If no files changed, nothing to sync
55
+ if len(filesToSync) == 0 {
56
+ fmt.Println("✓ All instruction files are up to date")
57
+ return nil
58
+ }
59
+
60
+ // Parse and sync only changed files using LLM in parallel
61
+ fmt.Printf("\nSyncing policies with LLM (processing %d changed file(s) in parallel)...\n", len(filesToSync))
44
62
 
45
63
  ctx := context.Background()
46
64
 
@@ -53,7 +71,7 @@ func Sync() error {
53
71
  }
54
72
 
55
73
  // Channel to collect results
56
- results := make(chan syncResult, len(instructionFiles))
74
+ results := make(chan syncResult, len(filesToSync))
57
75
  var wg sync.WaitGroup
58
76
 
59
77
  // Start spinner
@@ -72,15 +90,15 @@ func Sync() error {
72
90
  mu.Lock()
73
91
  c := completed
74
92
  mu.Unlock()
75
- fmt.Printf("\r%s Processing files... (%d/%d completed)", chars[i%len(chars)], c, len(instructionFiles))
93
+ fmt.Printf("\r%s Processing files... (%d/%d completed)", chars[i%len(chars)], c, len(filesToSync))
76
94
  i++
77
95
  time.Sleep(100 * time.Millisecond)
78
96
  }
79
97
  }
80
98
  }()
81
99
 
82
- // Launch goroutines for each file
83
- for _, file := range instructionFiles {
100
+ // Launch goroutines only for changed files
101
+ for _, file := range filesToSync {
84
102
  wg.Add(1)
85
103
  go func(file string) {
86
104
  defer wg.Done()
@@ -132,17 +150,17 @@ func Sync() error {
132
150
  return fmt.Errorf("failed to save cache: %w", err)
133
151
  }
134
152
 
135
- fmt.Printf("\n✓ Synced %d policies from %d file(s)\n", totalPolicies, len(instructionFiles))
153
+ fmt.Printf("\n✓ Synced %d policies from %d changed file(s)\n", totalPolicies, len(filesToSync))
136
154
  return nil
137
155
  }
138
156
 
139
157
  // FindInstructionFiles searches for instruction files in the git repository
140
- // Looks for: AGENTS.md, POLICIES.md, CLAUDE.md, and .cursor/rules/*.mdc
158
+ // Looks for: AGENTS.md, POLICIES.md, CLAUDE.md, BUGBOT.md, and .cursor/rules/*.mdc
141
159
  func FindInstructionFiles(gitRoot string) ([]string, error) {
142
160
  var files []string
143
161
 
144
162
  // Common instruction file names to look for
145
- commonNames := []string{"AGENTS.md", "POLICIES.md", "CLAUDE.md"}
163
+ commonNames := []string{"AGENTS.md", "POLICIES.md", "CLAUDE.md", "BUGBOT.md"}
146
164
 
147
165
  // Directories to skip
148
166
  skipDirs := map[string]bool{
package/main.go CHANGED
@@ -57,7 +57,7 @@ EXAMPLES:
57
57
 
58
58
  INSTRUCTION FILES:
59
59
  Tanagram looks for instruction files in your git repository:
60
- - AGENTS.md, POLICIES.md, CLAUDE.md
60
+ - AGENTS.md, POLICIES.md, CLAUDE.md, BUGBOT.md
61
61
  - Cursor rules: .cursor/rules/*.mdc
62
62
 
63
63
  Policies are cached and automatically resynced when files change.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanagram/cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Tanagram - Catch sloppy code before it ships",
5
5
  "main": "index.js",
6
6
  "bin": {