threadlines 0.2.6 → 0.2.9

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.
@@ -1,190 +1,10 @@
1
1
  "use strict";
2
2
  /**
3
- * Review Context Detection
3
+ * Review Context Types
4
4
  *
5
- * Determines what type of code review context we're in:
6
- * - PR/MR: Multiple commits comparing two branches
7
- * - Branch: All commits on a branch vs base branch
8
- * - Commit: Single commit changes
5
+ * Type definitions for the different code review contexts:
6
+ * - PR/MR: Comparing source branch vs target branch (branch-level diff)
7
+ * - Commit: Single commit changes (any push without PR/MR)
9
8
  * - Local: Staged/unstaged changes in working directory
10
- *
11
- * Context detection is environment-specific - each CI platform
12
- * provides different environment variables.
13
9
  */
14
10
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.detectContext = detectContext;
16
- /**
17
- * Detects the review context based on the environment.
18
- *
19
- * Each environment has different environment variables available,
20
- * so detection logic is environment-specific.
21
- */
22
- function detectContext(environment) {
23
- switch (environment) {
24
- case 'github':
25
- return detectGitHubContext();
26
- case 'gitlab':
27
- return detectGitLabContext();
28
- case 'bitbucket':
29
- return detectBitbucketContext();
30
- case 'vercel':
31
- return detectVercelContext();
32
- case 'local':
33
- return { type: 'local' };
34
- default:
35
- return { type: 'local' };
36
- }
37
- }
38
- /**
39
- * GitHub Actions context detection
40
- *
41
- * Environment Variables:
42
- * - PR: GITHUB_EVENT_NAME='pull_request', GITHUB_BASE_REF, GITHUB_HEAD_REF, GITHUB_EVENT_NUMBER
43
- * - Branch: GITHUB_REF_NAME
44
- * - Commit: GITHUB_SHA
45
- */
46
- function detectGitHubContext() {
47
- // 1. Check for PR context
48
- if (process.env.GITHUB_EVENT_NAME === 'pull_request') {
49
- const targetBranch = process.env.GITHUB_BASE_REF;
50
- const sourceBranch = process.env.GITHUB_HEAD_REF;
51
- const prNumber = process.env.GITHUB_EVENT_PULL_REQUEST_NUMBER || process.env.GITHUB_EVENT_NUMBER;
52
- if (targetBranch && sourceBranch && prNumber) {
53
- return {
54
- type: 'pr',
55
- prNumber,
56
- sourceBranch,
57
- targetBranch
58
- };
59
- }
60
- }
61
- // 2. Check for branch context
62
- if (process.env.GITHUB_REF_NAME) {
63
- return {
64
- type: 'branch',
65
- branchName: process.env.GITHUB_REF_NAME
66
- };
67
- }
68
- // 3. Check for commit context
69
- if (process.env.GITHUB_SHA) {
70
- return {
71
- type: 'commit',
72
- commitSha: process.env.GITHUB_SHA
73
- };
74
- }
75
- // 4. Fallback to local
76
- return { type: 'local' };
77
- }
78
- /**
79
- * GitLab CI context detection
80
- *
81
- * Environment Variables:
82
- * - MR: CI_MERGE_REQUEST_IID, CI_MERGE_REQUEST_TARGET_BRANCH_NAME, CI_MERGE_REQUEST_SOURCE_BRANCH_NAME, CI_MERGE_REQUEST_TITLE
83
- * - Branch: CI_COMMIT_REF_NAME
84
- * - Commit: CI_COMMIT_SHA
85
- */
86
- function detectGitLabContext() {
87
- // 1. Check for MR context
88
- if (process.env.CI_MERGE_REQUEST_IID) {
89
- const targetBranch = process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;
90
- const sourceBranch = process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME;
91
- const mrNumber = process.env.CI_MERGE_REQUEST_IID;
92
- const mrTitle = process.env.CI_MERGE_REQUEST_TITLE;
93
- if (targetBranch && sourceBranch && mrNumber) {
94
- return {
95
- type: 'mr',
96
- mrNumber,
97
- sourceBranch,
98
- targetBranch,
99
- prTitle: mrTitle || undefined
100
- };
101
- }
102
- }
103
- // 2. Check for branch context
104
- if (process.env.CI_COMMIT_REF_NAME) {
105
- return {
106
- type: 'branch',
107
- branchName: process.env.CI_COMMIT_REF_NAME
108
- };
109
- }
110
- // 3. Check for commit context
111
- if (process.env.CI_COMMIT_SHA) {
112
- return {
113
- type: 'commit',
114
- commitSha: process.env.CI_COMMIT_SHA
115
- };
116
- }
117
- // 4. Fallback to local
118
- return { type: 'local' };
119
- }
120
- /**
121
- * Vercel context detection
122
- *
123
- * Environment Variables:
124
- * - Branch: VERCEL_GIT_COMMIT_REF
125
- * - Commit: VERCEL_GIT_COMMIT_SHA
126
- *
127
- * Vercel Limitation:
128
- * Vercel performs shallow clones of the repository, typically fetching only the
129
- * specific commit being deployed. The git repository in Vercel's build environment
130
- * does not contain the full git history or remote branch references (e.g., origin/main).
131
- * This means branch-based diff operations (comparing feature branch against base branch)
132
- * are not possible because the base branch refs are not available in the repository.
133
- *
134
- * Solution:
135
- * We hardcode commit context for Vercel, using VERCEL_GIT_COMMIT_SHA to get a
136
- * commit-based diff (comparing the commit against its parent). This works within
137
- * Vercel's constraints since we only need the commit SHA, not branch references.
138
- */
139
- function detectVercelContext() {
140
- // Hardcode commit context for Vercel due to shallow clone limitations
141
- // Vercel's git repository doesn't have base branch refs available
142
- if (process.env.VERCEL_GIT_COMMIT_SHA) {
143
- return {
144
- type: 'commit',
145
- commitSha: process.env.VERCEL_GIT_COMMIT_SHA
146
- };
147
- }
148
- // Fallback to local
149
- return { type: 'local' };
150
- }
151
- /**
152
- * Bitbucket Pipelines context detection
153
- *
154
- * Environment Variables (all tested 2026-01-18):
155
- * - Branch: BITBUCKET_BRANCH
156
- * - Commit: BITBUCKET_COMMIT
157
- * - PR: BITBUCKET_PR_ID, BITBUCKET_PR_DESTINATION_BRANCH
158
- *
159
- * Note: Bitbucket does not provide PR title as an environment variable.
160
- */
161
- function detectBitbucketContext() {
162
- // PR context
163
- const prId = process.env.BITBUCKET_PR_ID;
164
- const prDestinationBranch = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
165
- const sourceBranch = process.env.BITBUCKET_BRANCH;
166
- if (prId && prDestinationBranch && sourceBranch) {
167
- return {
168
- type: 'pr',
169
- prNumber: prId,
170
- sourceBranch,
171
- targetBranch: prDestinationBranch
172
- };
173
- }
174
- // Branch context
175
- if (process.env.BITBUCKET_BRANCH) {
176
- return {
177
- type: 'branch',
178
- branchName: process.env.BITBUCKET_BRANCH
179
- };
180
- }
181
- // Commit context
182
- if (process.env.BITBUCKET_COMMIT) {
183
- return {
184
- type: 'commit',
185
- commitSha: process.env.BITBUCKET_COMMIT
186
- };
187
- }
188
- // Fallback to local
189
- return { type: 'local' };
190
- }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = void 0;
7
+ exports.enableDebug = enableDebug;
8
+ exports.isDebugEnabled = isDebugEnabled;
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ /**
11
+ * Global debug flag - set when --debug is passed to CLI
12
+ */
13
+ let debugEnabled = false;
14
+ /**
15
+ * Enable debug logging (called when --debug flag is set)
16
+ */
17
+ function enableDebug() {
18
+ debugEnabled = true;
19
+ }
20
+ /**
21
+ * Check if debug logging is enabled
22
+ */
23
+ function isDebugEnabled() {
24
+ return debugEnabled;
25
+ }
26
+ /**
27
+ * Logger utility for CLI output
28
+ *
29
+ * - debug/info: Only shown when --debug flag is set
30
+ * - warn/error: Always shown (critical information)
31
+ */
32
+ exports.logger = {
33
+ /**
34
+ * Debug-level log (technical details, internal state)
35
+ * Only shown with --debug flag
36
+ */
37
+ debug: (message) => {
38
+ if (debugEnabled) {
39
+ console.log(chalk_1.default.gray(`[DEBUG] ${message}`));
40
+ }
41
+ },
42
+ /**
43
+ * Info-level log (what's happening, progress updates)
44
+ * Only shown with --debug flag
45
+ */
46
+ info: (message) => {
47
+ if (debugEnabled) {
48
+ console.log(chalk_1.default.blue(`[INFO] ${message}`));
49
+ }
50
+ },
51
+ /**
52
+ * Warning (non-fatal issues, recommendations)
53
+ * Always shown
54
+ */
55
+ warn: (message) => {
56
+ console.log(chalk_1.default.yellow(`⚠️ ${message}`));
57
+ },
58
+ /**
59
+ * Error (failures, problems)
60
+ * Always shown
61
+ */
62
+ error: (message) => {
63
+ console.error(chalk_1.default.red(`❌ ${message}`));
64
+ }
65
+ };
@@ -38,6 +38,7 @@ exports.validateThreadline = validateThreadline;
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const yaml = __importStar(require("js-yaml"));
41
+ const logger_1 = require("../utils/logger");
41
42
  const REQUIRED_FIELDS = ['id', 'version', 'patterns'];
42
43
  /**
43
44
  * Find and validate all threadlines in the threadlines folder.
@@ -61,7 +62,7 @@ async function findThreadlines(searchRoot, gitRoot) {
61
62
  threadlines.push(result.threadline);
62
63
  }
63
64
  else {
64
- console.warn(`⚠️ Skipping ${file}: ${result.errors?.join(', ')}`);
65
+ logger_1.logger.warn(`Skipping ${file}: ${result.errors?.join(', ')}`);
65
66
  }
66
67
  }
67
68
  return threadlines;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadlines",
3
- "version": "0.2.6",
3
+ "version": "0.2.9",
4
4
  "description": "Threadlines CLI - AI-powered linter based on your natural language documentation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/dist/git/repo.js DELETED
@@ -1,253 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.getGitHubRepoName = getGitHubRepoName;
40
- exports.getVercelRepoName = getVercelRepoName;
41
- exports.getLocalRepoName = getLocalRepoName;
42
- exports.getGitHubBranchName = getGitHubBranchName;
43
- exports.getVercelBranchName = getVercelBranchName;
44
- exports.getLocalBranchName = getLocalBranchName;
45
- exports.getGitLabRepoName = getGitLabRepoName;
46
- exports.getGitLabBranchName = getGitLabBranchName;
47
- exports.getDefaultBranchName = getDefaultBranchName;
48
- const simple_git_1 = __importDefault(require("simple-git"));
49
- const fs = __importStar(require("fs"));
50
- const path = __importStar(require("path"));
51
- /**
52
- * GitHub Actions: Get repository name
53
- *
54
- * Uses GITHUB_REPOSITORY environment variable (format: "owner/repo").
55
- * This is the ONLY method for GitHub - no fallbacks, no alternatives.
56
- */
57
- async function getGitHubRepoName(_repoRoot) {
58
- const githubRepo = process.env.GITHUB_REPOSITORY;
59
- if (!githubRepo) {
60
- throw new Error('GitHub Actions: GITHUB_REPOSITORY environment variable is not set. ' +
61
- 'This should be automatically provided by GitHub Actions.');
62
- }
63
- const serverUrl = process.env.GITHUB_SERVER_URL || 'https://github.com';
64
- return `${serverUrl}/${githubRepo}.git`;
65
- }
66
- /**
67
- * Vercel: Get repository name
68
- *
69
- * Uses VERCEL_GIT_REPO_OWNER and VERCEL_GIT_REPO_SLUG environment variables.
70
- * This is the ONLY method for Vercel - no fallbacks, no alternatives.
71
- */
72
- async function getVercelRepoName(_repoRoot) {
73
- const owner = process.env.VERCEL_GIT_REPO_OWNER;
74
- const slug = process.env.VERCEL_GIT_REPO_SLUG;
75
- if (!owner || !slug) {
76
- throw new Error('Vercel: VERCEL_GIT_REPO_OWNER or VERCEL_GIT_REPO_SLUG environment variable is not set. ' +
77
- 'This should be automatically provided by Vercel CI.');
78
- }
79
- return `https://github.com/${owner}/${slug}.git`;
80
- }
81
- /**
82
- * Local: Get repository name
83
- *
84
- * Uses git command to get origin remote URL.
85
- * This is the ONLY method for local - no fallbacks, no alternatives.
86
- * Git should always be available in local development.
87
- */
88
- async function getLocalRepoName(repoRoot) {
89
- const git = (0, simple_git_1.default)(repoRoot);
90
- // Check if we're in a git repo
91
- const isRepo = await git.checkIsRepo();
92
- if (!isRepo) {
93
- throw new Error('Local: Not a git repository. Threadline requires a git repository.');
94
- }
95
- try {
96
- const remotes = await git.getRemotes(true);
97
- const origin = remotes.find(r => r.name === 'origin');
98
- if (!origin || !origin.refs?.fetch) {
99
- throw new Error('Local: No origin remote found. ' +
100
- 'Please configure an origin remote: git remote add origin <url>');
101
- }
102
- return origin.refs.fetch;
103
- }
104
- catch (error) {
105
- // If it's already our error, re-throw it
106
- if (error instanceof Error && error.message.includes('Local:')) {
107
- throw error;
108
- }
109
- // Otherwise, wrap it
110
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
111
- throw new Error(`Local: Failed to get repository name from git: ${errorMessage}`);
112
- }
113
- }
114
- /**
115
- * GitHub Actions: Get branch name
116
- *
117
- * Uses GITHUB_REF_NAME environment variable.
118
- * This is the ONLY method for GitHub - no fallbacks, no alternatives.
119
- */
120
- async function getGitHubBranchName(_repoRoot) {
121
- const refName = process.env.GITHUB_REF_NAME;
122
- if (!refName) {
123
- throw new Error('GitHub Actions: GITHUB_REF_NAME environment variable is not set. ' +
124
- 'This should be automatically provided by GitHub Actions.');
125
- }
126
- return refName;
127
- }
128
- /**
129
- * Vercel: Get branch name
130
- *
131
- * Uses VERCEL_GIT_COMMIT_REF environment variable.
132
- * This is the ONLY method for Vercel - no fallbacks, no alternatives.
133
- */
134
- async function getVercelBranchName(_repoRoot) {
135
- const branchName = process.env.VERCEL_GIT_COMMIT_REF;
136
- if (!branchName) {
137
- throw new Error('Vercel: VERCEL_GIT_COMMIT_REF environment variable is not set. ' +
138
- 'This should be automatically provided by Vercel CI.');
139
- }
140
- return branchName;
141
- }
142
- /**
143
- * Local: Get branch name
144
- *
145
- * Uses git command to get current branch name.
146
- * This is the ONLY method for local - no fallbacks, no alternatives.
147
- * Git should always be available in local development.
148
- */
149
- async function getLocalBranchName(repoRoot) {
150
- const git = (0, simple_git_1.default)(repoRoot);
151
- // Check if we're in a git repo
152
- const isRepo = await git.checkIsRepo();
153
- if (!isRepo) {
154
- throw new Error('Local: Not a git repository. Threadline requires a git repository.');
155
- }
156
- try {
157
- const branchName = await git.revparse(['--abbrev-ref', 'HEAD']);
158
- if (!branchName || branchName.trim() === '') {
159
- throw new Error('Local: Could not determine branch name. ' +
160
- 'This might be a brand new repository with no commits. ' +
161
- 'Make at least one commit before running threadlines check.');
162
- }
163
- // Handle detached HEAD state
164
- if (branchName === 'HEAD') {
165
- throw new Error('Local: Currently in detached HEAD state. ' +
166
- 'Please checkout a branch before running threadlines check.');
167
- }
168
- return branchName.trim();
169
- }
170
- catch (error) {
171
- // If it's already our error, re-throw it
172
- if (error instanceof Error && error.message.includes('Local:')) {
173
- throw error;
174
- }
175
- // Otherwise, wrap it
176
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
177
- throw new Error(`Local: Failed to get branch name from git: ${errorMessage}`);
178
- }
179
- }
180
- /**
181
- * GitLab CI: Get repository name
182
- *
183
- * Uses CI_PROJECT_URL environment variable.
184
- * This is the ONLY method for GitLab - no fallbacks, no alternatives.
185
- */
186
- async function getGitLabRepoName(_repoRoot) {
187
- const projectUrl = process.env.CI_PROJECT_URL;
188
- if (!projectUrl) {
189
- throw new Error('GitLab CI: CI_PROJECT_URL environment variable is not set. ' +
190
- 'This should be automatically provided by GitLab CI.');
191
- }
192
- // CI_PROJECT_URL is like "https://gitlab.com/owner/repo"
193
- // Add .git suffix for consistency with other environments
194
- return `${projectUrl}.git`;
195
- }
196
- /**
197
- * GitLab CI: Get branch name
198
- *
199
- * Uses CI_COMMIT_REF_NAME environment variable.
200
- * This is the ONLY method for GitLab - no fallbacks, no alternatives.
201
- */
202
- async function getGitLabBranchName(_repoRoot) {
203
- const refName = process.env.CI_COMMIT_REF_NAME;
204
- if (!refName) {
205
- throw new Error('GitLab CI: CI_COMMIT_REF_NAME environment variable is not set. ' +
206
- 'This should be automatically provided by GitLab CI.');
207
- }
208
- return refName;
209
- }
210
- /**
211
- * Detects the default branch name of the repository for GitHub Actions.
212
- *
213
- * Uses GITHUB_EVENT_PATH JSON (repository.default_branch) - the most authoritative source
214
- * provided directly by GitHub Actions.
215
- *
216
- * This function is ONLY called from GitHub Actions context (github.ts),
217
- * so GITHUB_EVENT_PATH should always be available. If it's not, we fail with a clear error.
218
- *
219
- * Returns the branch name (e.g., "main", "master") without the "origin/" prefix.
220
- * Throws an error if the default branch cannot be detected.
221
- */
222
- async function getDefaultBranchName(_repoRoot) {
223
- // GitHub Actions provides GITHUB_EVENT_PATH which contains repository.default_branch
224
- const githubEventPath = process.env.GITHUB_EVENT_PATH;
225
- if (!githubEventPath) {
226
- throw new Error('GITHUB_EVENT_PATH environment variable is not set. ' +
227
- 'This should be automatically provided by GitHub Actions. ' +
228
- 'This function should only be called in GitHub Actions context.');
229
- }
230
- try {
231
- const eventPath = path.resolve(githubEventPath);
232
- if (!fs.existsSync(eventPath)) {
233
- throw new Error(`GITHUB_EVENT_PATH file does not exist: ${eventPath}`);
234
- }
235
- const eventJson = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
236
- const defaultBranch = eventJson.repository?.default_branch;
237
- if (!defaultBranch || typeof defaultBranch !== 'string') {
238
- throw new Error('Could not find repository.default_branch in GITHUB_EVENT_PATH JSON. ' +
239
- 'This should be automatically provided by GitHub Actions.');
240
- }
241
- return defaultBranch;
242
- }
243
- catch (error) {
244
- // If it's already our error, re-throw it
245
- if (error instanceof Error && (error.message.includes('GITHUB_EVENT_PATH') || error.message.includes('default_branch'))) {
246
- throw error;
247
- }
248
- // Otherwise, wrap it
249
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
250
- throw new Error(`Failed to read or parse GITHUB_EVENT_PATH: ${errorMessage}. ` +
251
- 'This should be automatically provided by GitHub Actions.');
252
- }
253
- }