@ikunin/sprintpilot 1.0.1 → 1.0.2

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
@@ -33,7 +33,7 @@ Sprintpilot is an autonomous delivery addon **compatible with [BMad Method](http
33
33
 
34
34
  BMad Method provides a structured development workflow with 50+ skills and agent personas. But using it manually means invoking each skill one at a time, navigating menus, making routine decisions, and handling git operations yourself. For a sprint with 10 stories across 3 epics, that's dozens of manual steps, context switches, and session restarts.
35
35
 
36
- ## The Solution: Sprintpilot
36
+ ## The Solution: Sprint Autopilot
37
37
 
38
38
  ```
39
39
  /sprint-autopilot-on
@@ -148,7 +148,7 @@ Output files (`_bmad-output/codebase-analysis/`):
148
148
  | `concerns-analysis.md` | TODOs/FIXMEs, security issues, dead code, deprecated patterns, error handling gaps |
149
149
  | `integrations-analysis.md` | External APIs, databases, message queues, cloud services, env vars |
150
150
 
151
- Scanned file types: TypeScript, JavaScript, Python, Java, Go, Rust, Ruby, C#, SQL, PL/SQL (`.sps`, `.spb`), XML, Shell.
151
+ Scanned file types: TypeScript, JavaScript, Python, Java, Go, Rust, Ruby, C, C++, C#, SQL, PL/SQL (`.sps`, `.spb`), XML, Shell.
152
152
 
153
153
  **`/sprintpilot-assess`** — 3 parallel agents produce actionable findings:
154
154
  - Dependency Auditor (CVEs, outdated packages, upgrade paths)
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  function parseArgs(argv, { booleanFlags = [], positionalActions = null } = {}) {
4
2
  const opts = {};
5
3
  const positional = [];
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  const { run, tryRun } = require('./spawn');
4
2
 
5
3
  async function git(args, opts = {}) {
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  const https = require('node:https');
4
2
  const http = require('node:http');
5
3
  const { URL } = require('node:url');
@@ -22,7 +20,12 @@ function postJson(urlStr, body, { headers = {}, timeoutMs = 15_000 } = {}) {
22
20
  // can fire on the size-cap abort path (we call req.destroy(err)). Without
23
21
  // this, the observed error message becomes non-deterministic.
24
22
  let settled = false;
25
- const done = (fn, val) => { if (!settled) { settled = true; fn(val); } };
23
+ const done = (fn, val) => {
24
+ if (!settled) {
25
+ settled = true;
26
+ fn(val);
27
+ }
28
+ };
26
29
  const ok = (val) => done(resolve, val);
27
30
  const fail = (err) => done(reject, err);
28
31
 
@@ -78,11 +81,15 @@ function postJson(urlStr, body, { headers = {}, timeoutMs = 15_000 } = {}) {
78
81
  if (aborted) return; // error path handles it
79
82
  const text = Buffer.concat(chunks).toString('utf8');
80
83
  let json = null;
81
- try { json = JSON.parse(text); } catch { /* non-json */ }
84
+ try {
85
+ json = JSON.parse(text);
86
+ } catch {
87
+ /* non-json */
88
+ }
82
89
  ok({ statusCode: res.statusCode, body: text, json });
83
90
  });
84
91
  res.on('error', fail);
85
- }
92
+ },
86
93
  );
87
94
  req.on('timeout', () => {
88
95
  req.destroy(new Error(`HTTP timeout after ${timeoutMs}ms`));
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  function out(msg) {
4
2
  process.stdout.write(String(msg));
5
3
  if (!String(msg).endsWith('\n')) process.stdout.write('\n');
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  const fs = require('node:fs');
4
2
 
5
3
  // Keyword-based fuzzy matches (high false-positive rate, but useful as a
@@ -10,20 +8,20 @@ const SECRET_KEYWORD = /API_KEY|SECRET|TOKEN|PASSWORD|aws_access|private_key/i;
10
8
  // secrets actually look like. Matching here is much less noisy than the
11
9
  // keyword list above.
12
10
  const SECRET_FORMATS = [
13
- /\bAKIA[0-9A-Z]{16}\b/, // AWS Access Key ID
14
- /\bASIA[0-9A-Z]{16}\b/, // AWS temporary Access Key ID
15
- /\bghp_[A-Za-z0-9]{30,}\b/, // GitHub personal access token
16
- /\bgho_[A-Za-z0-9]{30,}\b/, // GitHub OAuth token
17
- /\bghu_[A-Za-z0-9]{30,}\b/, // GitHub user-to-server token
18
- /\bghs_[A-Za-z0-9]{30,}\b/, // GitHub server-to-server token
19
- /\bghr_[A-Za-z0-9]{30,}\b/, // GitHub refresh token
20
- /\bgithub_pat_[A-Za-z0-9_]{20,}\b/, // GitHub fine-grained PAT
21
- /\bsk-[A-Za-z0-9_-]{20,}\b/, // OpenAI / Anthropic-like
22
- /\bsk_live_[A-Za-z0-9]{20,}\b/, // Stripe live secret
23
- /\bsk_test_[A-Za-z0-9]{20,}\b/, // Stripe test secret
24
- /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/, // Slack tokens
25
- /\bAIza[0-9A-Za-z_-]{35,99}\b/, // Google API key (standard is 39 chars = AIza + 35)
26
- /-----BEGIN [A-Z ]*PRIVATE KEY-----/, // PEM private key header
11
+ /\bAKIA[0-9A-Z]{16}\b/, // AWS Access Key ID
12
+ /\bASIA[0-9A-Z]{16}\b/, // AWS temporary Access Key ID
13
+ /\bghp_[A-Za-z0-9]{30,}\b/, // GitHub personal access token
14
+ /\bgho_[A-Za-z0-9]{30,}\b/, // GitHub OAuth token
15
+ /\bghu_[A-Za-z0-9]{30,}\b/, // GitHub user-to-server token
16
+ /\bghs_[A-Za-z0-9]{30,}\b/, // GitHub server-to-server token
17
+ /\bghr_[A-Za-z0-9]{30,}\b/, // GitHub refresh token
18
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/, // GitHub fine-grained PAT
19
+ /\bsk-[A-Za-z0-9_-]{20,}\b/, // OpenAI / Anthropic-like
20
+ /\bsk_live_[A-Za-z0-9]{20,}\b/, // Stripe live secret
21
+ /\bsk_test_[A-Za-z0-9]{20,}\b/, // Stripe test secret
22
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/, // Slack tokens
23
+ /\bAIza[0-9A-Za-z_-]{35,99}\b/, // Google API key (standard is 39 chars = AIza + 35)
24
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/, // PEM private key header
27
25
  ];
28
26
 
29
27
  // Deprecated: callers should use `matchesSecret(line)` which applies both
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  const child = require('node:child_process');
4
2
 
5
3
  function run(file, args, { cwd, timeoutMs = 30_000, input, env } = {}) {
@@ -17,7 +15,7 @@ function run(file, args, { cwd, timeoutMs = 30_000, input, env } = {}) {
17
15
  (err, stdout, stderr) => {
18
16
  if (err) {
19
17
  const e = new Error(
20
- `${file} ${args.join(' ')} failed with code ${err.code ?? err.signal ?? 'unknown'}`
18
+ `${file} ${args.join(' ')} failed with code ${err.code ?? err.signal ?? 'unknown'}`,
21
19
  );
22
20
  e.exitCode = typeof err.code === 'number' ? err.code : 1;
23
21
  e.signal = err.signal || null;
@@ -27,16 +25,26 @@ function run(file, args, { cwd, timeoutMs = 30_000, input, env } = {}) {
27
25
  return reject(e);
28
26
  }
29
27
  resolve({ stdout: String(stdout || ''), stderr: String(stderr || ''), exitCode: 0 });
30
- }
28
+ },
31
29
  );
32
30
  // Rejecting on spawn-time errors (ENOENT etc) ensures the caller sees a
33
31
  // rejected Promise rather than hanging, even if `proc.stdin` was never
34
32
  // attached. Without this, a missing binary can crash via `proc.stdin.write`.
35
33
  proc.on('error', reject);
36
34
  if (input !== undefined && proc.stdin) {
37
- proc.stdin.on('error', () => { /* ignore EPIPE etc */ });
38
- try { proc.stdin.write(input); } catch { /* ignore */ }
39
- try { proc.stdin.end(); } catch { /* ignore */ }
35
+ proc.stdin.on('error', () => {
36
+ /* ignore EPIPE etc */
37
+ });
38
+ try {
39
+ proc.stdin.write(input);
40
+ } catch {
41
+ /* ignore */
42
+ }
43
+ try {
44
+ proc.stdin.end();
45
+ } catch {
46
+ /* ignore */
47
+ }
40
48
  }
41
49
  });
42
50
  }
@@ -60,7 +68,12 @@ function runInherit(file, args, { cwd, env, input } = {}) {
60
68
  proc.on('error', reject);
61
69
  proc.on('close', (code) => resolve({ exitCode: code ?? 0 }));
62
70
  if (input !== undefined && proc.stdin) {
63
- try { proc.stdin.write(input); proc.stdin.end(); } catch { /* ignore */ }
71
+ try {
72
+ proc.stdin.write(input);
73
+ proc.stdin.end();
74
+ } catch {
75
+ /* ignore */
76
+ }
64
77
  }
65
78
  });
66
79
  }
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  function splitLines(text) {
4
2
  if (!text) return [];
5
3
  const trimmedTrailing = text.replace(/\r?\n$/, '');
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  // Narrow YAML helper covering the addon-owned shape:
4
2
  // key: value
5
3
  // stories:
@@ -8,7 +6,7 @@
8
6
  // field2: value
9
7
  // No deep nesting, no anchors, no flow sequences beyond [a,b,c] literal we pass through.
10
8
 
11
- const SPECIAL_CHARS = /[:{}\[\],&*#?|<>=!%@`\n]|^-|^\s|\s$/;
9
+ const SPECIAL_CHARS = /[:{}[\],&*#?|<>=!%@`\n]|^-|^\s|\s$/;
12
10
 
13
11
  // YAML 1.1 reserved literals that parsers interpret as booleans/null when
14
12
  // unquoted (e.g. "no" -> false). Must be quoted to round-trip as strings.
@@ -81,8 +79,14 @@ function replaceStoryBlock(existing, storyKey, newBlock) {
81
79
  // header is part of this block. Blank lines inside the block count
82
80
  // as continuations and are also consumed.
83
81
  while (i < lines.length) {
84
- if (lines[i].length === 0) { i++; continue; }
85
- if (indentOf(lines[i]) > headerIndent) { i++; continue; }
82
+ if (lines[i].length === 0) {
83
+ i++;
84
+ continue;
85
+ }
86
+ if (indentOf(lines[i]) > headerIndent) {
87
+ i++;
88
+ continue;
89
+ }
86
90
  break;
87
91
  }
88
92
  // Strip any trailing blanks we may have consumed past the block.
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 1.0.1
3
+ version: 1.0.2
4
4
  description: Sprintpilot — autopilot and multi-agent addon for BMad Method (git workflow, parallel agents, autonomous story execution)
5
5
  bmad_compatibility: ">=6.2.0"
6
6
  modules:
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
2
 
4
3
  const { parseArgs } = require('../lib/runtime/args');
5
4
  const { tryRun, run } = require('../lib/runtime/spawn');
@@ -9,7 +8,9 @@ const { postJson } = require('../lib/runtime/http');
9
8
  const log = require('../lib/runtime/log');
10
9
 
11
10
  function help() {
12
- log.out("Usage: create-pr.js --platform <github|gitlab|bitbucket|gitea|git_only> --branch <name> --base <branch> --title 'title' --body 'body' [--base-url <url>]");
11
+ log.out(
12
+ "Usage: create-pr.js --platform <github|gitlab|bitbucket|gitea|git_only> --branch <name> --base <branch> --title 'title' --body 'body' [--base-url <url>]",
13
+ );
13
14
  }
14
15
 
15
16
  async function hasCli(name) {
@@ -80,12 +81,18 @@ function redactAuth(text) {
80
81
  return String(text)
81
82
  .replace(/("?authorization"?\s*:\s*")[^"]*(")/gi, '$1[REDACTED]$2')
82
83
  .replace(/(bearer\s+)\S+/gi, '$1[REDACTED]')
83
- .replace(/("?(?:token|access_token|api_key|private_token)"?\s*[:=]\s*")[^"]*(")/gi, '$1[REDACTED]$2');
84
+ .replace(
85
+ /("?(?:token|access_token|api_key|private_token)"?\s*[:=]\s*")[^"]*(")/gi,
86
+ '$1[REDACTED]$2',
87
+ );
84
88
  }
85
89
 
86
90
  async function main() {
87
91
  const { opts } = parseArgs(process.argv.slice(2), { booleanFlags: ['dry-run'] });
88
- if (opts.help) { help(); process.exit(0); }
92
+ if (opts.help) {
93
+ help();
94
+ process.exit(0);
95
+ }
89
96
 
90
97
  const platform = opts.platform;
91
98
  const branch = opts.branch;
@@ -130,13 +137,11 @@ async function main() {
130
137
  log.out('SKIPPED');
131
138
  process.exit(2);
132
139
  }
133
- const r = await tryRun('gh', [
134
- 'pr', 'create',
135
- '--base', baseBranch,
136
- '--head', branch,
137
- '--title', title,
138
- '--body', body,
139
- ], { timeoutMs: 60_000 });
140
+ const r = await tryRun(
141
+ 'gh',
142
+ ['pr', 'create', '--base', baseBranch, '--head', branch, '--title', title, '--body', body],
143
+ { timeoutMs: 60_000 },
144
+ );
140
145
  const combined = `${r.stdout}${r.stderr}`;
141
146
  if (r.exitCode !== 0) {
142
147
  log.error(`gh pr create failed: ${combined.trim()}`);
@@ -152,15 +157,24 @@ async function main() {
152
157
  log.out('SKIPPED');
153
158
  process.exit(2);
154
159
  }
155
- const r = await tryRun('glab', [
156
- 'mr', 'create',
157
- '--source-branch', branch,
158
- '--target-branch', baseBranch,
159
- '--title', title,
160
- '--description', body,
161
- '--remove-source-branch',
162
- '--yes',
163
- ], { timeoutMs: 60_000 });
160
+ const r = await tryRun(
161
+ 'glab',
162
+ [
163
+ 'mr',
164
+ 'create',
165
+ '--source-branch',
166
+ branch,
167
+ '--target-branch',
168
+ baseBranch,
169
+ '--title',
170
+ title,
171
+ '--description',
172
+ body,
173
+ '--remove-source-branch',
174
+ '--yes',
175
+ ],
176
+ { timeoutMs: 60_000 },
177
+ );
164
178
  const combined = `${r.stdout}${r.stderr}`;
165
179
  if (r.exitCode !== 0) {
166
180
  log.error(`glab mr create failed: ${combined.trim()}`);
@@ -173,13 +187,22 @@ async function main() {
173
187
 
174
188
  if (platform === 'bitbucket') {
175
189
  if (await hasCli('bb')) {
176
- const r = await tryRun('bb', [
177
- 'pr', 'create',
178
- '--source', branch,
179
- '--destination', baseBranch,
180
- '--title', title,
181
- '--description', body,
182
- ], { timeoutMs: 60_000 });
190
+ const r = await tryRun(
191
+ 'bb',
192
+ [
193
+ 'pr',
194
+ 'create',
195
+ '--source',
196
+ branch,
197
+ '--destination',
198
+ baseBranch,
199
+ '--title',
200
+ title,
201
+ '--description',
202
+ body,
203
+ ],
204
+ { timeoutMs: 60_000 },
205
+ );
183
206
  const combined = `${r.stdout}${r.stderr}`;
184
207
  if (r.exitCode !== 0) {
185
208
  log.error(`bb pr create failed: ${combined.trim()}`);
@@ -200,11 +223,14 @@ async function main() {
200
223
  destination: { branch: { name: baseBranch } },
201
224
  description: body,
202
225
  },
203
- { headers: { Authorization: `Bearer ${process.env.BITBUCKET_TOKEN}` } }
226
+ { headers: { Authorization: `Bearer ${process.env.BITBUCKET_TOKEN}` } },
204
227
  );
205
228
  if (res.statusCode === 201) {
206
229
  const href = res.json?.links?.html?.href;
207
- if (href) { log.out(href); return; }
230
+ if (href) {
231
+ log.out(href);
232
+ return;
233
+ }
208
234
  log.out('CREATED (URL not extracted from response)');
209
235
  return;
210
236
  }
@@ -223,13 +249,22 @@ async function main() {
223
249
 
224
250
  if (platform === 'gitea') {
225
251
  if (await hasCli('tea')) {
226
- const r = await tryRun('tea', [
227
- 'pr', 'create',
228
- '--base', baseBranch,
229
- '--head', branch,
230
- '--title', title,
231
- '--description', body,
232
- ], { timeoutMs: 60_000 });
252
+ const r = await tryRun(
253
+ 'tea',
254
+ [
255
+ 'pr',
256
+ 'create',
257
+ '--base',
258
+ baseBranch,
259
+ '--head',
260
+ branch,
261
+ '--title',
262
+ title,
263
+ '--description',
264
+ body,
265
+ ],
266
+ { timeoutMs: 60_000 },
267
+ );
233
268
  const combined = `${r.stdout}${r.stderr}`;
234
269
  if (r.exitCode !== 0) {
235
270
  log.error(`tea pr create failed: ${combined.trim()}`);
@@ -245,11 +280,14 @@ async function main() {
245
280
  const res = await postJson(
246
281
  `${baseUrl.replace(/\/+$/, '')}/api/v1/repos/${ownerRepo}/pulls`,
247
282
  { base: baseBranch, head: branch, title, body },
248
- { headers: { Authorization: `token ${process.env.GITEA_TOKEN}` } }
283
+ { headers: { Authorization: `token ${process.env.GITEA_TOKEN}` } },
249
284
  );
250
285
  if (res.statusCode === 201) {
251
286
  const href = res.json?.html_url;
252
- if (href) { log.out(href); return; }
287
+ if (href) {
288
+ log.out(href);
289
+ return;
290
+ }
253
291
  log.out('CREATED (URL not extracted from response)');
254
292
  return;
255
293
  }
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
2
 
4
3
  const { parseArgs } = require('../lib/runtime/args');
5
4
  const { tryRun } = require('../lib/runtime/spawn');
@@ -17,7 +16,10 @@ async function hasCli(name) {
17
16
 
18
17
  async function main() {
19
18
  const { opts } = parseArgs(process.argv.slice(2));
20
- if (opts.help) { help(); process.exit(0); }
19
+ if (opts.help) {
20
+ help();
21
+ process.exit(0);
22
+ }
21
23
  const provider = opts.provider || 'auto';
22
24
 
23
25
  if (provider !== 'auto') {
@@ -45,16 +47,39 @@ async function main() {
45
47
 
46
48
  const remote = (await tryGitStdout(['remote', 'get-url', 'origin'])) || '';
47
49
 
48
- if (/github\.com[:/]/i.test(remote)) { log.out('github'); return; }
49
- if (/gitlab\./i.test(remote)) { log.out('gitlab'); return; }
50
- if (/bitbucket\.org[:/]/i.test(remote)) { log.out('bitbucket'); return; }
50
+ if (/github\.com[:/]/i.test(remote)) {
51
+ log.out('github');
52
+ return;
53
+ }
54
+ if (/gitlab\./i.test(remote)) {
55
+ log.out('gitlab');
56
+ return;
57
+ }
58
+ if (/bitbucket\.org[:/]/i.test(remote)) {
59
+ log.out('bitbucket');
60
+ return;
61
+ }
51
62
 
52
- if (hasTea) { log.out('gitea'); return; }
53
- if (hasGh) { log.out('github'); return; }
54
- if (hasGlab) { log.out('gitlab'); return; }
55
- if (hasBb) { log.out('bitbucket'); return; }
63
+ if (hasTea) {
64
+ log.out('gitea');
65
+ return;
66
+ }
67
+ if (hasGh) {
68
+ log.out('github');
69
+ return;
70
+ }
71
+ if (hasGlab) {
72
+ log.out('gitlab');
73
+ return;
74
+ }
75
+ if (hasBb) {
76
+ log.out('bitbucket');
77
+ return;
78
+ }
56
79
 
57
- log.err("WARN: no platform CLI found (gh, glab, bb, tea) and remote URL didn't match known platforms");
80
+ log.err(
81
+ "WARN: no platform CLI found (gh, glab, bb, tea) and remote URL didn't match known platforms",
82
+ );
58
83
  log.out('git_only');
59
84
  }
60
85
 
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
2
 
4
3
  const fs = require('node:fs');
5
4
  const path = require('node:path');
@@ -10,12 +9,17 @@ const { readStoryField } = require('../lib/runtime/yaml-lite');
10
9
  const log = require('../lib/runtime/log');
11
10
 
12
11
  function help() {
13
- log.out('Usage: health-check.js [--worktrees-dir path] [--base-branch main] [--status-file path]');
12
+ log.out(
13
+ 'Usage: health-check.js [--worktrees-dir path] [--base-branch main] [--status-file path]',
14
+ );
14
15
  }
15
16
 
16
17
  async function main() {
17
18
  const { opts } = parseArgs(process.argv.slice(2));
18
- if (opts.help) { help(); process.exit(0); }
19
+ if (opts.help) {
20
+ help();
21
+ process.exit(0);
22
+ }
19
23
  const worktreesDir = opts['worktrees-dir'] || '.worktrees';
20
24
  const baseBranch = opts['base-branch'] || 'main';
21
25
  const statusFile = opts['status-file'] || '';
@@ -34,13 +38,18 @@ async function main() {
34
38
  log.err("WARN: no 'origin' remote configured — commit comparison may be inaccurate");
35
39
  }
36
40
 
37
- let total = 0, cleanDone = 0, committed = 0, stale = 0, dirty = 0, orphan = 0;
41
+ let total = 0,
42
+ cleanDone = 0,
43
+ committed = 0,
44
+ stale = 0,
45
+ dirty = 0,
46
+ orphan = 0;
38
47
 
39
- const statusText = statusFile && fs.existsSync(statusFile)
40
- ? fs.readFileSync(statusFile, 'utf8')
41
- : null;
48
+ const statusText =
49
+ statusFile && fs.existsSync(statusFile) ? fs.readFileSync(statusFile, 'utf8') : null;
42
50
 
43
- const entries = fs.readdirSync(worktreesDir, { withFileTypes: true })
51
+ const entries = fs
52
+ .readdirSync(worktreesDir, { withFileTypes: true })
44
53
  .filter((e) => e.isDirectory());
45
54
 
46
55
  for (const entry of entries) {
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
2
 
4
3
  const fs = require('node:fs');
5
4
  const path = require('node:path');
@@ -54,7 +53,9 @@ function localBin(name) {
54
53
  try {
55
54
  const stat = fs.statSync(p);
56
55
  if (stat.isFile()) return p;
57
- } catch { /* ignore */ }
56
+ } catch {
57
+ /* ignore */
58
+ }
58
59
  return null;
59
60
  }
60
61
 
@@ -72,7 +73,8 @@ async function lintLanguage(lang, files) {
72
73
  if (lang === 'python') {
73
74
  if (await hasCli('ruff')) return runLinter('ruff', 'ruff', ['check'], files);
74
75
  if (await hasCli('flake8')) return runLinter('flake8', 'flake8', [], files);
75
- if (await hasCli('pylint')) return runLinter('pylint', 'pylint', ['--output-format=text'], files);
76
+ if (await hasCli('pylint'))
77
+ return runLinter('pylint', 'pylint', ['--output-format=text'], files);
76
78
  return null;
77
79
  }
78
80
  if (lang === 'js-ts') {
@@ -83,15 +85,18 @@ async function lintLanguage(lang, files) {
83
85
  return null;
84
86
  }
85
87
  if (lang === 'rust') {
86
- if (await hasCli('cargo')) return runLinter('cargo-clippy', 'cargo', ['clippy', '--message-format=short'], []);
88
+ if (await hasCli('cargo'))
89
+ return runLinter('cargo-clippy', 'cargo', ['clippy', '--message-format=short'], []);
87
90
  return null;
88
91
  }
89
92
  if (lang === 'go') {
90
- if (await hasCli('golangci-lint')) return runLinter('golangci-lint', 'golangci-lint', ['run'], []);
93
+ if (await hasCli('golangci-lint'))
94
+ return runLinter('golangci-lint', 'golangci-lint', ['run'], []);
91
95
  return null;
92
96
  }
93
97
  if (lang === 'ruby') {
94
- if (await hasCli('rubocop')) return runLinter('rubocop', 'rubocop', ['--format', 'simple'], files);
98
+ if (await hasCli('rubocop'))
99
+ return runLinter('rubocop', 'rubocop', ['--format', 'simple'], files);
95
100
  return null;
96
101
  }
97
102
  if (lang === 'java') {
@@ -108,25 +113,35 @@ async function lintLanguage(lang, files) {
108
113
  return null;
109
114
  }
110
115
  if (lang === 'c') {
111
- if (await hasCli('cppcheck')) return runLinter('cppcheck', 'cppcheck', ['--enable=warning,style'], files);
116
+ if (await hasCli('cppcheck'))
117
+ return runLinter('cppcheck', 'cppcheck', ['--enable=warning,style'], files);
112
118
  if (await hasCli('clang-tidy')) return runLinter('clang-tidy', 'clang-tidy', [], files);
113
119
  return null;
114
120
  }
115
121
  if (lang === 'cpp') {
116
- if (await hasCli('cppcheck')) return runLinter('cppcheck', 'cppcheck', ['--enable=warning,style', '--language=c++'], files);
122
+ if (await hasCli('cppcheck'))
123
+ return runLinter('cppcheck', 'cppcheck', ['--enable=warning,style', '--language=c++'], files);
117
124
  if (await hasCli('clang-tidy')) return runLinter('clang-tidy', 'clang-tidy', [], files);
118
125
  return null;
119
126
  }
120
127
  if (lang === 'csharp') {
121
- if (await hasCli('dotnet')) return runLinter('dotnet-format', 'dotnet', ['format', '--verify-no-changes', '--diagnostics'], []);
128
+ if (await hasCli('dotnet'))
129
+ return runLinter(
130
+ 'dotnet-format',
131
+ 'dotnet',
132
+ ['format', '--verify-no-changes', '--diagnostics'],
133
+ [],
134
+ );
122
135
  return null;
123
136
  }
124
137
  if (lang === 'swift') {
125
- if (await hasCli('swiftlint')) return runLinter('swiftlint', 'swiftlint', ['lint', '--quiet'], files);
138
+ if (await hasCli('swiftlint'))
139
+ return runLinter('swiftlint', 'swiftlint', ['lint', '--quiet'], files);
126
140
  return null;
127
141
  }
128
142
  if (lang === 'sql') {
129
- if (await hasCli('sqlfluff')) return runLinter('sqlfluff', 'sqlfluff', ['lint', '--dialect', 'oracle'], files);
143
+ if (await hasCli('sqlfluff'))
144
+ return runLinter('sqlfluff', 'sqlfluff', ['lint', '--dialect', 'oracle'], files);
130
145
  return null;
131
146
  }
132
147
  if (lang === 'kotlin') {
@@ -135,7 +150,8 @@ async function lintLanguage(lang, files) {
135
150
  return null;
136
151
  }
137
152
  if (lang === 'php') {
138
- if (await hasCli('phpstan')) return runLinter('phpstan', 'phpstan', ['analyse', '--no-progress'], files);
153
+ if (await hasCli('phpstan'))
154
+ return runLinter('phpstan', 'phpstan', ['analyse', '--no-progress'], files);
139
155
  if (await hasCli('phpcs')) return runLinter('phpcs', 'phpcs', [], files);
140
156
  return null;
141
157
  }
@@ -172,7 +188,10 @@ async function runOverride(name, files) {
172
188
 
173
189
  async function main() {
174
190
  const { opts } = parseArgs(process.argv.slice(2));
175
- if (opts.help) { help(); process.exit(0); }
191
+ if (opts.help) {
192
+ help();
193
+ process.exit(0);
194
+ }
176
195
 
177
196
  const limit = parseInt(opts.limit || '100', 10);
178
197
  const outputFile = opts['output-file'] || '';
@@ -180,9 +199,9 @@ async function main() {
180
199
 
181
200
  const modified = await tryGitStdout(['diff', '--name-only', 'HEAD']);
182
201
  const untracked = await tryGitStdout(['ls-files', '--others', '--exclude-standard']);
183
- const all = Array.from(
184
- new Set([...splitLines(modified || ''), ...splitLines(untracked || '')])
185
- ).filter(Boolean).sort();
202
+ const all = Array.from(new Set([...splitLines(modified || ''), ...splitLines(untracked || '')]))
203
+ .filter(Boolean)
204
+ .sort();
186
205
 
187
206
  if (all.length === 0) {
188
207
  log.out('No changed files to lint');