Skip to content

Polish npx shotfix for first-run experience#3

Open
JasperNoBoxDev wants to merge 1 commit intomainfrom
feat/npx-shotfix-polish
Open

Polish npx shotfix for first-run experience#3
JasperNoBoxDev wants to merge 1 commit intomainfrom
feat/npx-shotfix-polish

Conversation

@JasperNoBoxDev
Copy link
Copy Markdown
Contributor

@JasperNoBoxDev JasperNoBoxDev commented Mar 15, 2026

Summary

  • Watch mode ON by defaultshotfix starts with auto-fix enabled; use --no-watch to disable
  • First-run API key setup — interactive prompt when no key found, saves to .shotfix/.env, loads automatically on subsequent runs
  • Startup banner — clean display with widget script tag instructions
  • Widget serving/widget.js endpoint serves the built widget JS directly from the CLI server
  • npm distribution — widget included in package files for unpkg/jsdelivr CDN access
  • --help flag — full usage documentation

Test plan

  • node cli/dist/bin.js --help shows usage
  • node cli/dist/bin.js without API key prompts interactively (saves to .shotfix/.env)
  • GEMINI_API_KEY=xxx node cli/dist/bin.js starts with watch mode ON
  • node cli/dist/bin.js --no-watch starts with watch mode OFF
  • curl localhost:2847/widget.js returns the widget JS
  • curl localhost:2847/health returns {"status":"ok","watch":true}
  • .shotfix/.gitignore includes both captures/ and .env

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added --help flag to display usage information and version details.
    • Interactive Gemini API key setup with automatic persistence to configuration file.
    • Watch mode now enabled by default; use --no-watch to disable.
    • Added widget serving endpoint.
  • Chores

    • Updated package keywords for improved discoverability.
    • Enhanced build workflow for widget assets.

- Watch mode ON by default (use --no-watch to disable)
- First-run API key prompt: interactive setup, saves to .shotfix/.env
- Clean startup banner with widget script tag instructions
- Serve widget JS from /widget.js endpoint (for easy dev setup)
- Include widget build in npm package (unpkg/jsdelivr compatible)
- Add --help flag with full usage docs
- Remove noxkey fallback (developer-only)
- .shotfix/.gitignore now includes .env

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 15, 2026

📝 Walkthrough

Walkthrough

This pull request adds widget asset building and serving capabilities to the CLI package, introduces interactive Gemini API key management with environment file persistence, and inverts the watch mode flag to be enabled by default. It also updates package metadata and adds several CLI enhancements.

Changes

Cohort / File(s) Summary
Build Configuration & Distribution
cli/package.json
Added widget build script that compiles and copies minified widget output; updated prepublishOnly hook to run widget build before main build; added widget asset to distribution files; refreshed keywords to reflect auto-fix and developer-tools focus.
CLI Implementation
cli/src/cli.ts
Added help banner, version display, and --help flag; inverted watch mode behavior (--no-watch disables, watch now default); introduced interactive Gemini API key retrieval with fallback chain (env var → .shotfix/.env file → user prompt with TTY); implemented .shotfix/.env and .gitignore file management; added cleanOldCaptures helper and /widget.js HTTP route for serving prebuilt widget; enhanced server startup logging with version and usage information.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI
    participant Env as Environment
    participant FileSystem as File System
    participant GeminiAPI as Gemini API

    User->>CLI: Start CLI (watch mode enabled by default)
    CLI->>Env: Check GEMINI_API_KEY env var
    alt API key in environment
        Env-->>CLI: Return API key
        CLI->>GeminiAPI: Use API key
    else API key not in environment
        CLI->>FileSystem: Check .shotfix/.env file
        alt API key in file
            FileSystem-->>CLI: Return API key from file
            CLI->>GeminiAPI: Use API key
        else API key not in file and TTY mode
            CLI->>User: Prompt for Gemini API key
            User-->>CLI: Enter API key
            CLI->>FileSystem: Write API key to .shotfix/.env
            FileSystem-->>CLI: File saved
            CLI->>User: Confirm API key saved
            CLI->>GeminiAPI: Use API key
        end
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐰 A widget builds, watch mode springs to life,
API keys prompt without the strife,
Files are served from dist so neat,
Interactive CLI, developer's treat! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Polish npx shotfix for first-run experience' directly and clearly summarizes the main objective of the pull request: improving the first-run user experience for the npx shotfix tool.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/npx-shotfix-polish
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the first-run experience and overall usability of the shotfix CLI. It streamlines the initial setup process for API keys, provides clearer guidance for integrating the tool, and improves the default operational behavior by enabling watch mode by default. These changes aim to make shotfix more intuitive and accessible for new and existing users.

Highlights

  • Default Watch Mode: The shotfix CLI now defaults to watch mode being enabled, automatically applying fixes. Users can disable this with the --no-watch flag.
  • Interactive API Key Setup: A new interactive prompt guides users to set up their Gemini API key on the first run, saving it to .shotfix/.env for future use.
  • Widget Serving and Distribution: The CLI now serves the widget.js file directly via a /widget.js endpoint, and the widget is included in the npm package for CDN access.
  • Enhanced Startup Experience: A new startup banner displays version information and clear instructions for integrating the Shotfix widget into HTML.
  • Help Documentation: Comprehensive usage documentation is now available via the --help or -h command-line flags.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • cli/package.json
    • Included widget/dist/shotfix.min.js in the files array for npm distribution.
    • Added a build:widget script to compile and copy the widget.
    • Modified the prepublishOnly script to ensure the widget is built before package publication.
    • Updated keywords to include auto-fix, developer-tools, and ui-debug.
  • cli/src/cli.ts
    • Implemented a --help flag to display usage instructions and environment variables.
    • Reversed the default behavior of watchMode to be ON by default, requiring --no-watch to disable.
    • Updated the .gitignore generation to explicitly include .env and ensure its presence if the file already exists.
    • Introduced an interactive prompt for Gemini API key input on first run if not found in environment variables or .shotfix/.env, saving it to .shotfix/.env.
    • Added a new HTTP endpoint /widget.js to serve the compiled shotfix.min.js file.
    • Refined the server startup console output to display a version banner, widget integration instructions, and clearer watch mode status messages.
    • Removed the dependency on noxkey for API key retrieval.
Activity
  • Initial setup of the pull request with a clear description and test plan provided by the author.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly improves the first-run experience for shotfix by adding a --help command, interactive API key setup, and serving the widget directly from the CLI. The changes are well-implemented. My review includes suggestions to enhance robustness and maintainability, focusing on cross-platform build script compatibility, improved error handling for file operations, and more resilient parsing of configuration files.

Comment on lines +51 to +56
try {
const existing = await readFile(join(shotfixDir, '.gitignore'), 'utf-8');
if (!existing.includes('.env')) {
await writeFile(join(shotfixDir, '.gitignore'), existing.trimEnd() + '\n.env\n');
}
} catch {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The nested catch block is empty, which silences any errors that might occur when trying to read or update the .gitignore file. If an error other than the file already existing occurs (e.g., a permissions issue), it will fail silently, and the .env file might not be ignored. This increases the risk of accidentally committing secrets. It's crucial to log potential errors here to alert the user.

    try {
      const existing = await readFile(join(shotfixDir, '.gitignore'), 'utf-8');
      if (!existing.includes('.env')) {
        await writeFile(join(shotfixDir, '.gitignore'), existing.trimEnd() + '\n.env\n');
      }
    } catch (e) {
      console.warn(`[shotfix] Failed to update .shotfix/.gitignore: ${(e as Error).message}`);
    }

"build": "tsc",
"start": "node dist/index.js",
"prepublishOnly": "npm run build"
"build:widget": "cd ../widget && npm run build && mkdir -p ../cli/widget/dist && cp dist/shotfix.min.js ../cli/widget/dist/",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The build:widget script relies on shell commands (cd, mkdir -p, cp) that may not be available or behave consistently across all operating systems, particularly on standard Windows command prompts. This can hinder cross-platform development and usage. For better portability, consider using Node.js-based utilities for file system operations, either through a library like fs-extra or by creating a small Node script that uses the built-in fs/promises module.

Comment on lines +90 to +91
const match = envContent.match(/^GEMINI_API_KEY=(.+)$/m);
if (match) apiKey = match[1].trim();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regular expression for parsing the .env file is not robust enough to handle common formats, such as values enclosed in quotes or lines with trailing comments. This could lead to parsing an incorrect API key. To improve reliability, the regex should be updated to accommodate these variations.

      const match = envContent.match(/^\s*GEMINI_API_KEY=(.*?)(\s*#.*)?$/m);
      if (match) apiKey = match[1].trim().replace(/^['"]|['"]$/g, '');

Comment on lines +390 to +393
} catch {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Widget not found');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The catch block for serving /widget.js sends a 404 response but doesn't log the error on the server. If reading the file fails for any reason (e.g., permissions, build errors), the issue is not visible on the server, making debugging difficult. Logging the error here would provide valuable diagnostic information.

      } catch (err) {
        console.error(`[shotfix] Error serving widget.js: ${(err as Error).message}`);
        res.writeHead(404, { 'Content-Type': 'text/plain' });
        res.end('Widget not found');
      }

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
cli/package.json (1)

16-17: Make build:widget publish flow cross-platform.

Line 16 relies on POSIX shell commands (mkdir -p, cp), which will fail in non-POSIX publish environments like Windows PowerShell or cmd.exe.

♻️ Proposed refactor
-    "build:widget": "cd ../widget && npm run build && mkdir -p ../cli/widget/dist && cp dist/shotfix.min.js ../cli/widget/dist/",
+    "build:widget": "node ./scripts/build-widget.mjs",
// scripts/build-widget.mjs
import { mkdir, copyFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { execFileSync } from 'node:child_process';

const widgetDir = resolve(process.cwd(), '../widget');
const targetDir = resolve(process.cwd(), 'widget/dist');
execFileSync('npm', ['run', 'build'], { cwd: widgetDir, stdio: 'inherit' });
await mkdir(targetDir, { recursive: true });
await copyFile(resolve(widgetDir, 'dist/shotfix.min.js'), resolve(targetDir, 'shotfix.min.js'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/package.json` around lines 16 - 17, Replace the POSIX-only "build:widget"
npm script with a cross-platform Node script: add a new scripts/build-widget.mjs
that runs npm run build in the ../widget folder (use execFileSync or spawnSync),
creates the target folder (use fs/promises.mkdir with { recursive: true }) and
copies dist/shotfix.min.js into cli/widget/dist (use fs/promises.copyFile or
copyFile), then update package.json "build:widget" to invoke node
./scripts/build-widget.mjs and keep "prepublishOnly" referencing "npm run
build:widget && npm run build"; ensure the identifiers mentioned (build:widget,
prepublishOnly, scripts/build-widget.mjs and functions execFileSync, mkdir,
copyFile) are used so the change is cross-platform.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/src/cli.ts`:
- Around line 97-101: Several console.log calls in cli.ts (notably the branch
that checks apiKey and watchMode using variables apiKey and watchMode, plus the
other occurrences around the blocks referenced) are missing the required
"[Shotfix]" prefix; update each plain console.log invocation (including the
messages at the apiKey/watchMode check and the other occurrences flagged) to
prepend "[Shotfix] " to the message string so all CLI output follows the project
logging prefix rule. Locate the console.log calls in the top-level CLI flow (the
apiKey/watchMode branch) and the other unprefixed console.log blocks and change
their messages to start with "[Shotfix]" while preserving the original message
text and spacing.
- Around line 24-25: Remove the user-configurable port option from the CLI:
delete the `--port` entry from the help/usage text and remove any
parsing/handling of a `port` flag/option in cli.ts (e.g., where CLI args are
parsed or a `port` variable is set). Ensure the server always uses a fixed
constant PORT = 2847 (replace any reference to parsed port with the constant) so
the health/capture endpoints remain on 2847; update any variables or function
calls that used `port` to reference the fixed `PORT` instead and remove unused
imports/variables related to the removed option.
- Around line 50-55: The existing .gitignore backfill only appends ".env";
update the logic in the try block that reads/writes join(shotfixDir,
'.gitignore') to check for and append any missing entries from the list [".env",
"captures/", "screenshots.json"] (or whichever specific screenshot JSON filename
you use) instead of only ".env"; use the existing variable names (readFile,
writeFile, shotfixDir, existing) to build a newContents string that adds only
the missing lines and then writeFile(join(shotfixDir, '.gitignore'),
newContents).
- Around line 111-114: The .shotfix/.env is being written with default
permissions; update the mkdir and writeFile calls so the directory is created
with restrictive mode and the env file is written with mode 0o600: when calling
mkdir(shotfixDir, { recursive: true }) include a mode of 0o700 and when calling
writeFile(join(shotfixDir, '.env'), ...) pass options with mode: 0o600 (or
alternatively call fs.chmod after writing) so the API key file is only
readable/writable by the owner; locate the mkdir and writeFile usages in cli.ts
to apply this change.

---

Nitpick comments:
In `@cli/package.json`:
- Around line 16-17: Replace the POSIX-only "build:widget" npm script with a
cross-platform Node script: add a new scripts/build-widget.mjs that runs npm run
build in the ../widget folder (use execFileSync or spawnSync), creates the
target folder (use fs/promises.mkdir with { recursive: true }) and copies
dist/shotfix.min.js into cli/widget/dist (use fs/promises.copyFile or copyFile),
then update package.json "build:widget" to invoke node
./scripts/build-widget.mjs and keep "prepublishOnly" referencing "npm run
build:widget && npm run build"; ensure the identifiers mentioned (build:widget,
prepublishOnly, scripts/build-widget.mjs and functions execFileSync, mkdir,
copyFile) are used so the change is cross-platform.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 872934d2-b118-4e98-b686-e7657a0243f4

📥 Commits

Reviewing files that changed from the base of the PR and between 454ee72 and 4b22d98.

📒 Files selected for processing (2)
  • cli/package.json
  • cli/src/cli.ts

Comment on lines +24 to +25
--port <n> Server port (default: 2847)
--dir <path> Captures directory (default: .shotfix/captures)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove --port from user-facing CLI behavior to keep endpoint contract fixed.

Line 24 advertises a configurable port, but this service contract is required to stay on 2847 for health/capture integration.

🔧 Proposed fix
-    --port <n>      Server port (default: 2847)
     --dir <path>    Captures directory (default: .shotfix/captures)

And remove --port parsing so the server always listens on PORT = 2847.

As per coding guidelines: "cli/src/**/*.{js,ts}: Fixed port 2847 must be used for the local dev server health check and capture endpoint".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--port <n> Server port (default: 2847)
--dir <path> Captures directory (default: .shotfix/captures)
--dir <path> Captures directory (default: .shotfix/captures)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/cli.ts` around lines 24 - 25, Remove the user-configurable port
option from the CLI: delete the `--port` entry from the help/usage text and
remove any parsing/handling of a `port` flag/option in cli.ts (e.g., where CLI
args are parsed or a `port` variable is set). Ensure the server always uses a
fixed constant PORT = 2847 (replace any reference to parsed port with the
constant) so the health/capture endpoints remain on 2847; update any variables
or function calls that used `port` to reference the fixed `PORT` instead and
remove unused imports/variables related to the removed option.

Comment on lines +50 to +55
// Already exists — make sure .env is in it
try {
const existing = await readFile(join(shotfixDir, '.gitignore'), 'utf-8');
if (!existing.includes('.env')) {
await writeFile(join(shotfixDir, '.gitignore'), existing.trimEnd() + '\n.env\n');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Backfill both .gitignore entries when the file already exists.

Lines 53-55 only append .env. If an existing .shotfix/.gitignore is missing captures/, screenshots/JSON can still be committed accidentally.

🛠️ Proposed fix
-      const existing = await readFile(join(shotfixDir, '.gitignore'), 'utf-8');
-      if (!existing.includes('.env')) {
-        await writeFile(join(shotfixDir, '.gitignore'), existing.trimEnd() + '\n.env\n');
-      }
+      const gitignorePath = join(shotfixDir, '.gitignore');
+      const existing = await readFile(gitignorePath, 'utf-8');
+      const lines = new Set(existing.split(/\r?\n/).filter(Boolean));
+      lines.add('captures/');
+      lines.add('.env');
+      await writeFile(gitignorePath, `${Array.from(lines).join('\n')}\n`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Already exists — make sure .env is in it
try {
const existing = await readFile(join(shotfixDir, '.gitignore'), 'utf-8');
if (!existing.includes('.env')) {
await writeFile(join(shotfixDir, '.gitignore'), existing.trimEnd() + '\n.env\n');
}
// Already exists — make sure .env is in it
try {
const gitignorePath = join(shotfixDir, '.gitignore');
const existing = await readFile(gitignorePath, 'utf-8');
const lines = new Set(existing.split(/\r?\n/).filter(Boolean));
lines.add('captures/');
lines.add('.env');
await writeFile(gitignorePath, `${Array.from(lines).join('\n')}\n`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/cli.ts` around lines 50 - 55, The existing .gitignore backfill only
appends ".env"; update the logic in the try block that reads/writes
join(shotfixDir, '.gitignore') to check for and append any missing entries from
the list [".env", "captures/", "screenshots.json"] (or whichever specific
screenshot JSON filename you use) instead of only ".env"; use the existing
variable names (readFile, writeFile, shotfixDir, existing) to build a
newContents string that adds only the missing lines and then
writeFile(join(shotfixDir, '.gitignore'), newContents).

Comment on lines +97 to +101
if (!apiKey && watchMode && process.stdin.isTTY) {
console.log('');
console.log(' 🔑 Shotfix needs a Gemini API key for auto-fix.');
console.log(' Get one free at: https://aistudio.google.com/apikey');
console.log('');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prefix new console logs with [Shotfix].

Lines 99-101, 114, 483, and 507-520 use unprefixed console output; changed logs should follow the project logging prefix rule.

As per coding guidelines: "Applies to /{widget,cli}//*.{js,ts} : Use [Shotfix] prefix for all console logging".

Also applies to: 114-114, 483-483, 507-520

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/cli.ts` around lines 97 - 101, Several console.log calls in cli.ts
(notably the branch that checks apiKey and watchMode using variables apiKey and
watchMode, plus the other occurrences around the blocks referenced) are missing
the required "[Shotfix]" prefix; update each plain console.log invocation
(including the messages at the apiKey/watchMode check and the other occurrences
flagged) to prepend "[Shotfix] " to the message string so all CLI output follows
the project logging prefix rule. Locate the console.log calls in the top-level
CLI flow (the apiKey/watchMode branch) and the other unprefixed console.log
blocks and change their messages to start with "[Shotfix]" while preserving the
original message text and spacing.

Comment on lines +111 to +114
if (apiKey) {
await mkdir(shotfixDir, { recursive: true });
await writeFile(join(shotfixDir, '.env'), `GEMINI_API_KEY=${apiKey}\n`);
console.log(' ✅ Saved to .shotfix/.env\n');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify .env write call and whether explicit restrictive mode is set.
rg -n "writeFile\\(\\s*join\\(shotfixDir, '\\.env'\\)" cli/src/cli.ts -C4

Repository: No-Box-Dev/shotfix

Length of output: 317


🏁 Script executed:

# Check if there are other instances of writeFile in this file that might have the same issue
rg -n "writeFile" cli/src/cli.ts

Repository: No-Box-Dev/shotfix

Length of output: 772


Write .shotfix/.env with restrictive permissions.

Line 113 saves the API key without explicit file mode; default permissions (typically 0o644 with common umask settings) make the file readable by other users on the system.

Fix
-      await writeFile(join(shotfixDir, '.env'), `GEMINI_API_KEY=${apiKey}\n`);
+      await writeFile(
+        join(shotfixDir, '.env'),
+        `GEMINI_API_KEY=${apiKey}\n`,
+        { mode: 0o600 }
+      );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (apiKey) {
await mkdir(shotfixDir, { recursive: true });
await writeFile(join(shotfixDir, '.env'), `GEMINI_API_KEY=${apiKey}\n`);
console.log(' ✅ Saved to .shotfix/.env\n');
if (apiKey) {
await mkdir(shotfixDir, { recursive: true });
await writeFile(
join(shotfixDir, '.env'),
`GEMINI_API_KEY=${apiKey}\n`,
{ mode: 0o600 }
);
console.log(' ✅ Saved to .shotfix/.env\n');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/cli.ts` around lines 111 - 114, The .shotfix/.env is being written
with default permissions; update the mkdir and writeFile calls so the directory
is created with restrictive mode and the env file is written with mode 0o600:
when calling mkdir(shotfixDir, { recursive: true }) include a mode of 0o700 and
when calling writeFile(join(shotfixDir, '.env'), ...) pass options with mode:
0o600 (or alternatively call fs.chmod after writing) so the API key file is only
readable/writable by the owner; locate the mkdir and writeFile usages in cli.ts
to apply this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant