semlint-cli 0.1.0 → 0.1.1

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
@@ -21,6 +21,12 @@ pnpm build
21
21
  semlint check
22
22
  ```
23
23
 
24
+ Scaffold a project config with automatic coding agent CLI detection:
25
+
26
+ ```bash
27
+ semlint init
28
+ ```
29
+
24
30
  If running from source:
25
31
 
26
32
  ```bash
@@ -38,6 +44,7 @@ pnpm check
38
44
  - `--fail-on <error|warn|never>`: failure threshold (default `error`)
39
45
  - `--batch`: run all selected rules in one backend call
40
46
  - `--debug`: enable debug logs to stderr
47
+ - `init --force`: overwrite an existing `semlint.json`
41
48
 
42
49
  Default diff behavior (without `--base`/`--head`) uses your local branch state:
43
50
 
@@ -92,6 +99,24 @@ Unknown fields are ignored.
92
99
  }
93
100
  ```
94
101
 
102
+ ## Config scaffolding and auto-detection
103
+
104
+ Run:
105
+
106
+ ```bash
107
+ semlint init
108
+ ```
109
+
110
+ This creates `./semlint.json` and auto-detects installed coding agent CLIs in this priority order:
111
+
112
+ 1. `cursor` -> backend `cursor-cli`
113
+ 2. `claude` -> backend `claude-code`
114
+ 3. `codex` -> backend `codex-cli`
115
+
116
+ If no known CLI is detected, Semlint falls back to `cursor-cli` + executable `cursor`.
117
+
118
+ Use `semlint init --force` to overwrite an existing config file.
119
+
95
120
  ## Rule files
96
121
 
97
122
  Rule JSON files are loaded from `rules/`.
package/dist/cli.js CHANGED
@@ -1,7 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ const init_1 = require("./init");
4
5
  const main_1 = require("./main");
6
+ const HELP_TEXT = [
7
+ "Usage:",
8
+ " semlint check [--backend <name>] [--model <name>] [--config <path>] [--format <text|json>] [--base <ref>] [--head <ref>] [--fail-on <error|warn|never>] [--batch] [--debug]",
9
+ " semlint init [--force]",
10
+ " semlint --help",
11
+ "",
12
+ "Commands:",
13
+ " check Run semantic lint rules against your git diff",
14
+ " init Create semlint.json with auto-detected agent backend",
15
+ "",
16
+ "Options:",
17
+ " -h, --help Show this help text"
18
+ ].join("\n");
19
+ class HelpRequestedError extends Error {
20
+ constructor() {
21
+ super(HELP_TEXT);
22
+ }
23
+ }
5
24
  const FLAGS_WITH_VALUES = new Set([
6
25
  "--backend",
7
26
  "--model",
@@ -18,9 +37,26 @@ function isFailOn(value) {
18
37
  return value === "error" || value === "warn" || value === "never";
19
38
  }
20
39
  function parseArgs(argv) {
40
+ if (argv.length === 0 || argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
41
+ throw new HelpRequestedError();
42
+ }
21
43
  const [command, ...rest] = argv;
22
- if (!command || command !== "check") {
23
- throw new Error("Usage: semlint check [--backend <name>] [--model <name>] [--config <path>] [--format <text|json>] [--base <ref>] [--head <ref>] [--fail-on <error|warn|never>] [--batch] [--debug]");
44
+ if (!command || (command !== "check" && command !== "init")) {
45
+ throw new Error(HELP_TEXT);
46
+ }
47
+ if (command === "init") {
48
+ const options = {
49
+ command: "init",
50
+ debug: false
51
+ };
52
+ for (const token of rest) {
53
+ if (token === "--force") {
54
+ options.force = true;
55
+ continue;
56
+ }
57
+ throw new Error(`Unknown flag for init: ${token}`);
58
+ }
59
+ return options;
24
60
  }
25
61
  const options = {
26
62
  command: "check",
@@ -81,13 +117,13 @@ function parseArgs(argv) {
81
117
  async function main() {
82
118
  try {
83
119
  const options = parseArgs(process.argv.slice(2));
84
- const exitCode = await (0, main_1.runSemlint)(options);
120
+ const exitCode = options.command === "init" ? (0, init_1.scaffoldConfig)(options.force) : await (0, main_1.runSemlint)(options);
85
121
  process.exitCode = exitCode;
86
122
  }
87
123
  catch (error) {
88
124
  const message = error instanceof Error ? error.message : String(error);
89
125
  process.stderr.write(`${message}\n`);
90
- process.exitCode = 2;
126
+ process.exitCode = error instanceof HelpRequestedError ? 0 : 2;
91
127
  }
92
128
  }
93
129
  void main();
package/dist/init.js ADDED
@@ -0,0 +1,78 @@
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.scaffoldConfig = scaffoldConfig;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const node_child_process_1 = require("node:child_process");
10
+ function commandExists(command) {
11
+ const result = (0, node_child_process_1.spawnSync)(command, ["--version"], {
12
+ stdio: "ignore"
13
+ });
14
+ return result.status === 0 || result.status === 1;
15
+ }
16
+ function detectBackend() {
17
+ const candidates = [
18
+ {
19
+ executable: "cursor",
20
+ backend: "cursor-cli",
21
+ reason: "detected Cursor CLI"
22
+ },
23
+ {
24
+ executable: "claude",
25
+ backend: "claude-code",
26
+ reason: "detected Claude Code CLI"
27
+ },
28
+ {
29
+ executable: "codex",
30
+ backend: "codex-cli",
31
+ reason: "detected Codex CLI"
32
+ }
33
+ ];
34
+ for (const candidate of candidates) {
35
+ if (commandExists(candidate.executable)) {
36
+ return candidate;
37
+ }
38
+ }
39
+ return {
40
+ backend: "cursor-cli",
41
+ executable: "cursor",
42
+ reason: "no known agent CLI detected, using default Cursor setup"
43
+ };
44
+ }
45
+ function scaffoldConfig(force = false) {
46
+ const targetPath = node_path_1.default.join(process.cwd(), "semlint.json");
47
+ if (node_fs_1.default.existsSync(targetPath) && !force) {
48
+ process.stderr.write(`Refusing to overwrite existing ${targetPath}. Re-run with "semlint init --force" to replace it.\n`);
49
+ return 2;
50
+ }
51
+ const detected = detectBackend();
52
+ const scaffold = {
53
+ backend: detected.backend,
54
+ model: "auto",
55
+ budgets: {
56
+ timeout_ms: 120000
57
+ },
58
+ output: {
59
+ format: "text"
60
+ },
61
+ execution: {
62
+ batch: false
63
+ },
64
+ rules: {
65
+ disable: [],
66
+ severity_overrides: {}
67
+ },
68
+ backends: {
69
+ [detected.backend]: {
70
+ executable: detected.executable
71
+ }
72
+ }
73
+ };
74
+ node_fs_1.default.writeFileSync(targetPath, `${JSON.stringify(scaffold, null, 2)}\n`, "utf8");
75
+ process.stdout.write(`Created ${targetPath}\n`);
76
+ process.stdout.write(`Backend setup: ${detected.backend} (${detected.reason})\n`);
77
+ return 0;
78
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "semlint-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Semantic lint CLI — runs LLM-backed rules on your git diff and returns CI-friendly exit codes",
5
5
  "type": "commonjs",
6
6
  "main": "dist/main.js",
7
7
  "bin": {
8
- "semlint": "dist/cli.js"
8
+ "semlint-cli": "dist/cli.js"
9
9
  },
10
10
  "files": [
11
11
  "dist",