@ataraxy-labs/sem 0.5.5 → 0.9.0

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
@@ -6,6 +6,10 @@
6
6
  <img src="assets/banner.svg" alt="sem" width="600" />
7
7
  </p>
8
8
 
9
+ <p align="center">
10
+ <a href="https://trendshift.io/repositories/25348" target="_blank"><img src="https://trendshift.io/api/badge/repositories/25348" alt="Ataraxy-Labs%2Fsem | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
11
+ </p>
12
+
9
13
  <p align="center">
10
14
  <strong>Semantic version control built on Git.</strong><br>
11
15
  Instead of lines changed, sem tells you what entities changed: functions, methods, classes.
@@ -24,7 +28,7 @@
24
28
  <img src="https://img.shields.io/badge/rust-stable-orange" alt="Rust">
25
29
  <img src="https://img.shields.io/badge/tests-133_passing-brightgreen" alt="Tests">
26
30
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-yellow" alt="License"></a>
27
- <img src="https://img.shields.io/badge/languages-26-blue" alt="Languages">
31
+ <img src="https://img.shields.io/badge/languages-31-blue" alt="Languages">
28
32
  </p>
29
33
 
30
34
  sem is a semantic version control tool that works on top of Git. It parses your code with tree-sitter, extracts every function, class, and method as an entity, and diffs at the entity level instead of lines. This means you see "function `blahh` was modified" instead of "lines x-y changed."
@@ -37,6 +41,12 @@ It works in any Git repo with no setup.
37
41
 
38
42
  ## Install
39
43
 
44
+ ```bash
45
+ curl -fsSL https://raw.githubusercontent.com/Ataraxy-Labs/sem/main/install.sh | sh
46
+ ```
47
+
48
+ Or via Homebrew:
49
+
40
50
  ```bash
41
51
  brew install sem-cli
42
52
  ```
@@ -92,6 +102,8 @@ If you installed via npm/bun, the binary lives in `node_modules/.bin/sem` and is
92
102
 
93
103
  Works in any Git repo. No setup required. Also works outside Git for arbitrary file comparison.
94
104
 
105
+ sem stores its SQLite entity cache outside the repository, under the OS cache directory by default. Set `SEM_CACHE_DIR=/path/to/cache` to override the cache root; repo-local overrides are ignored so cache files do not dirty the working tree.
106
+
95
107
  ### sem diff
96
108
 
97
109
  Entity-level diff with rename detection, structural hashing, and word-level inline highlights.
@@ -154,6 +166,9 @@ sem impact authenticateUser --json
154
166
 
155
167
  # Disambiguate by file
156
168
  sem impact authenticateUser --file src/auth.ts
169
+
170
+ # Include generated/build directories that repo-wide scans skip by default
171
+ sem impact authenticateUser --no-default-excludes
157
172
  ```
158
173
 
159
174
  ### sem blame
@@ -198,11 +213,15 @@ sem entities src/auth.ts
198
213
  # JSON output
199
214
  sem entities --json
200
215
  sem entities src/auth.ts --json
216
+
217
+ # Include generated/build directories that repo-wide scans skip by default
218
+ sem entities --no-default-excludes
201
219
  ```
202
220
 
203
221
  ### sem context
204
222
 
205
- Token-budgeted context for LLMs: the entity, its dependencies, and its dependents, fitted to a token budget.
223
+ Token-budgeted context for LLMs: the entity, its dependencies, and its dependents, fitted to a strict content token budget.
224
+ When the target signature itself does not fit, JSON output reports `target_omitted: true`.
206
225
 
207
226
  ```bash
208
227
  sem context authenticateUser
@@ -212,6 +231,9 @@ sem context authenticateUser --budget 4000
212
231
 
213
232
  # JSON output
214
233
  sem context authenticateUser --json
234
+
235
+ # Include generated/build directories that repo-wide scans skip by default
236
+ sem context authenticateUser --no-default-excludes
215
237
  ```
216
238
 
217
239
  ## Use as default Git diff
@@ -232,7 +254,7 @@ sem unsetup
232
254
 
233
255
  ## What it parses
234
256
 
235
- 26 programming languages with full entity extraction via tree-sitter:
257
+ 31 programming languages with full entity extraction via tree-sitter:
236
258
 
237
259
  | Language | Extensions | Entities |
238
260
  |----------|-----------|----------|
@@ -261,6 +283,11 @@ sem unsetup
261
283
  | Dart | `.dart` | classes, mixins, extensions, enums, type aliases, functions |
262
284
  | OCaml | `.ml` `.mli` | values, modules, types, classes, externals |
263
285
  | Scala | `.scala` `.sc` `.sbt` | classes, objects, traits, enums, functions, vals, extensions |
286
+ | Nix | `.nix` | bindings, inherit declarations |
287
+ | Haskell | `.hs` | functions, signatures, data types, newtypes, classes, instances, type synonyms |
288
+ | Elm | `.elm` | value declarations, type aliases, type declarations, port annotations, infix declarations |
289
+ | Clojure | `.clj` `.cljs` `.cljc` | vars, functions, macros, multimethods, protocols, records, types |
290
+ | D | `.d` `.di` | modules, functions, classes, structs, interfaces, unions, enums, templates, aliases, unittests |
264
291
  | Zig | `.zig` | functions, tests, variables |
265
292
 
266
293
  Plus structured data formats:
@@ -270,6 +297,7 @@ Plus structured data formats:
270
297
  | JSON | `.json` | properties, objects (RFC 6901 paths) |
271
298
  | YAML | `.yml` `.yaml` | sections, properties (dot paths) |
272
299
  | TOML | `.toml` | sections, properties |
300
+ | EDN | `.edn` | top-level map entries (keyword keys) |
273
301
  | CSV | `.csv` `.tsv` | rows (first column as identity) |
274
302
  | Markdown | `.md` `.mdx` | heading-based sections |
275
303
 
@@ -333,6 +361,11 @@ sem diff --format json
333
361
  "added": 1,
334
362
  "modified": 1,
335
363
  "deleted": 1,
364
+ "moved": 0,
365
+ "renamed": 0,
366
+ "reordered": 0,
367
+ "binary": 0,
368
+ "orphan": 0,
336
369
  "total": 3
337
370
  },
338
371
  "changes": [
@@ -341,12 +374,19 @@ sem diff --format json
341
374
  "changeType": "added",
342
375
  "entityType": "function",
343
376
  "entityName": "validateToken",
377
+ "startLine": 12,
378
+ "endLine": 18,
379
+ "oldStartLine": null,
380
+ "oldEndLine": null,
344
381
  "filePath": "src/auth.ts"
345
382
  }
346
- ]
383
+ ],
384
+ "binaryChanges": []
347
385
  }
348
386
  ```
349
387
 
388
+ The named change-type buckets (`added`, `modified`, `deleted`, `moved`, `renamed`, `reordered`) always sum to `total`. `orphan` is a cross-cutting metadata count for module-level changes, and those changes are already included in the named change-type buckets.
389
+
350
390
  ## As a library
351
391
 
352
392
  sem-core can be used as a Rust library dependency:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ataraxy-labs/sem",
3
- "version": "0.5.5",
3
+ "version": "0.9.0",
4
4
  "description": "npm wrapper for the sem CLI. Downloads the matching release binary and exposes the sem command in node_modules/.bin.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
@@ -42,6 +42,6 @@
42
42
  "scripts": {
43
43
  "postinstall": "node ./scripts/postinstall.mjs",
44
44
  "prepack": "node ./scripts/sync-package-version.mjs",
45
- "test": "node ./scripts/package-meta.test.mjs"
45
+ "test": "node --test"
46
46
  }
47
47
  }
@@ -38,9 +38,21 @@ async function downloadFile(url, destinationPath) {
38
38
  }
39
39
 
40
40
  function extractArchive(archivePath, outputDirectory) {
41
- const result = spawnSync('tar', ['-xzf', archivePath, '-C', outputDirectory], {
42
- stdio: 'pipe',
43
- });
41
+ const args = ['-xzf', archivePath, '-C', outputDirectory];
42
+ let result = spawnSync('tar', args, { stdio: 'pipe' });
43
+
44
+ // On Windows, GNU tar (from Git for Windows / MSYS2) interprets colons in
45
+ // paths as remote host separators (host:path). If extraction fails with a
46
+ // colon-related error, retry with --force-local which disables this behavior.
47
+ // We don't add it unconditionally because the Windows built-in bsdtar doesn't
48
+ // recognize the flag.
49
+ if (
50
+ process.platform === 'win32' &&
51
+ result.status !== 0 &&
52
+ result.stderr?.toString('utf8').includes('Cannot connect to')
53
+ ) {
54
+ result = spawnSync('tar', [...args, '--force-local'], { stdio: 'pipe' });
55
+ }
44
56
 
45
57
  if (result.error) {
46
58
  throw new Error(`Failed to extract archive: ${result.error.message}`);
@@ -5,19 +5,36 @@ import fs from 'node:fs/promises';
5
5
  * Downloads checksums.txt from the release, verifies the archive matches.
6
6
  * Returns silently on success, throws on mismatch or missing checksum.
7
7
  */
8
- export async function verifyChecksum(archivePath, archiveName, releaseBaseUrl) {
8
+ export async function verifyChecksum(
9
+ archivePath,
10
+ archiveName,
11
+ releaseBaseUrl,
12
+ { env = process.env, fetchFn = fetch } = {},
13
+ ) {
14
+ if (env.SEM_SKIP_CHECKSUM === '1') {
15
+ console.warn('Skipping checksum verification because SEM_SKIP_CHECKSUM=1.');
16
+ return;
17
+ }
18
+
9
19
  const checksumsUrl = `${releaseBaseUrl}/checksums.txt`;
10
20
 
11
- const response = await fetch(checksumsUrl, {
12
- headers: { 'user-agent': '@ataraxy-labs/sem npm installer' },
13
- redirect: 'follow',
14
- });
21
+ let response;
22
+ try {
23
+ response = await fetchFn(checksumsUrl, {
24
+ headers: { 'user-agent': '@ataraxy-labs/sem npm installer' },
25
+ redirect: 'follow',
26
+ });
27
+ } catch (error) {
28
+ throw new Error(
29
+ `Failed to fetch checksum metadata from ${checksumsUrl}: ${error.message}`,
30
+ );
31
+ }
15
32
 
16
33
  if (!response.ok) {
17
- console.warn(
18
- `Could not fetch checksums (${response.status}), skipping verification.`,
34
+ throw new Error(
35
+ `Failed to fetch checksum metadata from ${checksumsUrl}: ` +
36
+ `${response.status} ${response.statusText}`,
19
37
  );
20
- return;
21
38
  }
22
39
 
23
40
  const checksumsText = await response.text();
@@ -33,10 +50,9 @@ export async function verifyChecksum(archivePath, archiveName, releaseBaseUrl) {
33
50
  }
34
51
 
35
52
  if (!expectedHash) {
36
- console.warn(
37
- `No checksum found for ${archiveName} in checksums.txt, skipping verification.`,
53
+ throw new Error(
54
+ `No checksum found for ${archiveName} in checksum metadata from ${checksumsUrl}.`,
38
55
  );
39
- return;
40
56
  }
41
57
 
42
58
  const fileBuffer = await fs.readFile(archivePath);