threadlines 0.1.11 → 0.1.13
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/dist/commands/check.js +47 -4
- package/dist/git/diff.js +24 -7
- package/dist/index.js +1 -1
- package/dist/utils/ci-detection.js +3 -1
- package/package.json +1 -1
package/dist/commands/check.js
CHANGED
|
@@ -47,6 +47,26 @@ const config_1 = require("../utils/config");
|
|
|
47
47
|
const fs = __importStar(require("fs"));
|
|
48
48
|
const path = __importStar(require("path"));
|
|
49
49
|
const chalk_1 = __importDefault(require("chalk"));
|
|
50
|
+
/**
|
|
51
|
+
* Detect the environment where the check is being run
|
|
52
|
+
* Returns: 'vercel', 'github', 'gitlab', or 'local'
|
|
53
|
+
*/
|
|
54
|
+
function detectEnvironment() {
|
|
55
|
+
// Vercel: VERCEL env var is always set in Vercel builds
|
|
56
|
+
if (process.env.VERCEL) {
|
|
57
|
+
return 'vercel';
|
|
58
|
+
}
|
|
59
|
+
// GitHub Actions: GITHUB_ACTIONS env var is always set
|
|
60
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
61
|
+
return 'github';
|
|
62
|
+
}
|
|
63
|
+
// GitLab CI: GITLAB_CI env var is always set, or CI is set with GitLab-specific vars
|
|
64
|
+
if (process.env.GITLAB_CI || (process.env.CI && process.env.CI_COMMIT_SHA)) {
|
|
65
|
+
return 'gitlab';
|
|
66
|
+
}
|
|
67
|
+
// Local development: none of the above
|
|
68
|
+
return 'local';
|
|
69
|
+
}
|
|
50
70
|
async function checkCommand(options) {
|
|
51
71
|
const repoRoot = process.cwd();
|
|
52
72
|
console.log(chalk_1.default.blue('🔍 Threadline: Checking code against your threadlines...\n'));
|
|
@@ -89,6 +109,9 @@ async function checkCommand(options) {
|
|
|
89
109
|
// 2. Determine review target and get git diff
|
|
90
110
|
let gitDiff;
|
|
91
111
|
let reviewContext = { type: 'local' };
|
|
112
|
+
let commitSha = undefined;
|
|
113
|
+
let commitMessage = undefined;
|
|
114
|
+
let prTitle = undefined;
|
|
92
115
|
// Validate mutually exclusive flags
|
|
93
116
|
const explicitFlags = [options.branch, options.commit, options.file, options.folder, options.files].filter(Boolean);
|
|
94
117
|
if (explicitFlags.length > 1) {
|
|
@@ -121,6 +144,12 @@ async function checkCommand(options) {
|
|
|
121
144
|
console.log(chalk_1.default.gray(`📝 Collecting git changes for commit: ${options.commit}...`));
|
|
122
145
|
gitDiff = await (0, diff_1.getCommitDiff)(repoRoot, options.commit);
|
|
123
146
|
reviewContext = { type: 'commit', value: options.commit };
|
|
147
|
+
commitSha = options.commit;
|
|
148
|
+
// Fetch commit message (reliable when we have SHA)
|
|
149
|
+
const message = await (0, diff_1.getCommitMessage)(repoRoot, options.commit);
|
|
150
|
+
if (message) {
|
|
151
|
+
commitMessage = message;
|
|
152
|
+
}
|
|
124
153
|
}
|
|
125
154
|
else {
|
|
126
155
|
// Auto-detect CI environment or use local changes
|
|
@@ -131,6 +160,10 @@ async function checkCommand(options) {
|
|
|
131
160
|
console.log(chalk_1.default.gray(`📝 Collecting git changes for ${autoTarget.type.toUpperCase()}: ${autoTarget.value}...`));
|
|
132
161
|
gitDiff = await (0, diff_1.getPRMRDiff)(repoRoot, autoTarget.sourceBranch, autoTarget.targetBranch);
|
|
133
162
|
reviewContext = { type: autoTarget.type, value: autoTarget.value };
|
|
163
|
+
// Use PR title from GitLab CI (reliable env var)
|
|
164
|
+
if (autoTarget.prTitle) {
|
|
165
|
+
prTitle = autoTarget.prTitle;
|
|
166
|
+
}
|
|
134
167
|
}
|
|
135
168
|
else if (autoTarget.type === 'branch') {
|
|
136
169
|
// Branch: use branch vs base
|
|
@@ -143,6 +176,12 @@ async function checkCommand(options) {
|
|
|
143
176
|
console.log(chalk_1.default.gray(`📝 Collecting git changes for commit: ${autoTarget.value}...`));
|
|
144
177
|
gitDiff = await (0, diff_1.getCommitDiff)(repoRoot, autoTarget.value);
|
|
145
178
|
reviewContext = { type: 'commit', value: autoTarget.value };
|
|
179
|
+
commitSha = autoTarget.value;
|
|
180
|
+
// Fetch commit message (reliable when we have SHA)
|
|
181
|
+
const message = await (0, diff_1.getCommitMessage)(repoRoot, autoTarget.value);
|
|
182
|
+
if (message) {
|
|
183
|
+
commitMessage = message;
|
|
184
|
+
}
|
|
146
185
|
}
|
|
147
186
|
else {
|
|
148
187
|
// Fallback: local development
|
|
@@ -195,14 +234,14 @@ async function checkCommand(options) {
|
|
|
195
234
|
// 4. Get repo name and branch name
|
|
196
235
|
const repoName = await (0, repo_1.getRepoName)(repoRoot);
|
|
197
236
|
const branchName = await (0, repo_1.getBranchName)(repoRoot);
|
|
198
|
-
// 5. Get API URL
|
|
237
|
+
// 5. Get API URL
|
|
199
238
|
const apiUrl = options.apiUrl ||
|
|
200
239
|
process.env.THREADLINE_API_URL ||
|
|
201
|
-
|
|
202
|
-
'http://localhost:3000';
|
|
240
|
+
'https://devthreadline.com';
|
|
203
241
|
// 6. Call review API
|
|
204
242
|
console.log(chalk_1.default.gray('🤖 Running threadline checks...'));
|
|
205
243
|
const client = new client_1.ReviewAPIClient(apiUrl);
|
|
244
|
+
const environment = detectEnvironment();
|
|
206
245
|
const response = await client.review({
|
|
207
246
|
threadlines: threadlinesWithContext,
|
|
208
247
|
diff: gitDiff.diff,
|
|
@@ -210,7 +249,11 @@ async function checkCommand(options) {
|
|
|
210
249
|
apiKey,
|
|
211
250
|
account,
|
|
212
251
|
repoName: repoName || undefined,
|
|
213
|
-
branchName: branchName || undefined
|
|
252
|
+
branchName: branchName || undefined,
|
|
253
|
+
commitSha: commitSha,
|
|
254
|
+
commitMessage: commitMessage,
|
|
255
|
+
prTitle: prTitle,
|
|
256
|
+
environment: environment
|
|
214
257
|
});
|
|
215
258
|
// 7. Display results (with filtering if --full not specified)
|
|
216
259
|
displayResults(response, options.full || false);
|
package/dist/git/diff.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getGitDiff = getGitDiff;
|
|
7
7
|
exports.getBranchDiff = getBranchDiff;
|
|
8
|
+
exports.getCommitMessage = getCommitMessage;
|
|
8
9
|
exports.getCommitDiff = getCommitDiff;
|
|
9
10
|
exports.getPRMRDiff = getPRMRDiff;
|
|
10
11
|
const simple_git_1 = __importDefault(require("simple-git"));
|
|
@@ -23,11 +24,11 @@ async function getGitDiff(repoRoot) {
|
|
|
23
24
|
let diff;
|
|
24
25
|
if (status.staged.length > 0) {
|
|
25
26
|
// Use staged changes
|
|
26
|
-
diff = await git.diff(['--cached']);
|
|
27
|
+
diff = await git.diff(['--cached', '-U200']);
|
|
27
28
|
}
|
|
28
29
|
else if (status.files.length > 0) {
|
|
29
30
|
// Use unstaged changes
|
|
30
|
-
diff = await git.diff();
|
|
31
|
+
diff = await git.diff(['-U200']);
|
|
31
32
|
}
|
|
32
33
|
else {
|
|
33
34
|
// No changes
|
|
@@ -73,7 +74,7 @@ async function getBranchDiff(repoRoot, branchName, baseBranch) {
|
|
|
73
74
|
const previousCommit = await git.revparse(['HEAD~1']).catch(() => null);
|
|
74
75
|
if (previousCommit) {
|
|
75
76
|
// Use commit-based diff instead
|
|
76
|
-
const diff = await git.diff([`${previousCommit}..HEAD
|
|
77
|
+
const diff = await git.diff([`${previousCommit}..HEAD`, '-U200']);
|
|
77
78
|
const diffSummary = await git.diffSummary([`${previousCommit}..HEAD`]);
|
|
78
79
|
const changedFiles = diffSummary.files.map(f => f.file);
|
|
79
80
|
return {
|
|
@@ -135,7 +136,7 @@ async function getBranchDiff(repoRoot, branchName, baseBranch) {
|
|
|
135
136
|
}
|
|
136
137
|
// Get diff between base and branch (cumulative diff of all commits)
|
|
137
138
|
// Format: git diff base...branch (three-dot notation finds common ancestor)
|
|
138
|
-
const diff = await git.diff([`${base}...${branchName}
|
|
139
|
+
const diff = await git.diff([`${base}...${branchName}`, '-U200']);
|
|
139
140
|
// Get list of changed files
|
|
140
141
|
const diffSummary = await git.diffSummary([`${base}...${branchName}`]);
|
|
141
142
|
const changedFiles = diffSummary.files.map(f => f.file);
|
|
@@ -144,6 +145,22 @@ async function getBranchDiff(repoRoot, branchName, baseBranch) {
|
|
|
144
145
|
changedFiles
|
|
145
146
|
};
|
|
146
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Get commit message for a specific commit SHA
|
|
150
|
+
* Returns full commit message (subject + body) or null if commit not found
|
|
151
|
+
*/
|
|
152
|
+
async function getCommitMessage(repoRoot, sha) {
|
|
153
|
+
const git = (0, simple_git_1.default)(repoRoot);
|
|
154
|
+
try {
|
|
155
|
+
// Get full commit message (subject + body)
|
|
156
|
+
const message = await git.show([sha, '--format=%B', '--no-patch']);
|
|
157
|
+
return message.trim() || null;
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
// Commit not found or invalid
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
147
164
|
/**
|
|
148
165
|
* Get diff for a specific commit
|
|
149
166
|
*/
|
|
@@ -160,7 +177,7 @@ async function getCommitDiff(repoRoot, sha) {
|
|
|
160
177
|
let changedFiles;
|
|
161
178
|
try {
|
|
162
179
|
// Get diff using git show
|
|
163
|
-
diff = await git.show([sha, '--format=', '--no-color']);
|
|
180
|
+
diff = await git.show([sha, '--format=', '--no-color', '-U200']);
|
|
164
181
|
// Get changed files using git show --name-only
|
|
165
182
|
const commitFiles = await git.show([sha, '--name-only', '--format=', '--pretty=format:']);
|
|
166
183
|
changedFiles = commitFiles
|
|
@@ -171,7 +188,7 @@ async function getCommitDiff(repoRoot, sha) {
|
|
|
171
188
|
catch (error) {
|
|
172
189
|
// Fallback: try git diff format
|
|
173
190
|
try {
|
|
174
|
-
diff = await git.diff([`${sha}^..${sha}
|
|
191
|
+
diff = await git.diff([`${sha}^..${sha}`, '-U200']);
|
|
175
192
|
// Get files from diff summary
|
|
176
193
|
const diffSummary = await git.diffSummary([`${sha}^..${sha}`]);
|
|
177
194
|
changedFiles = diffSummary.files.map(f => f.file);
|
|
@@ -197,7 +214,7 @@ async function getPRMRDiff(repoRoot, sourceBranch, targetBranch) {
|
|
|
197
214
|
}
|
|
198
215
|
// Get diff between target and source (cumulative diff)
|
|
199
216
|
// Format: git diff target...source (three-dot notation finds common ancestor)
|
|
200
|
-
const diff = await git.diff([`${targetBranch}...${sourceBranch}
|
|
217
|
+
const diff = await git.diff([`${targetBranch}...${sourceBranch}`, '-U200']);
|
|
201
218
|
// Get list of changed files
|
|
202
219
|
const diffSummary = await git.diffSummary([`${targetBranch}...${sourceBranch}`]);
|
|
203
220
|
const changedFiles = diffSummary.files.map(f => f.file);
|
package/dist/index.js
CHANGED
|
@@ -61,7 +61,7 @@ program
|
|
|
61
61
|
program
|
|
62
62
|
.command('check')
|
|
63
63
|
.description('Check code against your threadlines')
|
|
64
|
-
.option('--api-url <url>', 'Threadline server URL', process.env.THREADLINE_API_URL ||
|
|
64
|
+
.option('--api-url <url>', 'Threadline server URL', process.env.THREADLINE_API_URL || 'https://devthreadline.com')
|
|
65
65
|
.option('--full', 'Show all results (compliant, attention, not_relevant). Default: only attention items')
|
|
66
66
|
.option('--branch <name>', 'Review all commits in branch vs base (e.g., --branch feature/new-feature)')
|
|
67
67
|
.option('--commit <ref>', 'Review specific commit. Accepts commit SHA or git reference (e.g., HEAD, HEAD~1, abc123). Example: --commit HEAD')
|
|
@@ -22,12 +22,14 @@ function getAutoReviewTarget() {
|
|
|
22
22
|
const targetBranch = process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;
|
|
23
23
|
const sourceBranch = process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME;
|
|
24
24
|
const mrNumber = process.env.CI_MERGE_REQUEST_IID;
|
|
25
|
+
const mrTitle = process.env.CI_MERGE_REQUEST_TITLE; // Reliable GitLab CI env var
|
|
25
26
|
if (targetBranch && sourceBranch && mrNumber) {
|
|
26
27
|
return {
|
|
27
28
|
type: 'mr',
|
|
28
29
|
value: mrNumber,
|
|
29
30
|
sourceBranch,
|
|
30
|
-
targetBranch
|
|
31
|
+
targetBranch,
|
|
32
|
+
prTitle: mrTitle || undefined // Only include if present
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
35
|
}
|