itworksbut 0.1.1 → 0.3.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
@@ -4,17 +4,20 @@ ItWorksBut is a Node.js CI tool for static checks in JavaScript, Node.js, web, T
4
4
 
5
5
  It focuses on common "it works, but..." risks often found in AI-generated or rushed prototypes: committed env files, missing lockfiles, weak CI, unsafe web APIs, broad desktop permissions, and similar issues.
6
6
 
7
- It only reads files and reports findings. It does not call external APIs, does not send telemetry, and does not modify the scanned project.
7
+ For every finding, ItWorksBut gives you a copy-ready fix prompt you can paste into your coding agent. It does not just say what is wrong; it tells your AI exactly what to inspect, what to change, and what not to leak.
8
+
9
+ It only reads files and reports findings. It does not call external APIs, does not send telemetry, and does not modify the scanned project unless you explicitly ask it to write `todo.md` with `--todo`.
8
10
 
9
11
  ## Installation
10
12
 
11
13
  ```sh
12
- npx itworksbut scan
14
+ npm install --global itworksbut
15
+ itworksbut scan
13
16
  ```
14
17
 
15
18
  ### Homebrew
16
19
 
17
- After the formula is committed to the tap, install with:
20
+ With the Homebrew tap:
18
21
 
19
22
  ```sh
20
23
  brew tap oliverjessner/tap
@@ -22,84 +25,77 @@ brew install itworksbut
22
25
  itworksbut scan
23
26
  ```
24
27
 
25
- One-line install:
26
-
27
- ```sh
28
- brew install oliverjessner/tap/itworksbut
29
- ```
30
-
31
- The `itworksbut` formula belongs in the Homebrew tap repo, not in this app repo:
32
-
33
- ```text
34
- https://github.com/oliverjessner/homebrew-tap
35
- └── Formula/
36
- └── itworksbut.rb
37
- ```
38
-
39
- This repository contains a one-command release script. It runs checks, publishes the npm package, generates the Homebrew formula, commits it to the tap, and pushes the tap:
28
+ ## Quick Start
40
29
 
41
30
  ```sh
42
- npm login
43
- npm run publish
31
+ itworksbut scan
44
32
  ```
45
33
 
46
- Do not run `npm publish` directly. The package blocks direct npm publishing so the Homebrew tap cannot be forgotten.
34
+ `scan` is intentionally the strict/default path: all checks are enabled, only heavy generated folders are skipped, and the default `fail-on` threshold is `low` so more issues fail early. Use `--config` only when you deliberately want to tune or suppress checks.
47
35
 
48
- Preview everything without publishing:
36
+ Common commands:
49
37
 
50
38
  ```sh
51
- npm run publish -- --dry-run
39
+ itworksbut scan --path .
40
+ itworksbut scan --fail-on high
41
+ itworksbut scan --json
42
+ itworksbut scan --sarif > itworksbut.sarif
43
+ itworksbut scan --todo
44
+ itworksbut scan --config itworksbut.config.json
45
+ itworksbut scan --verbose
46
+ itworksbut --version
52
47
  ```
53
48
 
54
- By default the script expects the tap checkout at `../homebrew-tap`. Override it when needed:
49
+ ## Options
55
50
 
56
- ```sh
57
- npm run publish -- --tap-path /path/to/homebrew-tap
51
+ ```text
52
+ itworksbut scan [options]
58
53
  ```
59
54
 
60
- Use `--no-push` when you want the script to commit the tap formula but leave the push to you.
55
+ - `--path <path>`: Scan a specific project directory. Defaults to the current directory.
56
+ - `--config <path>`: Use a custom config file. Defaults to `itworksbut.config.json` when present.
57
+ - `--fail-on <severity>`: Exit with code `1` when a finding at or above the severity exists. Levels: `critical`, `high`, `medium`, `low`, `info`. Default: `low`.
58
+ - `--json`: Print machine-readable JSON only. No banner, colors, spinner, table, or extra text.
59
+ - `--sarif`: Print SARIF JSON for GitHub Code Scanning. No banner, colors, spinner, table, or extra text.
60
+ - `--todo`: Write an AI-ready `todo.md` into the scanned project with prioritized findings, fix prompts, and acceptance criteria.
61
+ - `--verbose`: Include scanner warnings and extra metadata in console output.
62
+ - `--quiet`: Print only the summary.
63
+ - `--no-color`: Disable colored output.
64
+ - `--no-banner`: Disable the ASCII intro banner.
65
+ - `--version`, `-v`: Print the installed ItWorksBut version.
61
66
 
62
- ## Local Usage
67
+ Exit codes:
63
68
 
64
- ```sh
65
- node ./bin/itworksbut.js scan
66
- node ./bin/itworksbut.js scan --json
67
- node ./bin/itworksbut.js scan --sarif
68
- node ./bin/itworksbut.js scan --fail-on high
69
- node ./bin/itworksbut.js scan --config itworksbut.config.json
70
- node ./bin/itworksbut.js scan --path .
71
- node ./bin/itworksbut.js scan --verbose
72
- ```
69
+ - `0`: no findings at or above the configured `fail-on` severity
70
+ - `1`: at least one finding at or above the configured `fail-on` severity
71
+ - `2`: tool/runtime error
73
72
 
74
- `scan` is intentionally the strict/default path: all checks are enabled, only heavy generated folders are skipped, and the default `fail-on` threshold is `low` so more issues fail early. Use `--config` only when you deliberately want to tune or suppress checks.
73
+ Severity levels are `critical`, `high`, `medium`, `low`, and `info`.
75
74
 
76
75
  ## Terminal Experience
77
76
 
78
77
  Normal console output is intentionally more opinionated than the machine-readable reporters:
79
78
 
80
79
  ```sh
81
- node ./bin/itworksbut.js scan --theme toxic
80
+ itworksbut scan
82
81
  ```
83
82
 
84
83
  Console-only flags:
85
84
 
86
85
  - `--no-color`
87
86
  - `--no-banner`
88
- - `--no-spinner`
89
- - `--compact`
90
87
  - `--quiet`
91
88
  - `--verbose`
92
- - `--theme default|toxic|mono`
93
89
 
94
90
  In CI, spinners and banners are automatically disabled. With `--json` and `--sarif`, stdout contains only valid machine-readable output. The edgy tone applies only to the Console Reporter.
95
91
 
96
- Exit codes:
92
+ To create a fix list for a coding agent:
97
93
 
98
- - `0`: no findings at or above the configured `fail-on` severity
99
- - `1`: at least one finding at or above the configured `fail-on` severity
100
- - `2`: tool/runtime error
94
+ ```sh
95
+ itworksbut scan --todo
96
+ ```
101
97
 
102
- Severity levels are `critical`, `high`, `medium`, `low`, and `info`.
98
+ This writes `todo.md` to the scanned project. The file is ordered by severity and includes agent rules, exact fix prompts, locations, recommendations, and final verification checkboxes.
103
99
 
104
100
  ## GitHub Actions
105
101
 
@@ -121,13 +117,13 @@ jobs:
121
117
  node-version: 20
122
118
  cache: npm
123
119
  - run: npm ci
124
- - run: node ./bin/itworksbut.js scan --fail-on high
120
+ - run: npx itworksbut scan --fail-on high
125
121
  ```
126
122
 
127
123
  For GitHub Code Scanning-style output:
128
124
 
129
125
  ```sh
130
- node ./bin/itworksbut.js scan --sarif > itworksbut.sarif
126
+ itworksbut scan --sarif > itworksbut.sarif
131
127
  ```
132
128
 
133
129
  ## Configuration
@@ -169,20 +165,21 @@ release/**
169
165
 
170
166
  ## Example Output
171
167
 
172
- ```text
173
- CRITICAL It works, but your .env is tracked.
174
- Check: env.env-file-tracked
175
- File: .env
176
- Why: .env appears to be tracked by git. Secrets may be exposed.
177
- Fix: Remove it from git index, rotate secrets, and commit .env.example.
178
-
179
- HIGH It works, but your SQL query is one template string away from pain.
180
- Check: database.raw-sql-interpolation
181
- File: src/db.js:12
182
- Why: Possible SQL injection risk: raw SQL appears to be built with template string interpolation.
183
- Fix: Use parameterized queries, prepared statements, or ORM query builders instead of interpolating values into SQL strings.
168
+ ![screenshot of an example output](/assets/medium_problems.webp)
169
+ CRITICAL It works, but your .env is tracked.
170
+ Check: env.env-file-tracked
171
+ 📁 File: .env
172
+ 🤔 Why: .env appears to be tracked by git. Secrets may be exposed.
173
+ 🤖 prompt: You are a senior security engineer working in this repository. Fix the ItWorksBut finding env.env-file-tracked at .env. Treat this as a concrete finding. Problem: .env appears to be tracked by git. Secrets may be exposed. Required change: Remove tracked env files from git, add safe examples such as .env.example, and make sure any exposed credentials are treated as compromised. Do not print, log, or preserve raw secret values; use placeholders only. Keep existing behavior intact where possible, add or update focused tests when useful, and do not silence the check unless the underlying risk is actually fixed.
174
+
175
+ HIGH It works, but your SQL query is one template string away from pain.
176
+ Check: database.raw-sql-interpolation
177
+ 📁 File: src/db.js:12
178
+ 🤔 Why: Possible SQL injection risk: raw SQL appears to be built with template string interpolation.
179
+ 🤖 prompt: You are a senior security engineer working in this repository. Fix the ItWorksBut finding database.raw-sql-interpolation at src/db.js:12. This finding is heuristic, so inspect the code first and only change behavior when the risk is real. Problem: Possible SQL injection risk: raw SQL appears to be built with template string interpolation. Required change: Replace SQL string interpolation or concatenation with parameterized queries, prepared statements, or a safe ORM query builder. Keep existing behavior intact where possible, add or update focused tests when useful, and do not silence the check unless the underlying risk is actually fixed.
184
180
 
185
181
  SUMMARY
182
+
186
183
  - ship status: DO NOT SHIP
187
184
  - Fix the red stuff before production.
188
185
  - total findings: 2
@@ -193,6 +190,7 @@ SUMMARY
193
190
  - info: 0
194
191
  - fail-on: high
195
192
  - exit decision: 1
193
+
196
194
  ```
197
195
 
198
196
  Secret-like findings never print the full secret value. Findings report the file, line, and secret type where possible.
@@ -239,3 +237,4 @@ Each check is a plain ESM module with an `id`, metadata, and async `run(context)
239
237
  ItWorksBut is a static heuristic scanner, not a pentest, SAST replacement, dependency vulnerability database, or runtime security monitor. Findings intentionally use wording such as "possible", "potential", and "appears to" when a check is heuristic.
240
238
 
241
239
  Use it as a CI guardrail for common project hygiene and security mistakes. For production systems, combine it with code review, tests, dependency scanning, secrets scanning, threat modeling, and focused security assessment.
240
+ ```
package/bin/itworksbut.js CHANGED
@@ -1,63 +1,72 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { parseArgs } from "../src/cli/parseArgs.js";
4
- import { printUsage, printRuntimeError } from "../src/cli/output.js";
5
- import { createScanSpinner, normalizeTheme, printIntro } from "../src/cli/terminal.js";
6
- import { scanProject } from "../src/core/scanner.js";
7
- import { getExitCode } from "../src/core/findings.js";
8
- import { reportConsole } from "../src/reporters/consoleReporter.js";
9
- import { reportJson } from "../src/reporters/jsonReporter.js";
10
- import { reportSarif } from "../src/reporters/sarifReporter.js";
3
+ import { parseArgs } from '../src/cli/parseArgs.js';
4
+ import { printUsage, printRuntimeError, printVersion } from '../src/cli/output.js';
5
+ import { createScanSpinner, printIntro } from '../src/cli/terminal.js';
6
+ import { packageInfo } from '../src/core/packageInfo.js';
7
+ import { scanProject } from '../src/core/scanner.js';
8
+ import { getExitCode } from '../src/core/findings.js';
9
+ import { reportConsole } from '../src/reporters/consoleReporter.js';
10
+ import { reportJson } from '../src/reporters/jsonReporter.js';
11
+ import { reportSarif } from '../src/reporters/sarifReporter.js';
12
+ import { writeTodoReport } from '../src/reporters/todoReporter.js';
11
13
 
12
14
  async function main() {
13
- const args = parseArgs(process.argv.slice(2));
14
- args.theme = normalizeTheme(args.theme);
15
-
16
- if (args.help) {
17
- printUsage();
18
- return 0;
19
- }
20
-
21
- if (args.command !== "scan") {
22
- printUsage();
23
- return 2;
24
- }
25
-
26
- printIntro(args);
27
-
28
- const spinner = createScanSpinner(args);
29
- if (spinner) spinner.start();
30
-
31
- let result;
32
- try {
33
- result = await scanProject({
34
- rootPath: args.path,
35
- configPath: args.config,
36
- failOn: args.failOn,
37
- verbose: args.verbose
38
- });
39
- if (spinner) spinner.succeed("Scan complete. Now the receipts.");
40
- } catch (error) {
41
- if (spinner) spinner.fail("Scan stopped before the receipts were printed.");
42
- throw error;
43
- }
44
-
45
- if (args.sarif) {
46
- process.stdout.write(`${JSON.stringify(reportSarif(result), null, 2)}\n`);
47
- } else if (args.json) {
48
- process.stdout.write(`${JSON.stringify(reportJson(result), null, 2)}\n`);
49
- } else {
50
- reportConsole(result, args);
51
- }
52
-
53
- return getExitCode(result.findings, result.config.failOn);
15
+ const args = parseArgs(process.argv.slice(2));
16
+
17
+ if (args.help) {
18
+ printUsage();
19
+ return 0;
20
+ }
21
+
22
+ if (args.version) {
23
+ printVersion(`It Works But… version ${packageInfo.version}`);
24
+ return 0;
25
+ }
26
+
27
+ if (args.command !== 'scan') {
28
+ printUsage();
29
+ return 2;
30
+ }
31
+
32
+ printIntro(args);
33
+
34
+ const spinner = createScanSpinner(args);
35
+ if (spinner) spinner.start();
36
+
37
+ let result;
38
+ try {
39
+ result = await scanProject({
40
+ rootPath: args.path,
41
+ configPath: args.config,
42
+ failOn: args.failOn,
43
+ verbose: args.verbose,
44
+ });
45
+ if (spinner) spinner.succeed('Scan complete. Now the receipts.');
46
+ } catch (error) {
47
+ if (spinner) spinner.fail('Scan stopped before the receipts were printed.');
48
+ throw error;
49
+ }
50
+
51
+ if (args.sarif) {
52
+ process.stdout.write(`${JSON.stringify(reportSarif(result), null, 2)}\n`);
53
+ } else if (args.json) {
54
+ process.stdout.write(`${JSON.stringify(reportJson(result), null, 2)}\n`);
55
+ } else if (args.todo) {
56
+ const filePath = await writeTodoReport(result);
57
+ if (!args.quiet) process.stdout.write(`Wrote AI todo file: ${filePath}\n`);
58
+ } else {
59
+ reportConsole(result, args);
60
+ }
61
+
62
+ return getExitCode(result.findings, result.config.failOn);
54
63
  }
55
64
 
56
65
  main()
57
- .then((code) => {
58
- process.exitCode = code;
59
- })
60
- .catch((error) => {
61
- printRuntimeError(error);
62
- process.exitCode = 2;
63
- });
66
+ .then(code => {
67
+ process.exitCode = code;
68
+ })
69
+ .catch(error => {
70
+ printRuntimeError(error);
71
+ process.exitCode = 2;
72
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itworksbut",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Static CI checks for common security, repo, dependency, build, and deployment risks in JavaScript vibe coding projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,11 +18,11 @@ export default {
18
18
  const args = match.match[1] || "";
19
19
  if (/\blimit\s*:/.test(args)) continue;
20
20
  findings.push({
21
- message: "express.json() appears to be used without an explicit request body size limit.",
21
+ message: "Express JSON body parsing appears to be used without an explicit request body size limit.",
22
22
  file: match.file,
23
23
  line: match.line,
24
24
  column: match.column,
25
- recommendation: "Set a conservative limit, for example express.json({ limit: '100kb' }), and tune it per route when needed."
25
+ recommendation: "Set a conservative JSON body parser limit such as 100kb, and tune it per route when needed."
26
26
  });
27
27
  }
28
28
  return findings;
package/src/cli/output.js CHANGED
@@ -1,3 +1,5 @@
1
+ import gradient from 'gradient-string';
2
+
1
3
  export function printUsage() {
2
4
  process.stdout.write(`ItWorksBut
3
5
 
@@ -12,17 +14,24 @@ Options:
12
14
  Levels: critical, high, medium, low, info. Default: low.
13
15
  --json Print machine-readable JSON.
14
16
  --sarif Print SARIF for GitHub Code Scanning.
17
+ --todo Write an AI-ready todo.md to the scanned project.
15
18
  --no-color Disable color styling.
16
19
  --no-banner Disable the intro banner.
17
- --no-spinner Disable scan spinner.
18
- --compact Print one-line findings.
19
20
  --quiet Print only the summary.
20
- --theme <theme> Console theme: default, toxic, mono.
21
21
  --verbose Include scanner warnings and extra metadata.
22
+ --version, -v Print the ItWorksBut version.
22
23
  --help Show this help.
23
24
  `);
24
25
  }
25
26
 
27
+ export function printVersion(version) {
28
+ try {
29
+ process.stdout.write(`${gradient.rainbow(version)}\n`);
30
+ } catch {
31
+ process.stdout.write(`${version}\n`);
32
+ }
33
+ }
34
+
26
35
  export function printRuntimeError(error) {
27
36
  const message = error instanceof Error ? error.message : String(error);
28
37
  process.stderr.write(`ItWorksBut runtime error: ${message}\n`);
@@ -1,5 +1,5 @@
1
- const FLAG_WITH_VALUE = new Set(["--fail-on", "--config", "--path", "--theme"]);
2
- const BOOLEAN_FLAGS = new Set(["--json", "--sarif", "--verbose", "--help", "-h", "--no-color", "--no-banner", "--no-spinner", "--compact", "--quiet"]);
1
+ const FLAG_WITH_VALUE = new Set(["--fail-on", "--config", "--path"]);
2
+ const BOOLEAN_FLAGS = new Set(["--json", "--sarif", "--todo", "--verbose", "--help", "-h", "--version", "-v", "--no-color", "--no-banner", "--quiet"]);
3
3
 
4
4
  export function parseArgs(argv) {
5
5
  const args = {
@@ -9,14 +9,13 @@ export function parseArgs(argv) {
9
9
  failOn: undefined,
10
10
  json: false,
11
11
  sarif: false,
12
+ todo: false,
12
13
  verbose: false,
13
14
  noColor: false,
14
15
  noBanner: false,
15
- noSpinner: false,
16
- compact: false,
17
16
  quiet: false,
18
- theme: "default",
19
- help: false
17
+ help: false,
18
+ version: false
20
19
  };
21
20
 
22
21
  const tokens = [...argv];
@@ -45,13 +44,13 @@ export function parseArgs(argv) {
45
44
 
46
45
  if (BOOLEAN_FLAGS.has(token)) {
47
46
  if (token === "--help" || token === "-h") args.help = true;
47
+ if (token === "--version" || token === "-v") args.version = true;
48
48
  if (token === "--json") args.json = true;
49
49
  if (token === "--sarif") args.sarif = true;
50
+ if (token === "--todo") args.todo = true;
50
51
  if (token === "--verbose") args.verbose = true;
51
52
  if (token === "--no-color") args.noColor = true;
52
53
  if (token === "--no-banner") args.noBanner = true;
53
- if (token === "--no-spinner") args.noSpinner = true;
54
- if (token === "--compact") args.compact = true;
55
54
  if (token === "--quiet") args.quiet = true;
56
55
  continue;
57
56
  }
@@ -59,8 +58,8 @@ export function parseArgs(argv) {
59
58
  throw new Error(`Unknown argument: ${token}`);
60
59
  }
61
60
 
62
- if (args.json && args.sarif) {
63
- throw new Error("Use only one output format: --json or --sarif");
61
+ if ([args.json, args.sarif, args.todo].filter(Boolean).length > 1) {
62
+ throw new Error("Use only one output format: --json, --sarif, or --todo");
64
63
  }
65
64
 
66
65
  return args;
@@ -70,6 +69,5 @@ function assignValue(args, flag, value) {
70
69
  if (flag === "--fail-on") args.failOn = value;
71
70
  else if (flag === "--config") args.config = value;
72
71
  else if (flag === "--path") args.path = value;
73
- else if (flag === "--theme") args.theme = value;
74
72
  else throw new Error(`Unknown argument: ${flag}`);
75
73
  }
@@ -4,8 +4,6 @@ import figlet from "figlet";
4
4
  import gradient from "gradient-string";
5
5
  import ora from "ora";
6
6
 
7
- const THEMES = new Set(["default", "toxic", "mono"]);
8
-
9
7
  const SPINNER_TEXT = {
10
8
  git: "Checking git hygiene",
11
9
  env: "Sniffing for secrets",
@@ -20,21 +18,12 @@ const SPINNER_TEXT = {
20
18
  default: "Looking for things that work but should not ship"
21
19
  };
22
20
 
23
- export function normalizeTheme(theme) {
24
- const normalized = String(theme || "default").toLowerCase();
25
- if (!THEMES.has(normalized)) {
26
- throw new Error(`Invalid theme "${theme}". Expected one of: default, toxic, mono`);
27
- }
28
- return normalized;
29
- }
30
-
31
21
  export function isFancyOutputEnabled(options = {}, env = process.env, stdout = process.stdout) {
32
- return Boolean(stdout.isTTY) && !env.CI && !options.json && !options.sarif && !options.noColor && normalizeTheme(options.theme) !== "mono";
22
+ return Boolean(stdout.isTTY) && !env.CI && !options.json && !options.sarif && !options.todo && !options.noColor;
33
23
  }
34
24
 
35
25
  export function isColorEnabled(options = {}, env = process.env, stdout = process.stdout) {
36
- if (options.noColor || options.json || options.sarif) return false;
37
- if (normalizeTheme(options.theme) === "mono") return false;
26
+ if (options.noColor || options.json || options.sarif || options.todo) return false;
38
27
  if (env.FORCE_COLOR && env.FORCE_COLOR !== "0") return true;
39
28
  if (env.CI) return false;
40
29
  return Boolean(stdout.isTTY);
@@ -51,7 +40,7 @@ export function shouldUseSpinner(options = {}, env = process.env, stdout = proce
51
40
  !env.CI &&
52
41
  !options.json &&
53
42
  !options.sarif &&
54
- !options.noSpinner &&
43
+ !options.todo &&
55
44
  !options.quiet
56
45
  );
57
46
  }
@@ -61,23 +50,18 @@ export function createScanSpinner(options = {}) {
61
50
  return ora({
62
51
  text: SPINNER_TEXT.default,
63
52
  stream: process.stderr,
64
- color: normalizeTheme(options.theme) === "toxic" ? "green" : "cyan"
53
+ color: "cyan"
65
54
  });
66
55
  }
67
56
 
68
57
  export function printIntro(options = {}) {
69
- if (options.json || options.sarif || options.noBanner || options.quiet || process.env.CI || !process.stdout.isTTY) {
58
+ if (options.json || options.sarif || options.todo || options.noBanner || options.quiet || process.env.CI || !process.stdout.isTTY) {
70
59
  return;
71
60
  }
72
61
 
73
- const theme = normalizeTheme(options.theme);
74
62
  const colors = getChalk(options);
75
- const renderTheme = options.noColor ? "mono" : theme;
76
- const title = renderTitle(renderTheme);
77
- const claim =
78
- theme === "toxic"
79
- ? `${colors.bold("Green builds. Red flags.")}\n${colors.green("Let's see what breaks before production.")}`
80
- : `${colors.bold("AI-built? Nice.")}\n${colors.yellow("Now let's see what breaks before production.")}`;
63
+ const title = renderTitle(options.noColor);
64
+ const claim = `${colors.bold("AI-built? Nice.")}\n${colors.yellow("Now let's see what breaks before production.")}`;
81
65
 
82
66
  process.stdout.write(`${title}\n`);
83
67
  process.stdout.write(
@@ -85,12 +69,12 @@ export function printIntro(options = {}) {
85
69
  padding: 1,
86
70
  margin: 1,
87
71
  borderStyle: "round",
88
- borderColor: renderTheme === "mono" ? undefined : renderTheme === "toxic" ? "green" : "cyan"
72
+ borderColor: options.noColor ? undefined : "cyan"
89
73
  })}\n`
90
74
  );
91
75
  }
92
76
 
93
- function renderTitle(theme) {
77
+ function renderTitle(noColor) {
94
78
  let title = "ItWorksBut";
95
79
  try {
96
80
  title = figlet.textSync("ItWorksBut", {
@@ -103,8 +87,7 @@ function renderTitle(theme) {
103
87
  }
104
88
 
105
89
  try {
106
- if (theme === "mono") return title;
107
- if (theme === "toxic") return gradient(["#faff00", "#39ff14", "#00f5ff"])(title);
90
+ if (noColor) return title;
108
91
  return gradient.rainbow(title);
109
92
  } catch {
110
93
  return title;
@@ -0,0 +1,11 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
6
+ const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
7
+
8
+ export const packageInfo = {
9
+ name: packageJson.name,
10
+ version: packageJson.version
11
+ };
@@ -1,6 +1,7 @@
1
1
  import checks from "../checks/index.js";
2
2
  import { createContext } from "./context.js";
3
3
  import { normalizeFinding, severityRank } from "./findings.js";
4
+ import { packageInfo } from "./packageInfo.js";
4
5
 
5
6
  export async function scanProject(options = {}) {
6
7
  const startedAt = new Date();
@@ -43,7 +44,7 @@ export async function scanProject(options = {}) {
43
44
  config: context.config,
44
45
  meta: {
45
46
  tool: "ItWorksBut",
46
- version: "0.1.0",
47
+ version: packageInfo.version,
47
48
  rootPath: context.rootPath,
48
49
  packageManager: context.packageManager,
49
50
  gitAvailable: context.gitAvailable,