triage-trip 0.1.0 → 0.1.4
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 +82 -111
- package/bin/cli.js +23 -11
- package/package.json +4 -2
- package/src/git.js +130 -3
- package/src/patch.js +662 -73
- package/src/triage.js +99 -16
- package/src/utils.js +63 -0
package/README.md
CHANGED
|
@@ -1,146 +1,117 @@
|
|
|
1
|
-
|
|
1
|
+
Markdown
|
|
2
|
+
# triage-trip
|
|
2
3
|
|
|
3
|
-
> Autonomous
|
|
4
|
+
> Autonomous CLI to pinpoint and patch regressions introduced by AI code assistants (Cursor, Copilot).
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
<img src="./assets/demo.gif" alt="AI-Blackbox Demo" width="700">
|
|
7
|
-
</p>
|
|
8
|
-
---
|
|
6
|
+

|
|
9
7
|
|
|
10
8
|
## The Problem
|
|
9
|
+
When you use AI coding assistants to refactor or draft features, errors often slip through:
|
|
10
|
+
- Subtle schema property renames (`userId` ➔ `user_id`).
|
|
11
|
+
- Dropped function arguments or breaking contract shifts.
|
|
12
|
+
- Regressions that aren't caught until 3 commits later.
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* An AI renames an object key or subtle schema attribute (e.g., `userId` ➔ `user_id`).
|
|
15
|
-
* Standard linters and compilers pass because the syntax is completely valid.
|
|
16
|
-
* Downstream modules crash 2–3 commits later with cryptic runtime errors (`TypeError: Cannot read properties of undefined`).
|
|
17
|
-
* Developers spend 20–30 minutes manually inspecting multi-file diffs, running `git bisect`, and searching for what changed.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## What `ai-blackbox` Does
|
|
22
|
-
|
|
23
|
-
`ai-blackbox` connects the dots across your development environment to trace the exact lineage of a regression:
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
[ User Prompt in IDE ] ➔ [ AI Generated Diff ] ➔ [ Git Commit ] ➔ [ Runtime Error ]
|
|
27
|
-
│
|
|
28
|
-
ai-blackbox triage
|
|
29
|
-
│
|
|
30
|
-
▼
|
|
31
|
-
• Exact Root Cause & Commit
|
|
32
|
-
• Attributed AI Prompt
|
|
33
|
-
• Semantic Contract Shift
|
|
34
|
-
• Clean Unified Diff Patch
|
|
14
|
+
Developers lose 30+ minutes digging through hundreds of lines of AI diffs trying to figure out which prompt broke the build.
|
|
35
15
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
1. **
|
|
39
|
-
2. **
|
|
40
|
-
3. **
|
|
16
|
+
## How It Works
|
|
17
|
+
`triage-trip` connects runtime failures back to their origin:
|
|
18
|
+
1. **Reads Local Git Stream:** Analyzes unstaged changes and recent commit diffs.
|
|
19
|
+
2. **Inspects Cursor Provenance:** Reads local IDE session SQLite databases (`state.vscdb`) to identify the exact human prompt that generated the regression.
|
|
20
|
+
3. **Isolates Semantic Shifts:** Determines contract and schema breaks.
|
|
21
|
+
4. **Interactive In-Memory Patching:** Applies clean unified diffs directly to your working tree via `git apply` with a single confirmation.
|
|
41
22
|
|
|
42
23
|
---
|
|
43
24
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
### Prerequisites
|
|
47
|
-
|
|
48
|
-
* **Node.js** (v18 or higher, ESM native)
|
|
49
|
-
* An active **OpenAI API Key**
|
|
50
|
-
|
|
51
|
-
### Local Setup
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
# Clone the repository
|
|
55
|
-
git clone https://github.com/your-username/ai-blackbox.git
|
|
56
|
-
cd ai-blackbox
|
|
57
|
-
|
|
58
|
-
# Install dependencies
|
|
59
|
-
npm install
|
|
60
|
-
|
|
61
|
-
# Link executable globally (optional for direct CLI access)
|
|
62
|
-
npm link
|
|
63
|
-
|
|
64
|
-
```
|
|
25
|
+
## Quick Start
|
|
65
26
|
|
|
66
|
-
|
|
27
|
+
Run instantly without global installation:
|
|
67
28
|
|
|
68
29
|
```bash
|
|
69
|
-
|
|
30
|
+
npx triage-trip triage "TypeError: Cannot read properties of undefined (reading 'toUpperCase')"
|
|
31
|
+
Or install globally:
|
|
32
|
+
|
|
33
|
+
Bash
|
|
34
|
+
npm install -g triage-trip
|
|
35
|
+
Environment Setup
|
|
36
|
+
Set your preferred LLM API key:
|
|
37
|
+
|
|
38
|
+
Bash
|
|
39
|
+
export ANTHROPIC_API_KEY="your-anthropic-key"
|
|
40
|
+
# or
|
|
41
|
+
export OPENAI_API_KEY="your-openai-key"
|
|
42
|
+
Usage
|
|
43
|
+
Diagnose an Error Trace
|
|
44
|
+
Bash
|
|
45
|
+
triage-trip triage "Cannot read properties of undefined (reading 'toUpperCase')"
|
|
46
|
+
Auto-Apply Patches Instantly
|
|
47
|
+
Skip the confirmation prompt to patch immediately:
|
|
48
|
+
|
|
49
|
+
Bash
|
|
50
|
+
triage-trip triage "ReferenceError: getUserProfile is not defined" --yes
|
|
51
|
+
Inspect More Commits
|
|
52
|
+
Bash
|
|
53
|
+
triage-trip triage "SyntaxError: Unexpected token" --commits 10
|
|
54
|
+
License
|
|
55
|
+
MIT
|
|
70
56
|
|
|
71
|
-
```
|
|
72
57
|
|
|
73
58
|
---
|
|
74
59
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
Run the triage engine directly against any runtime error message or stack trace:
|
|
60
|
+
### Step 3: Commit and Publish
|
|
78
61
|
|
|
79
62
|
```bash
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# Or globally (if linked)
|
|
84
|
-
ai-blackbox triage "TypeError: Cannot read properties of undefined (reading 'toUpperCase')"
|
|
85
|
-
|
|
86
|
-
```
|
|
63
|
+
# Stage the updated doc and new GIF
|
|
64
|
+
git add README.md assets/demo.gif package.json bin/cli.js
|
|
87
65
|
|
|
88
|
-
|
|
66
|
+
# Commit
|
|
67
|
+
git commit -m "docs: rebrand to triage-trip with new demo gif"
|
|
89
68
|
|
|
90
|
-
|
|
91
|
-
|
|
69
|
+
# Push to your remote
|
|
70
|
+
git push origin main
|
|
92
71
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
## Example Output
|
|
72
|
+
# Publish live on npm
|
|
73
|
+
npm publish --access public
|
|
96
74
|
|
|
97
|
-
```markdown
|
|
98
|
-
■ AI-Blackbox Triage Engine
|
|
99
75
|
|
|
100
|
-
|
|
76
|
+
## Inspiration & Ecosystem
|
|
101
77
|
|
|
102
|
-
|
|
103
|
-
The breaking change was introduced in commit `b83c577` ("refactor: update user schema"),
|
|
104
|
-
which modified the `getUserProfile` function in `user.js`.
|
|
78
|
+
Large engineering organizations (like Uber with their internal autonomous debugging agents) have long relied on automated bisect and triage systems to catch regressions before they reach staging.
|
|
105
79
|
|
|
106
|
-
|
|
80
|
+
However, existing enterprise tooling wasn't built for the AI code assistant era. When an LLM refactors multi-file repositories in Cursor or Copilot, conventional stack traces don't tell you which human prompt caused the contract shift.
|
|
107
81
|
|
|
108
|
-
|
|
109
|
-
causing `profile.userId` to evaluate to `undefined`.
|
|
82
|
+
`triage-trip` bridges that gap for every developer: a zero-overhead local CLI that brings automated root-cause isolation, Cursor prompt provenance, and in-memory patching right to your terminal.
|
|
110
83
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@@ -6,2 +6,2 @@
|
|
115
|
-
- console.log(`Loading dashboard for user ID: ${profile.userId.toUpperCase()}`);
|
|
116
|
-
+ console.log(`Loading dashboard for user ID: ${profile.user_id.toUpperCase()}`);
|
|
84
|
+
## Security & Privacy
|
|
85
|
+
- **Read-Only SQLite Access:** `triage-trip` reads local IDE databases (`state.vscdb`) in strict read-only mode (`readonly: true`). It never modifies editor settings or session files.
|
|
86
|
+
- **Direct LLM Communication:** Your repository context and diffs are sent directly to Anthropic or OpenAI using your own local environment API keys. No intermediate servers, telemetry trackers, or external logging daemons.
|
|
117
87
|
|
|
118
|
-
|
|
88
|
+
## Compatibility
|
|
89
|
+
- **IDEs:** Cursor (Full prompt provenance support via local Composer storage), VS Code / Windsurf (Git diff & contract triage)
|
|
90
|
+
- **Platforms:** macOS, Linux, Windows (WSL / PowerShell)
|
|
91
|
+
- **Runtime:** Node.js `>= 18.0.0`
|
|
119
92
|
|
|
120
|
-
|
|
93
|
+
## CLI Options
|
|
121
94
|
|
|
122
|
-
|
|
95
|
+
| Flag | Default | Description |
|
|
96
|
+
| :--- | :--- | :--- |
|
|
97
|
+
| `-c, --commits <num>` | `5` | Number of recent commits to inspect alongside unstaged working diffs |
|
|
98
|
+
| `-y, --yes` | `false` | Automatically apply the proposed patch without an interactive prompt |
|
|
99
|
+
| `-V, --version` | | Display current version |
|
|
100
|
+
| `-h, --help` | | Display help and usage options |
|
|
123
101
|
|
|
124
|
-
|
|
125
|
-
ai-blackbox/
|
|
126
|
-
├── bin/
|
|
127
|
-
│ └── cli.js # CLI commands & terminal output formatting (Commander, Ora, Picocolors)
|
|
128
|
-
├── src/
|
|
129
|
-
│ ├── git.js # Working tree and commit diff extraction (simple-git)
|
|
130
|
-
│ ├── cursor.js # Cursor SQLite database parser (better-sqlite3)
|
|
131
|
-
│ └── triage.js # OpenAI triage diagnostic engine (GPT-4o)
|
|
132
|
-
├── package.json
|
|
133
|
-
└── README.md
|
|
102
|
+
## Development
|
|
134
103
|
|
|
135
|
-
```
|
|
104
|
+
```bash
|
|
105
|
+
git clone [https://github.com/arules3/triage-trip.git](https://github.com/arules3/triage-trip.git)
|
|
106
|
+
cd triage-trip
|
|
107
|
+
npm install
|
|
108
|
+
npm link
|
|
136
109
|
|
|
137
|
-
---
|
|
138
110
|
|
|
139
|
-
## Roadmap
|
|
111
|
+
## Current Status & Roadmap
|
|
140
112
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
* [ ] GitHub Action runner for automated PR failure triage
|
|
113
|
+
### What's Working Today (`v0.1.0`)
|
|
114
|
+
- **Git Context Analysis:** Reads unstaged working tree diffs and recent commit logs.
|
|
115
|
+
- **Cursor Prompt Provenance:** Direct read-only extraction from local `state.vscdb` to link regressions to specific Composer prompts.
|
|
116
|
+
- **Root Cause Isolation:** LLM-assisted semantic contract shift detection.
|
|
117
|
+
- **Interactive Patching:** Clean, in-memory `git apply` with user confirmation prompts (`--yes` fallback).
|
package/bin/cli.js
CHANGED
|
@@ -24,25 +24,32 @@ program
|
|
|
24
24
|
.option('-c, --commits <number>', 'Number of recent commits to analyze', '5')
|
|
25
25
|
.option('-y, --yes', 'Automatically apply the patch without asking', false)
|
|
26
26
|
.action(async (errorInput, options) => {
|
|
27
|
-
console.log(pc.bold(pc.cyan('\n■
|
|
27
|
+
console.log(pc.bold(pc.cyan('\n■ Triage-Trip Engine\n')));
|
|
28
28
|
|
|
29
29
|
const spinner = ora('Reading recent git changes & Cursor prompts...').start();
|
|
30
30
|
let diagnosis = '';
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
try {
|
|
33
33
|
const commitCount = parseInt(options.commits, 10);
|
|
34
34
|
const [changes, prompts] = await Promise.all([
|
|
35
|
-
getRecentChanges(commitCount),
|
|
35
|
+
getRecentChanges(commitCount, errorInput),
|
|
36
36
|
Promise.resolve().then(() => getRecentCursorPrompts(10))
|
|
37
37
|
]);
|
|
38
38
|
|
|
39
39
|
spinner.text = 'Isolating structural contract shifts and AST regressions...';
|
|
40
|
-
diagnosis = await triageRegression(errorInput, changes, prompts)
|
|
40
|
+
diagnosis = await triageRegression(errorInput, changes, prompts, () => {
|
|
41
|
+
// Stop spinner before the first streamed token so ora doesn't garble stdout
|
|
42
|
+
spinner.stop();
|
|
43
|
+
process.stdout.write('\n');
|
|
44
|
+
});
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
console.log('\n' + diagnosis + '\n');
|
|
46
|
+
console.log('\n' + pc.green('✓ Root cause isolated.') + '\n');
|
|
44
47
|
} catch (err) {
|
|
45
|
-
spinner.
|
|
48
|
+
if (spinner.isSpinning) {
|
|
49
|
+
spinner.fail(pc.red('Triage failed: ' + err.message));
|
|
50
|
+
} else {
|
|
51
|
+
console.error(pc.red('\nTriage failed: ' + err.message));
|
|
52
|
+
}
|
|
46
53
|
process.exit(1);
|
|
47
54
|
}
|
|
48
55
|
|
|
@@ -60,16 +67,21 @@ program
|
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
if (shouldApply) {
|
|
63
|
-
const patchSpinner = ora('Applying patch
|
|
70
|
+
const patchSpinner = ora('Applying patch to working tree...').start();
|
|
64
71
|
try {
|
|
65
|
-
await applyDiff(patch);
|
|
72
|
+
const res = await applyDiff(patch);
|
|
66
73
|
patchSpinner.succeed(pc.green('Patch applied successfully to working tree.'));
|
|
74
|
+
if (res?.modifiedFiles && res.modifiedFiles.length > 0) {
|
|
75
|
+
console.log(pc.dim(`Modified: ${res.modifiedFiles.join(', ')}`));
|
|
76
|
+
}
|
|
67
77
|
console.log(pc.dim('Run `git diff` to review or `git commit` to save the fix.\n'));
|
|
68
78
|
} catch (err) {
|
|
69
|
-
patchSpinner.fail(pc.red(err.message));
|
|
79
|
+
patchSpinner.fail(pc.red('Patch application failed: ' + err.message));
|
|
70
80
|
}
|
|
71
81
|
}
|
|
82
|
+
} else {
|
|
83
|
+
console.log(pc.dim('ℹ No executable patch block was detected in the diagnostic report.\n'));
|
|
72
84
|
}
|
|
73
85
|
});
|
|
74
86
|
|
|
75
|
-
program.parse(process.argv);
|
|
87
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "triage-trip",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Autonomous CLI to pinpoint and patch regressions introduced by AI code assistants",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "node test/patch.test.js && node test/simulation.test.js && node test/streaming.test.js",
|
|
8
|
+
"test:sim": "node test/simulation.test.js",
|
|
9
|
+
"test:stream": "node test/streaming.test.js"
|
|
8
10
|
},
|
|
9
11
|
"keywords": [
|
|
10
12
|
"cursor",
|
package/src/git.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { simpleGit } from 'simple-git';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
2
4
|
|
|
3
5
|
const git = simpleGit();
|
|
4
6
|
|
|
@@ -13,7 +15,126 @@ function sanitizeDiff(diffStr, maxChars = 4000) {
|
|
|
13
15
|
return diffStr;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
const BINARY_EXTENSIONS = new Set([
|
|
19
|
+
'.gif', '.png', '.jpg', '.jpeg', '.cast', '.ico', '.pdf', '.zip',
|
|
20
|
+
'.tar', '.gz', '.db', '.sqlite', '.sqlite3', '.bin', '.wasm'
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Collects active working copy text for relevant files with line numbers.
|
|
25
|
+
* Gives the LLM exact source of truth for current file state, eliminating hallucinated diffs.
|
|
26
|
+
*/
|
|
27
|
+
function collectActiveFiles(status, commitDiffs, errorInput = '', maxFiles = 5) {
|
|
28
|
+
const candidates = new Set();
|
|
29
|
+
|
|
30
|
+
// 1. Extract file paths from error input / stack trace
|
|
31
|
+
if (errorInput) {
|
|
32
|
+
const fileMatches = errorInput.match(/(?:[a-zA-Z0-9_\-\.\/]+\/)?([a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9]+)/g) || [];
|
|
33
|
+
for (const match of fileMatches) {
|
|
34
|
+
const cleanPath = match.replace(/^(?:file:\/\/\/|\/)/, '');
|
|
35
|
+
candidates.add(cleanPath);
|
|
36
|
+
candidates.add(path.basename(cleanPath));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. Working tree files from git status
|
|
41
|
+
const statusFiles = [
|
|
42
|
+
...(status.modified || []),
|
|
43
|
+
...(status.staged || []),
|
|
44
|
+
...(status.created || []),
|
|
45
|
+
...(status.not_added || []),
|
|
46
|
+
];
|
|
47
|
+
for (const f of statusFiles) candidates.add(f);
|
|
48
|
+
|
|
49
|
+
// 3. Files from latest commit diff
|
|
50
|
+
if (commitDiffs.length > 0 && commitDiffs[0].diff) {
|
|
51
|
+
const diffFiles = commitDiffs[0].diff.match(/(?:---|\+\+\+)\s+[ab]\/([^\s\n]+)/g) || [];
|
|
52
|
+
for (const df of diffFiles) {
|
|
53
|
+
const p = df.replace(/^(?:---|\+\+\+)\s+[ab]\//, '');
|
|
54
|
+
candidates.add(p);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const activeFiles = {};
|
|
59
|
+
let count = 0;
|
|
60
|
+
|
|
61
|
+
for (const candidate of candidates) {
|
|
62
|
+
if (count >= maxFiles) break;
|
|
63
|
+
|
|
64
|
+
// Resolve file
|
|
65
|
+
let fullPath = path.resolve(process.cwd(), candidate);
|
|
66
|
+
if (!fs.existsSync(fullPath)) {
|
|
67
|
+
// Try resolving by basename in workspace
|
|
68
|
+
const baseName = path.basename(candidate);
|
|
69
|
+
const found = findCandidateFile(process.cwd(), baseName);
|
|
70
|
+
if (found) fullPath = found;
|
|
71
|
+
else continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const ext = path.extname(fullPath).toLowerCase();
|
|
75
|
+
if (BINARY_EXTENSIONS.has(ext)) continue;
|
|
76
|
+
if (fullPath.includes('node_modules') || fullPath.includes('.git')) continue;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const stat = fs.statSync(fullPath);
|
|
80
|
+
if (!stat.isFile() || stat.size > 50000) continue;
|
|
81
|
+
|
|
82
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
83
|
+
const relPath = path.relative(process.cwd(), fullPath);
|
|
84
|
+
|
|
85
|
+
const lines = content.split('\n');
|
|
86
|
+
const formatted = lines
|
|
87
|
+
.slice(0, 150)
|
|
88
|
+
.map((line, idx) => `${idx + 1}| ${line}`)
|
|
89
|
+
.join('\n');
|
|
90
|
+
|
|
91
|
+
activeFiles[relPath] = formatted;
|
|
92
|
+
count++;
|
|
93
|
+
|
|
94
|
+
// Also inspect relative local imports for JS/TS
|
|
95
|
+
if (['.js', '.ts', '.jsx', '.tsx', '.mjs'].includes(ext)) {
|
|
96
|
+
const importMatches = content.match(/(?:import|require)\s*\(?['"](\.[^'"]+)['"]/g) || [];
|
|
97
|
+
for (const imp of importMatches) {
|
|
98
|
+
const importRel = imp.match(/['"](\.[^'"]+)['"]/)?.[1];
|
|
99
|
+
if (importRel) {
|
|
100
|
+
const dir = path.dirname(fullPath);
|
|
101
|
+
let importedPath = path.resolve(dir, importRel);
|
|
102
|
+
if (!fs.existsSync(importedPath) && fs.existsSync(importedPath + '.js')) {
|
|
103
|
+
importedPath = importedPath + '.js';
|
|
104
|
+
}
|
|
105
|
+
if (fs.existsSync(importedPath) && !activeFiles[path.relative(process.cwd(), importedPath)]) {
|
|
106
|
+
candidates.add(path.relative(process.cwd(), importedPath));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch {}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return activeFiles;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function findCandidateFile(dir, baseName, depth = 0) {
|
|
118
|
+
if (depth > 4) return null;
|
|
119
|
+
const ignored = new Set(['node_modules', '.git', 'dist', 'build', '.cache', 'coverage']);
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
123
|
+
for (const ent of entries) {
|
|
124
|
+
if (ent.isDirectory()) {
|
|
125
|
+
if (!ignored.has(ent.name)) {
|
|
126
|
+
const found = findCandidateFile(path.join(dir, ent.name), baseName, depth + 1);
|
|
127
|
+
if (found) return found;
|
|
128
|
+
}
|
|
129
|
+
} else if (ent.isFile() && ent.name === baseName) {
|
|
130
|
+
return path.join(dir, ent.name);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch {}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function getRecentChanges(maxCommits = 3, errorInput = '') {
|
|
17
138
|
const isRepo = await git.checkIsRepo();
|
|
18
139
|
if (!isRepo) {
|
|
19
140
|
throw new Error('Not inside a valid Git repository.');
|
|
@@ -27,6 +148,9 @@ export async function getRecentChanges(maxCommits = 3) {
|
|
|
27
148
|
':!yarn.lock',
|
|
28
149
|
':!pnpm-lock.yaml',
|
|
29
150
|
':!bun.lockb',
|
|
151
|
+
':!assets/*',
|
|
152
|
+
':!*.gif',
|
|
153
|
+
':!*.cast',
|
|
30
154
|
];
|
|
31
155
|
|
|
32
156
|
const unstagedDiff = await git.diff(['--', '.', ...lockfileIgnores]);
|
|
@@ -58,10 +182,13 @@ export async function getRecentChanges(maxCommits = 3) {
|
|
|
58
182
|
})
|
|
59
183
|
);
|
|
60
184
|
|
|
185
|
+
const activeFileContents = collectActiveFiles(status, commitDiffs, errorInput);
|
|
186
|
+
|
|
61
187
|
return {
|
|
62
188
|
branch: status.current,
|
|
63
|
-
untrackedFiles: status.not_added,
|
|
189
|
+
untrackedFiles: status.not_added,
|
|
64
190
|
workingDiff: sanitizeDiff(combinedWorkingDiff) || null,
|
|
65
191
|
recentCommits: commitDiffs,
|
|
192
|
+
activeFileContents,
|
|
66
193
|
};
|
|
67
|
-
}
|
|
194
|
+
}
|