threadlines 0.1.41 → 0.2.3

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Threadlines
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # threadlines
1
+ # Threadline
2
2
 
3
3
  Threadline CLI - AI-powered linter based on your natural language documentation.
4
4
 
@@ -14,13 +14,13 @@ Getting teams to follow consistent quality standards is **hard**. Really hard.
14
14
 
15
15
  ### What Makes Threadline Different?
16
16
 
17
- - **🎯 Focused Reviews** - Instead of one AI trying to check everything, Threadline runs multiple specialized AI reviewers in parallel. Each threadline focuses on one thing and does it well.
17
+ - **Focused Reviews** - Instead of one AI trying to check everything, Threadline runs multiple specialized AI reviewers in parallel. Each threadline focuses on one thing and does it well.
18
18
 
19
- - **📝 Documentation That Lives With Your Code** - Your coding standards live in your repo, in a `/threadlines` folder. They're version-controlled, reviewable, and always in sync with your codebase.
19
+ - **Documentation That Lives With Your Code** - Your coding standards live in your repo, in a `/threadlines` folder. They're version-controlled, reviewable, and always in sync with your codebase.
20
20
 
21
- - **🔍 Fully Auditable** - Every AI review decision is logged and traceable. You can see exactly what was checked, why it passed or failed, and have confidence in the results.
21
+ - **Fully Auditable** - Every AI review decision is logged and traceable. You can see exactly what was checked, why it passed or failed, and have confidence in the results.
22
22
 
23
- - **⚡ Fast & Parallel** - Multiple threadlines run simultaneously, so you get comprehensive feedback in seconds, not minutes.
23
+ - **Fast & Parallel** - Multiple threadlines run simultaneously, so you get comprehensive feedback in seconds, not minutes.
24
24
 
25
25
  ## Installation
26
26
 
@@ -48,19 +48,6 @@ npx --yes threadlines check
48
48
 
49
49
  The `--yes` flag auto-confirms package installation, preventing prompts that block automation.
50
50
 
51
- ### Option 3: Local Project Dependency (Recommended for Teams)
52
-
53
- ```bash
54
- npm install --save-dev threadlines
55
- ```
56
-
57
- Then use:
58
- ```bash
59
- npx threadlines check
60
- ```
61
-
62
- This ensures everyone on your team uses the same version.
63
-
64
51
  ## Quick Start
65
52
 
66
53
  ### 1. Initialize Your First Threadline
@@ -95,26 +82,6 @@ Edit `threadlines/example.md` with your coding standards, then rename it to some
95
82
  ```bash
96
83
  npx threadlines check
97
84
  ```
98
-
99
- ## Usage
100
-
101
- ### Initialize Threadline Template
102
-
103
- ```bash
104
- threadlines init
105
- ```
106
-
107
- Creates a template threadline file to get you started. The command will:
108
- - Create the `/threadlines` directory if it doesn't exist
109
- - Generate `threadlines/example.md` with boilerplate content
110
- - Display instructions for API key configuration
111
-
112
- ### Check Code Against Threadlines
113
-
114
- ```bash
115
- threadlines check
116
- ```
117
-
118
85
  By default, analyzes your staged/unstaged git changes against all threadlines in the `/threadlines` directory.
119
86
 
120
87
  **Common Use Cases:**
@@ -164,11 +131,15 @@ threadlines check --full
164
131
 
165
132
  ### Environment Variables
166
133
 
167
- - `THREADLINE_API_KEY` - **Required.** Your Threadline API key for authentication
168
- - Can be set in `.env.local` file (recommended for local development)
169
- - Or as an environment variable (required for CI/CD)
170
- - `THREADLINE_API_URL` - Server URL (default: http://localhost:3000)
171
- - Can also be set with `--api-url` flag: `npx threadlines check --api-url http://your-server.com`
134
+ | Variable | Purpose | Required |
135
+ |----------|---------|----------|
136
+ | `THREADLINE_API_KEY` | Authentication with Threadlines API | Yes |
137
+ | `THREADLINE_ACCOUNT` | Your Threadlines account email | Yes |
138
+ | `THREADLINE_API_URL` | Custom API endpoint (default: https://devthreadline.com) | No |
139
+
140
+ Both required variables can be set in a `.env.local` file (recommended for local development) or as environment variables (required for CI/CD).
141
+
142
+ The optional `THREADLINE_API_URL` allows you to point to a custom server if you want to intercept telemetry with your own endpoint. It can also be set with the `--api-url` flag.
172
143
 
173
144
  ## Threadline Files
174
145
 
package/SECURITY.md ADDED
@@ -0,0 +1,47 @@
1
+ # Security Policy
2
+
3
+ ## Environment Variables
4
+
5
+ The Threadlines CLI reads the following environment variables:
6
+
7
+ | Variable | Purpose | Required |
8
+ |----------|---------|----------|
9
+ | `THREADLINE_API_KEY` | Authentication with Threadlines API | Yes |
10
+ | `THREADLINE_ACCOUNT` | Your Threadlines account email | Yes |
11
+ | `THREADLINE_API_URL` | Custom API endpoint (default: https://devthreadline.com) | No |
12
+
13
+ ### What We Do NOT Read
14
+
15
+ This CLI does **not** access, read, or transmit any other environment variables. We do not:
16
+
17
+ - Enumerate `process.env` to discover other secrets
18
+ - Read AWS, GCP, or other cloud credentials
19
+ - Access database connection strings
20
+ - Read any secrets beyond what's documented above
21
+
22
+ ## Data Transmission
23
+
24
+ The CLI sends the following data to the Threadlines API:
25
+
26
+ 1. **Git diff content** - The code changes being analyzed
27
+ 2. **Threadline definitions** - Your markdown files from `/threadlines`
28
+ 3. **Metadata** - Repository name, branch, commit SHA, context files specified in your threadline definitions
29
+
30
+ We do **not** send:
31
+ - Your entire codebase
32
+ - Environment variables or secrets
33
+
34
+ ## Auditing This Package
35
+
36
+ To verify the published npm package matches this source:
37
+
38
+ ```bash
39
+ # Compare published package to source
40
+ npm pack threadlines
41
+ tar -xzf threadlines-*.tgz
42
+ diff -r package/dist dist/ # After building locally
43
+ ```
44
+
45
+ All releases are published via GitHub Actions with npm provenance attestation,
46
+ which cryptographically links each npm package to its source commit.
47
+
@@ -46,7 +46,7 @@ const github_1 = require("../git/github");
46
46
  const gitlab_1 = require("../git/gitlab");
47
47
  const vercel_1 = require("../git/vercel");
48
48
  const local_1 = require("../git/local");
49
- const git_diff_executor_1 = require("../utils/git-diff-executor");
49
+ const diff_1 = require("../git/diff");
50
50
  const fs = __importStar(require("fs"));
51
51
  const path = __importStar(require("path"));
52
52
  const chalk_1 = __importDefault(require("chalk"));
@@ -122,7 +122,6 @@ async function checkCommand(options) {
122
122
  }
123
123
  // 2. Detect environment and context
124
124
  const environment = (0, environment_1.detectEnvironment)();
125
- let context;
126
125
  let gitDiff;
127
126
  let repoName;
128
127
  let branchName;
@@ -138,25 +137,18 @@ async function checkCommand(options) {
138
137
  if (options.file) {
139
138
  console.log(chalk_1.default.gray(`📝 Reading file: ${options.file}...`));
140
139
  gitDiff = await (0, file_1.getFileContent)(repoRoot, options.file);
141
- context = { type: 'local' }; // File context doesn't need git context
142
- // For file/folder/files, repo/branch are not available - skip them
143
140
  }
144
141
  else if (options.folder) {
145
142
  console.log(chalk_1.default.gray(`📝 Reading folder: ${options.folder}...`));
146
143
  gitDiff = await (0, file_1.getFolderContent)(repoRoot, options.folder);
147
- context = { type: 'local' };
148
- // For file/folder/files, repo/branch are not available - skip them
149
144
  }
150
145
  else if (options.files && options.files.length > 0) {
151
146
  console.log(chalk_1.default.gray(`📝 Reading ${options.files.length} file(s)...`));
152
147
  gitDiff = await (0, file_1.getMultipleFilesContent)(repoRoot, options.files);
153
- context = { type: 'local' };
154
- // For file/folder/files, repo/branch are not available - skip them
155
148
  }
156
149
  else if (options.branch) {
157
150
  console.log(chalk_1.default.gray(`📝 Collecting git changes for branch: ${options.branch}...`));
158
- context = { type: 'branch', branchName: options.branch };
159
- gitDiff = await (0, git_diff_executor_1.getDiffForContext)(context, repoRoot, environment);
151
+ gitDiff = await (0, diff_1.getBranchDiff)(repoRoot, options.branch);
160
152
  // Get repo/branch using environment-specific approach
161
153
  if (environment === 'github') {
162
154
  const gitContext = await (0, github_1.getGitHubContext)(repoRoot);
@@ -207,8 +199,7 @@ async function checkCommand(options) {
207
199
  }
208
200
  else if (options.commit) {
209
201
  console.log(chalk_1.default.gray(`📝 Collecting git changes for commit: ${options.commit}...`));
210
- context = { type: 'commit', commitSha: options.commit };
211
- gitDiff = await (0, git_diff_executor_1.getDiffForContext)(context, repoRoot, environment);
202
+ gitDiff = await (0, diff_1.getCommitDiff)(repoRoot, options.commit);
212
203
  // Get repo/branch using environment-specific approach
213
204
  if (environment === 'github') {
214
205
  const gitContext = await (0, github_1.getGitHubContext)(repoRoot);
@@ -283,7 +274,6 @@ async function checkCommand(options) {
283
274
  gitDiff = envContext.diff;
284
275
  repoName = envContext.repoName;
285
276
  branchName = envContext.branchName;
286
- context = envContext.context;
287
277
  // Use metadata from environment context
288
278
  metadata = {
289
279
  commitSha: envContext.commitSha,
@@ -294,9 +284,8 @@ async function checkCommand(options) {
294
284
  };
295
285
  }
296
286
  if (gitDiff.changedFiles.length === 0) {
297
- console.error(chalk_1.default.red(' Error: No changes detected.'));
298
- console.error(chalk_1.default.red(' Threadline check requires code changes to analyze.'));
299
- process.exit(1);
287
+ console.error(chalk_1.default.bold('ℹ️ No changes detected.'));
288
+ process.exit(0);
300
289
  }
301
290
  // Check for zero diff (files changed but no actual code changes)
302
291
  if (!gitDiff.diff || gitDiff.diff.trim() === '') {
@@ -7,9 +7,7 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.getDiffForEnvironment = getDiffForEnvironment;
10
- exports.getDiffForContext = getDiffForContext;
11
10
  exports.getContextDescription = getContextDescription;
12
- const diff_1 = require("../git/diff");
13
11
  const vercel_diff_1 = require("../git/vercel-diff");
14
12
  const github_diff_1 = require("../git/github-diff");
15
13
  const local_diff_1 = require("../git/local-diff");
@@ -45,36 +43,6 @@ async function getDiffForEnvironment(environment, repoRoot, _context) {
45
43
  throw new Error(`Unknown environment: ${_exhaustive}`);
46
44
  }
47
45
  }
48
- /**
49
- * Legacy GitLab-specific diff function (deprecated).
50
- *
51
- * This function is kept for backward compatibility but should not be used.
52
- * GitLab now uses getDiffForEnvironment() which routes to getGitLabDiff().
53
- *
54
- * @deprecated Use getDiffForEnvironment('gitlab', repoRoot) instead
55
- */
56
- async function getDiffForContext(context, repoRoot, environment) {
57
- // This should only be called for GitLab legacy code paths
58
- if (environment !== 'gitlab') {
59
- throw new Error(`getDiffForContext() is deprecated. Use getDiffForEnvironment('${environment}', repoRoot) instead.`);
60
- }
61
- switch (context.type) {
62
- case 'pr':
63
- return await (0, diff_1.getPRMRDiff)(repoRoot, context.sourceBranch, context.targetBranch);
64
- case 'mr':
65
- return await (0, diff_1.getPRMRDiff)(repoRoot, context.sourceBranch, context.targetBranch);
66
- case 'branch':
67
- return await (0, diff_1.getBranchDiff)(repoRoot, context.branchName);
68
- case 'commit':
69
- return await (0, diff_1.getCommitDiff)(repoRoot, context.commitSha);
70
- case 'local':
71
- throw new Error('Local context should use getDiffForEnvironment(), not getDiffForContext()');
72
- default:
73
- // TypeScript exhaustiveness check - should never reach here
74
- const _exhaustive = context;
75
- throw new Error(`Unknown context type: ${_exhaustive}`);
76
- }
77
- }
78
46
  /**
79
47
  * Returns a human-readable description of the context for logging.
80
48
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "threadlines",
3
- "version": "0.1.41",
4
- "description": "Threadline CLI - AI-powered linter based on your natural language documentation",
3
+ "version": "0.2.3",
4
+ "description": "Threadlines CLI - AI-powered linter based on your natural language documentation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "threadline": "./bin/threadline"
@@ -9,7 +9,9 @@
9
9
  "files": [
10
10
  "dist",
11
11
  "bin",
12
- "README.md"
12
+ "README.md",
13
+ "LICENSE",
14
+ "SECURITY.md"
13
15
  ],
14
16
  "keywords": [
15
17
  "code-quality",
@@ -17,15 +19,20 @@
17
19
  "ai",
18
20
  "code-review",
19
21
  "threadline",
20
- "code-standards"
22
+ "code-standards",
23
+ "documentation",
24
+ "llm"
21
25
  ],
22
- "author": "",
26
+ "author": "Threadlines",
23
27
  "license": "MIT",
24
28
  "repository": {
25
29
  "type": "git",
26
- "url": "git+https://github.com/ngrootscholten/threadline.git",
27
- "directory": "packages/cli"
30
+ "url": "git+https://github.com/ngrootscholten/threadline-cli.git"
28
31
  },
32
+ "bugs": {
33
+ "url": "https://github.com/ngrootscholten/threadline-cli/issues"
34
+ },
35
+ "homepage": "https://devthreadline.com/",
29
36
  "scripts": {
30
37
  "build": "npm run check && tsc",
31
38
  "dev": "tsc --watch",
@@ -34,10 +41,10 @@
34
41
  "lint": "eslint src/**/*.ts",
35
42
  "lint:fix": "eslint src/**/*.ts --fix",
36
43
  "typecheck": "tsc --noEmit",
37
- "check": "npm run lint && npm run typecheck",
38
- "threadlines": "npx -y threadlines check",
39
- "threadlines:local": "npx -y threadlines check || exit 0",
40
- "check:all": "npm run check && npm run threadlines"
44
+ "check": "npm run lint && npm run typecheck"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
41
48
  },
42
49
  "dependencies": {
43
50
  "axios": "^1.7.9",
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAutoReviewTarget = getAutoReviewTarget;
4
- function getAutoReviewTarget() {
5
- // 1. PR/MR context
6
- if (process.env.GITHUB_EVENT_NAME === 'pull_request') {
7
- const targetBranch = process.env.GITHUB_BASE_REF;
8
- const sourceBranch = process.env.GITHUB_HEAD_REF;
9
- const prNumber = process.env.GITHUB_EVENT_PULL_REQUEST_NUMBER || process.env.GITHUB_EVENT_NUMBER;
10
- // Log PR detection for verification (remove after confirming it works)
11
- console.log(`[PR Detection] GITHUB_BASE_REF=${targetBranch || 'NOT SET'}, GITHUB_HEAD_REF=${sourceBranch || 'NOT SET'}, PR_NUMBER=${prNumber || 'NOT SET'}`);
12
- if (targetBranch && sourceBranch && prNumber) {
13
- return {
14
- type: 'pr',
15
- value: prNumber,
16
- sourceBranch,
17
- targetBranch
18
- };
19
- }
20
- }
21
- if (process.env.CI_MERGE_REQUEST_IID) {
22
- const targetBranch = process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;
23
- const sourceBranch = process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME;
24
- const mrNumber = process.env.CI_MERGE_REQUEST_IID;
25
- const mrTitle = process.env.CI_MERGE_REQUEST_TITLE;
26
- if (targetBranch && sourceBranch && mrNumber) {
27
- return {
28
- type: 'mr',
29
- value: mrNumber,
30
- sourceBranch,
31
- targetBranch,
32
- prTitle: mrTitle || undefined
33
- };
34
- }
35
- }
36
- // 2. Branch name
37
- const branch = process.env.GITHUB_REF_NAME ||
38
- process.env.CI_COMMIT_REF_NAME ||
39
- process.env.VERCEL_GIT_COMMIT_REF;
40
- if (branch) {
41
- return { type: 'branch', value: branch };
42
- }
43
- // 3. Commit SHA
44
- const commit = process.env.GITHUB_SHA ||
45
- process.env.CI_COMMIT_SHA ||
46
- process.env.VERCEL_GIT_COMMIT_SHA;
47
- if (commit) {
48
- return { type: 'commit', value: commit };
49
- }
50
- // 4. Local development
51
- return null;
52
- }