Skip to content

feat: add positional path arguments for single-file/directory targeting#15

Open
CodeDeficient wants to merge 35 commits into
mainfrom
fix/false-positive-issues-2-6-7-8
Open

feat: add positional path arguments for single-file/directory targeting#15
CodeDeficient wants to merge 35 commits into
mainfrom
fix/false-positive-issues-2-6-7-8

Conversation

@CodeDeficient

@CodeDeficient CodeDeficient commented Jun 9, 2026

Copy link
Copy Markdown
Owner

User description

Adds [path...] positional arguments to KarpeSlop so it can target specific files or directories instead of always globbing the entire project.

Closes #14 — the original issue framed the problem as '--strict mode should focus on one file', but the real gap was that single-file targeting didn't exist at all. Once it exists, --strict correctly reports which targeted file(s) had critical issues.

Changes

  • Add targetPaths field and constructor parameter to AISlopDetector
  • Add resolveTargetPaths() to validate, filter, and expand CLI paths
  • Update detect() to use targetPaths when provided instead of findAllFiles()
  • Parse positional args (non-flags) as target paths in runIfMain()
  • Add -- separator so file paths starting with - aren't misclassified as flags
  • Update --strict to report which file(s) had critical issues when targeting
  • Update help text with [path...] usage and examples

Refactors

  • Extract getGlobIgnorePatterns() and isExcludedPath() so findAllFiles() and resolveTargetPaths() share the same exclusion logic
  • Simplify isExcludedPath with De Morgan's law; add missing exclusion patterns (dotfiles, /types/, .d.ts)
  • Use segment-based check for types/ to avoid substring false positives (e.g., subtypes/, mytypes/)

Usage

karpeslop src/app.ts               # Scan a single file
karpeslop src/lib/                 # Scan a directory
karpeslop src/app.ts src/lib/      # Scan specific paths
karpeslop --strict src/app.ts      # Block on critical issues in a specific file
karpeslop -- -dash-file.ts         # Scan a file whose name starts with -

Summary by CodeRabbit

  • New Features

    • Fresh npm package-version detection with configurable minimum age (default 7 days)
    • Support for explicit CLI target paths via -- separator
    • Project manifests and lockfiles are always discovered and analyzed, even in quiet mode
  • Bug Fixes

    • Validates minimum-package-age config at load time
    • Reduces duplicate fresh-package reports and fixes CLI executable mapping
    • Improved process exit handling for termination signals
  • Refactor

    • Shared glob ignore/exclusion logic for discovery/targeting
  • Other

    • Fresh-version findings excluded from score calculation

CodeAnt-AI Description

Check package freshness and target specific files or folders from the CLI

What Changed

  • KarpeSlop now scans package.json and package-lock.json for recently published npm versions and warns when a dependency is too new to trust
  • You can now scan one file, one folder, or several paths directly from the command line instead of always scanning the whole project
  • -- is supported so paths that start with - are treated as targets, not flags
  • --strict now names the targeted file(s) that contain critical issues
  • The CLI entry point and exit handling were corrected so the command runs reliably and returns the right exit code
  • New tests cover path parsing, excluded paths, package-age checks, and CLI behavior; changelog and package metadata were updated

Impact

✅ Faster single-file scans
✅ Fewer false flag parsing failures
✅ Clearer strict-mode failures

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…d less than 7 days ago

- Add minPackageAgeDays config option (default: 7 days)
- Add analyzePackageVersions method to parse package.json/package-lock.json
- Add getNpmPackageAge helper to fetch version publish date from npm registry
- Add npmPackageCache to cache npm lookups and avoid repeated API calls
- Update detect and analyzeFile to be async for npm lookups
KarpeSlop had no way to target specific files or directories -- it always
globs the entire project. Add [path...] positional arguments that accept
files or directories to scan, skipping full-project discovery when provided.

- Add targetPaths field and constructor parameter to AISlopDetector
- Add resolveTargetPaths() to validate, filter, and expand CLI paths
- Update detect() to use targetPaths when provided instead of findAllFiles()
- Parse positional args (non-flags) as target paths in runIfMain()
- Update --strict to report which file(s) had critical issues when targeting
- Update help text with path arguments and examples

Closes #14
…hs, add -- separator

The resolveTargetPaths() method had an incomplete ignore list missing
'**/.*', '**/types/**', self-exclusion patterns, and the post-glob filter.
This meant targeting a directory would include .git/, coverage/, and the
detector itself — files excluded in full-project scan mode.

Extract getGlobIgnorePatterns() and isExcludedPath() as shared methods
used by both findAllFiles() and resolveTargetPaths().

Also add -- separator support so file paths starting with - aren't
misclassified as flags.
…patterns

Convert double-negation !(a && b) to positive form (a || b) for
readability. Add checks for dotfiles, /types/, .d.ts, and self-
exclusion patterns that were in getGlobIgnorePatterns but missing
from isExcludedPath.
…for explicit file args

- Replace startsWith('.') with segment-based check so nested dotfiles
  like src/.env are properly excluded
- Add startsWith('types/') to match root-level types/ directory
- Run isExcludedPath on explicitly passed file paths in
  resolveTargetPaths so excluded files are skipped with a warning
  rather than silently scanned
Mirror the three-check pattern used for generated/ — trailing slash
variant catches types/ as a directory anywhere in the path, while the
prefix-only and contains variants cover segment-edge cases.
…false positives

includes('types/') is a substring match — it would match subtypes/,
mytypes/, and any path where 'types/' appears as a substring of a
segment. Use split('/').includes('types') so only actual segments
named 'types' are excluded.
After switching to split('/').includes('types') in 7db3f3f, the older
includes('/types/') and startsWith('types/') lines were subsumed by
the segment-based check. Remove them to keep the exclusion list clean.
@codeant-ai

codeant-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds configurable npm-publish-time “fresh package version” checks, makes per-file analysis async with bounded registry lookups and caching, centralizes discovery/ignore/exclude logic and target-path expansion (including -- passthrough), updates CLI strict-mode reporting and signal-aware exit handling, and bumps package metadata with CHANGELOG entry for 1.0.25.

Changes

Fresh Package Version Detection and CLI Targeting

Layer / File(s) Summary
Configuration and Package Metadata
CHANGELOG.md, package.json, ai-slop-detector.ts, karpeslop-bin.js
Adds minPackageAgeDays config field (validated as finite non-negative), bumps version to 1.0.25, updates CLI entrypoint in bin, and documents the 1.0.25 release in CHANGELOG.md.
CLI Parsing, -- Passthrough, Strict Mode & Signal Handling
karpeslop-cli.js, ai-slop-detector.ts, karpeslop-bin.js
CLI parsing supports -- passthrough so trailing args are treated as paths; detector constructed with optional targetPaths; quiet/strict flags derived from parsed flags; --strict lists files with critical issues when explicit targets are provided; process exit handling centralized with signal-aware mapping.
File Discovery, Ignore Patterns, and Path Resolution
ai-slop-detector.ts, karpeslop-bin.js
Centralizes glob ignore patterns and exclusion logic, refactors findAllFiles() to include manifests, and adds resolveTargetPaths() to expand/validate CLI-provided files/directories with shared exclusion rules.
Async File Analysis and Manifest Routing
ai-slop-detector.ts, karpeslop-bin.js
Converts analyzeFile() to async, updates detect() to await per-file analysis, and routes manifest files to analyzePackageVersions() for freshness checks.
Package Version Analysis and Caching
ai-slop-detector.ts, karpeslop-bin.js
Adds semver caret/tilde parsing, analyzePackageVersions() and getNpmPackageAge() that extract dependency candidates (caret/tilde ranges from package.json, resolved entries from package-lock.json), perform rate-limited registry fetches with abort timeouts, cache per-package time maps, deduplicate reports, and emit fresh_package_version when age < minPackageAgeDays (default 7). Also excludes fresh_package_version issues from the KarpeSlop score.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI
  participant AISlopDetector
  participant resolveTargetPaths
  participant analyzeFile
  participant analyzePackageVersions
  participant getNpmPackageAge
  participant npmRegistry

  User->>CLI: run with optional paths / flags
  CLI->>AISlopDetector: construct(with parsed targetPaths)
  AISlopDetector->>resolveTargetPaths: expand/validate targets
  AISlopDetector->>analyzeFile: await per-file analysis
  analyzeFile->>analyzePackageVersions: manifest content
  analyzePackageVersions->>getNpmPackageAge: request package@version (pLimit)
  getNpmPackageAge->>npmRegistry: fetch metadata (abort if slow)
  npmRegistry-->>getNpmPackageAge: return publish times
  getNpmPackageAge-->>analyzePackageVersions: publish time / age
  analyzePackageVersions-->>analyzeFile: emit fresh_package_version (deduped)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A fresher detector hops into the fray,
Sniffing npm packages, checking their age each day,
With -- as a path-builder's trusty guide,
Strict mode finds focus, no more noisy tide,
Async and cached, swift as a bounding hare.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add positional path arguments for single-file/directory targeting' accurately summarizes the main feature addition enabling explicit file/directory targeting instead of full project globbing.
Linked Issues check ✅ Passed The PR addresses all requirements from issue #14: implements positional path arguments for single-file targeting, refactors exclusion logic, updates --strict mode to report targeted files with exit code 2, and ensures manifest analysis for package version detection.
Out of Scope Changes check ✅ Passed The PR includes fresh package version detection (npm registry lookups with minPackageAgeDays config, caching, concurrency throttling) and signal-aware exit handling in karpeslop-cli.js which extend beyond the core #14 requirement but are documented as included follow-ups supporting robustness and completeness.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/false-positive-issues-2-6-7-8

Warning

Review ran into problems

🔥 Problems

Stopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a @coderabbit review after the pipeline has finished.


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.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Jun 9, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a390199733

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ai-slop-detector.ts Outdated
Comment on lines +914 to +915
if (filePath.endsWith('package.json') || filePath.endsWith('package-lock.json')) {
this.analyzePackageVersions(filePath, content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make package files reachable before analyzing them

When users rely on the new fresh_package_version rule advertised in the changelog, this branch is never reached from the CLI: findAllFiles() only globs .ts/.tsx/.js/.jsx, and resolveTargetPaths() rejects explicit package.json or package-lock.json targets as unsupported because targetExtensions does not include .json. As a result, both full scans and karpeslop package.json silently skip the only files this rule can analyze.

Useful? React with 👍 / 👎.

Comment thread ai-slop-detector.ts Outdated
Comment on lines +563 to +566
} else if (this.targetExtensions.includes(ext)) {
resolved.push(targetPath);
} else {
console.warn(`⚠️ Target file has unsupported extension (${ext}), skipping: ${targetPath}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Explicit file targeting rejects .json inputs as unsupported, so users cannot target package.json/package-lock.json directly even though package-age logic was added. This leaves the new package analysis unreachable for targeted scans. Allow JSON files for this specific analysis path. [incomplete implementation]

Severity Level: Major ⚠️
- ⚠️ Users cannot target `package.json`/`package-lock.json` via CLI.
- ⚠️ Manifest-only scans misleadingly report "no issues detected".
- ⚠️ Package freshness rule remains unreachable in typical targeted use.
Steps of Reproduction ✅
1. Run the CLI with a manifest path, e.g. `karpeslop package.json`, which is routed
through `runIfMain()` in `ai-slop-detector.ts:27-44`. Since the argument does not start
with `-`, it lands in `targetPaths` (line 43), and `AISlopDetector` is constructed with
those `targetPaths` at line 46.

2. `runIfMain()` computes `quiet`/`strict` (lines 99-100) and calls `await
detector.detect(quiet)` at line 103. In `detect()` at `ai-slop-detector.ts:152-171`,
`this.targetPaths.length > 0` is true, so it calls `this.resolveTargetPaths()` (line 158)
to turn CLI paths into `filesToAnalyze`.

3. Inside `resolveTargetPaths()` at `ai-slop-detector.ts:249-268`, for `targetPath`
pointing to `package.json`, `fs.statSync(targetPath)` reports a file, `ext =
path.extname(targetPath)` becomes `.json`, and `this.isExcludedPath(targetPath)` is false
(check at `ai-slop-detector.ts:208-225` does not match `package.json`).

4. The extension check block at `ai-slop-detector.ts:559-566` then executes: since `.json`
is not in `this.targetExtensions` (defined as `['.ts', '.tsx', '.js', '.jsx']` at class
top), control goes to the `else` branch at line 565, logging `⚠️ Target file has
unsupported extension (.json), skipping: package.json` and never pushing the path into
`resolved`. `detect()` then iterates over an empty `filesToAnalyze` array and
`generateReport()` at `ai-slop-detector.ts:113-120` prints "✅ No AI Slop issues
detected!", meaning `package.json`/`package-lock.json` are never analyzed in targeted
scans despite the new package-age logic in `analyzePackageVersions()`.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ai-slop-detector.ts
**Line:** 563:566
**Comment:**
	*Incomplete Implementation: Explicit file targeting rejects `.json` inputs as unsupported, so users cannot target `package.json`/`package-lock.json` directly even though package-age logic was added. This leaves the new package analysis unreachable for targeted scans. Allow JSON files for this specific analysis path.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment thread ai-slop-detector.ts Outdated
Comment on lines +914 to +915
if (filePath.endsWith('package.json') || filePath.endsWith('package-lock.json')) {
this.analyzePackageVersions(filePath, content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: analyzePackageVersions is async but it's called without await, so detect() can finish and generate/exit before package-age checks complete. This causes nondeterministic missing issues (especially in --strict mode). Await this call so package findings are included before reporting and exit-code decisions. [possible bug]

Severity Level: Major ⚠️
- ⚠️ Package freshness issues may be missing from CLI output.
- ⚠️ `ai-slop-report.json` may omit `fresh_package_version` entries.
- ⚠️ Future strict-mode checks on package age could misfire.
Steps of Reproduction ✅
1. The CLI entrypoint `runIfMain()` in `ai-slop-detector.ts:27-47` constructs `const
detector = new AISlopDetector(rootDir, targetPaths.length > 0 ? targetPaths : undefined)`
and then calls `await detector.detect(quiet)` at `ai-slop-detector.ts:102-104`.

2. `detect()` at `ai-slop-detector.ts:152-176` builds `filesToAnalyze` and, for each file,
awaits the async method `this.analyzeFile(file, quiet)` (line 175), expecting all per-file
analysis to complete before returning issues.

3. Inside `analyzeFile()` at `ai-slop-detector.ts:9-237`, after pattern scanning, there is
a special-case block at lines 234-237: `if (filePath.endsWith('package.json') ||
filePath.endsWith('package-lock.json')) { this.analyzePackageVersions(filePath, content);
}` which calls the async `analyzePackageVersions()` (declared at
`ai-slop-detector.ts:25-71`) without `await`.

4. When `analyzeFile()` is eventually invoked with a `package.json` or `package-lock.json`
path (once JSON files are included in `filesToAnalyze` via `findAllFiles()` at
`ai-slop-detector.ts:231-243` or `resolveTargetPaths()` at `ai-slop-detector.ts:249-268`),
it will return to `detect()` before `analyzePackageVersions()` finishes, allowing
`detect()` to call `generateReport()` at `ai-slop-detector.ts:113-120` and `runIfMain()`
to compute exit codes at `ai-slop-detector.ts:108-121` without all `fresh_package_version`
issues having been added to `this.issues`. Currently, JSON files are not yet wired into
scanning (see `targetExtensions` and `findAllFiles()`), so this bug is latent but real in
the new async design.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ai-slop-detector.ts
**Line:** 914:915
**Comment:**
	*Possible Bug: `analyzePackageVersions` is async but it's called without `await`, so `detect()` can finish and generate/exit before package-age checks complete. This causes nondeterministic missing issues (especially in `--strict` mode). Await this call so package findings are included before reporting and exit-code decisions.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment thread ai-slop-detector.ts
Comment on lines +1065 to +1066
const minAgeDays = this.config.minPackageAgeDays ?? 7;
const minAgeMs = minAgeDays * 24 * 60 * 60 * 1000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: minPackageAgeDays is used in arithmetic without validation, so an invalid config value (e.g. non-numeric string) produces NaN and disables the age comparison silently. Validate this config field as a finite positive number before using it. [type error]

Severity Level: Major ⚠️
- ⚠️ Misconfigured `minPackageAgeDays` silently disables package-age checks.
- ⚠️ Users may trust manifest freshness despite broken comparison logic.
- ⚠️ CI policies based on this rule become unreliable under bad config.
Steps of Reproduction ✅
1. Create a configuration file such as `.karpesloprc.json` in the project root with a
non-numeric `minPackageAgeDays` (e.g. `{ \"minPackageAgeDays\": \"seven\" }`).
`loadConfig()` at `ai-slop-detector.ts:93-107` reads and parses this config, then calls
`validateConfig()` at `ai-slop-detector.ts:27-87`.

2. `validateConfig()` rigorously validates `customPatterns`, `severityOverrides`, and
`ignorePaths` (lines 15-65) but never checks that `cfg.minPackageAgeDays` exists or is a
finite number; it returns `cfg as KarpeSlopConfig` at line 67, so the string `"seven"` is
stored as `this.config.minPackageAgeDays`.

3. When `analyzePackageVersions()` is invoked for a manifest file at
`ai-slop-detector.ts:25-71`, it computes `const minAgeDays = this.config.minPackageAgeDays
?? 7;` (line 26), which results in `minAgeDays === "seven"`, and then `const minAgeMs =
minAgeDays * 24 * 60 * 60 * 1000;` (line 27), which yields `NaN` because string `"seven"`
cannot be multiplied.

4. In the loop at `ai-slop-detector.ts:53-69`, each `ageInfo.ageMs < minAgeMs` comparison
becomes `ageInfo.ageMs < NaN`, which is always false in JavaScript. This silently prevents
any `fresh_package_version` issues from being added to `this.issues`, effectively
disabling the package-age rule whenever `minPackageAgeDays` is misconfigured, without any
warning to the user.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ai-slop-detector.ts
**Line:** 1065:1066
**Comment:**
	*Type Error: `minPackageAgeDays` is used in arithmetic without validation, so an invalid config value (e.g. non-numeric string) produces `NaN` and disables the age comparison silently. Validate this config field as a finite positive number before using it.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment thread ai-slop-detector.ts Outdated
Comment on lines +1092 to +1095
for (const [pkgName, version] of Object.entries(packageData)) {
if (version.startsWith('^') || version.startsWith('~')) {
const actualVersion = version.slice(1);
const ageInfo = await this.getNpmPackageAge(pkgName, actualVersion);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The package-age rule only runs for versions starting with ^ or ~, but package-lock.json stores concrete versions (for example 1.2.3) so those entries are never checked. This makes the new lockfile analysis effectively nonfunctional. Handle exact versions too (strip range prefix only when present). [logic error]

Severity Level: Major ⚠️
- ⚠️ `package-lock.json` freshness checks never execute for pinned versions.
- ⚠️ Dependency age assessment relies solely on `package.json` ranges.
- ⚠️ CI consumers may falsely assume lockfile versions are vetted.
Steps of Reproduction ✅
1. When `analyzeFile()` eventually processes a `package-lock.json` (special-case call at
`ai-slop-detector.ts:234-237` for paths ending with `package-lock.json`), it passes the
file content into `analyzePackageVersions(filePath, content)` at
`ai-slop-detector.ts:25-71`.

2. In `analyzePackageVersions()`, the `package-lock.json` branch at
`ai-slop-detector.ts:35-45` builds `packageData[name] = version` using the `version` field
from each `pkg.packages` entry, which in a real npm lockfile is a concrete version string
such as `"1.2.3"` without any `^` or `~` prefix.

3. The loop at `ai-slop-detector.ts:53-55` then iterates over
`Object.entries(packageData)` and only proceeds with the age check when
`version.startsWith('^') || version.startsWith('~')`. For lockfile entries (e.g.
`"1.2.3"`), this condition is always false, so the body that computes `const actualVersion
= version.slice(1); const ageInfo = await this.getNpmPackageAge(pkgName, actualVersion);`
never executes.

4. As a result, even if `getNpmPackageAge()` at `ai-slop-detector.ts:76-105` would
indicate that a dependency was published just hours ago, no `fresh_package_version` issues
are ever pushed to `this.issues` from the lockfile path; only ranged versions in
`package.json` (handled at `ai-slop-detector.ts:46-48`) can currently be flagged. This
makes the intended lockfile analysis effectively a no-op.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ai-slop-detector.ts
**Line:** 1092:1095
**Comment:**
	*Logic Error: The package-age rule only runs for versions starting with `^` or `~`, but `package-lock.json` stores concrete versions (for example `1.2.3`) so those entries are never checked. This makes the new lockfile analysis effectively nonfunctional. Handle exact versions too (strip range prefix only when present).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment thread karpeslop-bin.js Outdated
Comment on lines +720 to +721
this.analyzePackageVersions(filePath, content);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The async package-version scan is invoked without await, so the CLI can print the report and exit before those checks finish. This can silently omit findings and produce incorrect strict-mode behavior. Await the async call inside file analysis. [possible bug]

Severity Level: Major ⚠️
- ⚠️ Runtime CLI may omit package freshness issues in output.
- ⚠️ Generated `ai-slop-report.json` can be incomplete for manifests.
- ⚠️ Future strict-mode gating on package age could be inconsistent.
Steps of Reproduction ✅
1. The published CLI entrypoint in `karpeslop-bin.js` constructs `new
AISlopDetector(rootDir)` (see constructor at `karpeslop-bin.js:228-231`) and calls `await
detector.detect(quiet)` in its main run function (not shown in the diff but mirrored from
`ai-slop-detector.ts:102-104`), relying on `detect()` to finish all per-file analysis
before exit.

2. `detect()` in `karpeslop-bin.js:25-45` finds TypeScript/JavaScript files via
`findAllFiles()` (`karpeslop-bin.js:51-92`), filters them based on `quiet`, and for each
one does `await this.analyzeFile(file, quiet)` at line 40.

3. In `analyzeFile()` at `karpeslop-bin.js:21-223`, after running all text-pattern checks,
there is a special-case block at lines 219-222: `if (filePath.endsWith('package.json') ||
filePath.endsWith('package-lock.json')) { this.analyzePackageVersions(filePath, content);
}`. `analyzePackageVersions()` is defined as an async function at
`karpeslop-bin.js:860-904`, but this call is made without `await`.

4. Once the codebase is updated to actually include `package.json`/`package-lock.json`
paths in `filesToAnalyze` (mirroring the intended design in `ai-slop-detector.ts`),
`analyzeFile()` will resolve its promise while `analyzePackageVersions()` is still
running. This allows `detect()` to finish, `generateReport()` at
`karpeslop-bin.js:949-955` to log results, and the CLI to compute exit codes based on an
incomplete `this.issues` array, omitting `fresh_package_version` findings that may be
added slightly later.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** karpeslop-bin.js
**Line:** 720:721
**Comment:**
	*Possible Bug: The async package-version scan is invoked without `await`, so the CLI can print the report and exit before those checks finish. This can silently omit findings and produce incorrect strict-mode behavior. Await the async call inside file analysis.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

- Add missing await on analyzePackageVersions call so fresh_package_version
  issues aren't dropped (fire-and-forget promise)
- Lockfile branch now correctly checks all package versions (not just
  ^/~ ranges) since package-lock stores exact semver
- Union dependencies/devDependencies/peerDependencies/optionalDependencies
  in package.json check instead of dependencies only
- Skip nested node_modules entries when scanning package-lock to avoid
  transitive double-counting
- package.json/package-lock.json now included in --quiet mode so
  fresh version warnings still fire
- Add parseVersionRange helper to centralize version-range handling
- Fix package-lock.json bin entry to point at karpeslop-cli.js
  (matching package.json) and bump version to 1.0.25
Comment thread ai-slop-detector.ts
Comment on lines +560 to +567
const ext = path.extname(targetPath);
if (this.isExcludedPath(targetPath)) {
console.warn(`⚠️ Target file is in an excluded path, skipping: ${targetPath}`);
} else if (this.targetExtensions.includes(ext)) {
resolved.push(targetPath);
} else {
console.warn(`⚠️ Target file has unsupported extension (${ext}), skipping: ${targetPath}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 Architect Review — HIGH

Explicit file targeting and global discovery only accept .ts/.tsx/.js/.jsx, so package.json/package-lock.json are never analyzed; this makes the new fresh_package_version rule unreachable even when users target manifest files.

Suggestion: Extend file discovery to include package.json/package-lock.json (both in full-project and targeted modes), e.g., by special-casing these manifest paths alongside the existing code extensions so package-age analysis actually runs.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is an **Architect / Logical Review** comment left during a code review. These reviews are first-class, important findings — not optional suggestions. Do NOT dismiss this as a 'big architectural change' just because the title says architect review; most of these can be resolved with a small, localized fix once the intent is understood.

**Path:** ai-slop-detector.ts
**Line:** 560:567
**Comment:**
	*HIGH: Explicit file targeting and global discovery only accept .ts/.tsx/.js/.jsx, so package.json/package-lock.json are never analyzed; this makes the new fresh_package_version rule unreachable even when users target manifest files.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
If a suggested approach is provided above, use it as the authoritative instruction. If no explicit code suggestion is given, you MUST still draft and apply your own minimal, localized fix — do not punt back with 'no suggestion provided, review manually'. Keep the change as small as possible: add a guard clause, gate on a loading state, reorder an await, wrap in a conditional, etc. Do not refactor surrounding code or expand scope beyond the finding.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix

@codeant-ai

codeant-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

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

7-10: Validate CLI entrypoint uses the targeting + package freshness implementation.

package.json publishes bin.karpeslop as karpeslop-cli.js, and that wrapper runs ai-slop-detector.ts via the local tsx binary. ai-slop-detector.ts contains the targeting and package freshness logic (targetPaths/resolveTargetPaths and analyzePackageVersions), so the executable path already references the intended implementation.

npm run build still outputs karpeslop-bin.js, but it isn’t the bin entrypoint; align bin with the built artifact only if that’s intended.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 7 - 10, package.json's "bin.karpeslop" points to
karpeslop-cli.js while the build script emits karpeslop-bin.js, causing a
mismatch between the published CLI entrypoint and the built artifact; decide
which artifact should be the true CLI and make one of two fixes: either change
"bin.karpeslop" to "karpeslop-bin.js" so the published bin matches the build, or
alter the "build" script to output karpeslop-cli.js (or update the wrapper) so
karpeslop-cli.js runs the compiled code; ensure whichever file is used still
invokes the implementation in ai-slop-detector.ts (which contains
targetPaths/resolveTargetPaths and analyzePackageVersions) so the targeting and
package freshness logic is executed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ai-slop-detector.ts`:
- Around line 533-538: findAllFiles() and resolveTargetPaths() currently filter
to only code extensions so package.json and package-lock.json never reach
analyzeFile(), making fresh_package_version unreachable; update the
file-selection logic to include "package.json" and "package-lock.json" (or a
".json" pattern) in this.targetExtensions or otherwise add explicit patterns for
those filenames so they are collected by the glob and pass through
isExcludedPath to analyzeFile(); ensure resolveTargetPaths() similarly allows
these filenames when expanding explicit targets so analyzeFile() can detect and
handle fresh_package_version.
- Around line 1128-1130: The npm registry fetch using const url =
`https://registry.npmjs.org/${encodeURIComponent(pkgName)}` and await fetch(url)
can stall indefinitely; wrap the fetch in an AbortController with a short
configurable timeout (e.g., 3–10s), start a timer that calls controller.abort(),
pass signal to fetch, clear the timer after fetch completes, and catch the
abort/timeout error to return null (or handle as existing non-ok path) so
detection flow cannot block indefinitely.
- Around line 914-916: The call to analyzePackageVersions(filePath, content) is
currently fire-and-forget and can allow reporting/strict-mode decisions to run
before npm-age issues are added; change the caller (the function containing the
snippet that checks filePath.endsWith('package.json') ||
filePath.endsWith('package-lock.json')) to await analyzePackageVersions(...) (or
return its Promise) so the analysis completes before continuing, and if
necessary mark that enclosing function as async and propagate the Promise to
callers so reporting/strict-mode logic waits for analyzePackageVersions to
finish.
- Line 71: The new config field minPackageAgeDays is typed but not validated in
validateConfig(), which allows non-finite/negative values to break freshness
logic; update validateConfig() to check that config.minPackageAgeDays (when
provided) is a finite integer >= 0 (or enforce a minimum like 0), reject or
coerce invalid values, and surface a clear error message referencing
minPackageAgeDays so callers know the config is invalid; ensure any code that
reads minPackageAgeDays (the freshness rule) relies on the validated value.
- Around line 1076-1081: In analyzePackageVersions() update the lockfile branch
so package entries are recorded even when pkgInfo.name is missing: derive the
package name from pkgPath (e.g., parse node_modules/... to produce '`@scope/pkg`'
or 'pkg') when pkgInfo.name is falsy, continue to use pkgInfo.version when
present, and retain the existing skip for empty pkgPath or 'node_modules/';
ensure you still populate packageData[name] = version (or undefined/empty if
version missing) so lockfile entries aren’t gated by pkgInfo.name, and leave the
later isRange/filePath handling unchanged.

In `@CHANGELOG.md`:
- Line 5: Update the CHANGELOG.md to include a new release header for 1.0.25 to
match package.json: add a top-level section "## [1.0.25] - <release-date>" above
the existing "## [1.0.24] - 2026-04-04" entry and populate it with the
appropriate release notes (or a brief summary/placeholder if the full notes are
elsewhere) so the file versioning aligns with package.json.

---

Nitpick comments:
In `@package.json`:
- Around line 7-10: package.json's "bin.karpeslop" points to karpeslop-cli.js
while the build script emits karpeslop-bin.js, causing a mismatch between the
published CLI entrypoint and the built artifact; decide which artifact should be
the true CLI and make one of two fixes: either change "bin.karpeslop" to
"karpeslop-bin.js" so the published bin matches the build, or alter the "build"
script to output karpeslop-cli.js (or update the wrapper) so karpeslop-cli.js
runs the compiled code; ensure whichever file is used still invokes the
implementation in ai-slop-detector.ts (which contains
targetPaths/resolveTargetPaths and analyzePackageVersions) so the targeting and
package freshness logic is executed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ac69d49d-f229-4b78-a664-8e472b35d66e

📥 Commits

Reviewing files that changed from the base of the PR and between c88ccc4 and a390199.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • CHANGELOG.md
  • ai-slop-detector.ts
  • karpeslop-bin.js
  • package.json

Comment thread ai-slop-detector.ts
Comment thread ai-slop-detector.ts
Comment thread ai-slop-detector.ts Outdated
Comment thread ai-slop-detector.ts Outdated
Comment thread ai-slop-detector.ts
Comment thread CHANGELOG.md
Merge main (picomatch + brace-expansion bumps, prior PR #9) and
address inline PR review comments from chatgpt-codex-connector and
codeant-ai:

- Make package.json/package-lock.json reachable: extend findAllFiles
  to discover manifest files at root, and allow .json extension in
  resolveTargetPaths for the two manifest filenames. Previously the
  fresh_package_version rule was unreachable from both full scans
  and explicit CLI targeting.
- Add minPackageAgeDays validation in validateConfig — rejects
  non-numeric or negative values which previously produced NaN and
  silently disabled the age comparison.
- Rebuild karpeslop-bin.js from updated source so the compiled
  artifact also awaits analyzePackageVersions.
@CodeDeficient

Copy link
Copy Markdown
Owner Author

Resolved merge conflict with main and addressed the review comments in 8ad6176:

  • findAllFiles() now also picks up package.json/package-lock.json at the project root, and resolveTargetPaths() accepts .json for the two manifest filenames. The fresh_package_version rule is now reachable from both full scans and explicit targeting.
  • Added validation for minPackageAgeDays in validateConfig — non-numeric or negative values now throw at config load instead of silently disabling age checks.
  • Rebuilt karpeslop-bin.js from the updated source so the compiled artifact also awaits analyzePackageVersions.
  • Resolved the package-lock.json version/bin conflict in favor of our values (1.0.25 / karpeslop-cli.js) which match package.json.

PR is now MERGEABLE.

- getNpmPackageAge: wrap fetch in AbortController with 5s timeout so
  registry requests can't stall the scan indefinitely
- analyzePackageVersions: derive package name from pkgPath when
  pkgInfo.name is missing in package-lock entries
- CHANGELOG: add 1.0.25 release header to match package.json
@CodeDeficient

Copy link
Copy Markdown
Owner Author

Addressed the new CodeRabbit review comments in 7a3924c:

  • getNpmPackageAge now wraps the npm registry fetch in an AbortController with a 5s timeout so a slow/unresponsive registry can't stall the scan indefinitely
  • analyzePackageVersions now derives the package name from pkgPath when pkgInfo.name is missing in lockfile entries, so packages without a name field still get analyzed
  • CHANGELOG.md has a new ## [1.0.25] - 2026-06-09 section matching package.json

The other inline review items (manifest reachability, await on analyzePackageVersions, minPackageAgeDays validation) were already addressed in 8ad6176.

- Parallelize getNpmPackageAge calls in analyzePackageVersions via
  Promise.all so registry requests run concurrently instead of
  sequentially (was N x 5s in worst case)
- Cache the full time map from the registry response instead of just
  the looked-up version, so different versions of the same package
  hit cache rather than re-fetching megabytes of data
- Log a one-time warning when the npm registry can't be reached
  instead of silently swallowing network errors
- Defer detector construction past --help/--version so a bad
  .karpesloprc.json doesn't break help output
- Drop dead info.name branch in lockfile parsing: name is derived
  from pkgPath since the only entry that has info.name is the
  root which is already skipped
@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed 4937743. The scan now includes nested workspace manifests in both full scans and directory-targeted scans, and fresh-package findings are deduped by package/version so package.json + package-lock.json do not double-report the same dependency.

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed a86c719.

  • fresh_package_version dedupe is now file-scoped (filePath|pkgName|version) so separate manifests in a monorepo still report independently.
  • karpeslop-cli.js now propagates child exit signals instead of treating them as success, and the fallback node --import tsx path uses the same exit handling.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@karpeslop-bin.js`:
- Around line 437-446: isExcludedPath currently checks hardcoded segment names
and file suffixes but ignores this.customIgnorePaths, causing explicitly
configured ignores to be bypassed for positional file targets; update
isExcludedPath to also iterate this.customIgnorePaths and return true if any
custom pattern matches the computed relativePath (or matches a segment as done
by excludedSegment) so that custom ignores are honored for both full-project
discovery and direct targets (apply the same change to the other similar block
referenced around 490-497); use the existing symbols relativePath, segments,
excludedSegment and this.customIgnorePaths to locate where to add the
pattern-match check.
- Around line 413-415: detect() currently continues when explicit target paths
resolve to zero files; after calling this.resolveTargetPaths() and assigning
filesToAnalyze, check if filesToAnalyze.length === 0 and fail fast: log a clear
error mentioning the supplied target paths (this.targetPaths) and exit non‑zero
(e.g., process.exit(1) or throw an Error) instead of printing "Targeting 0
file(s)" and continuing. Update the block that contains the console.log call so
the zero-case triggers the failure path and returns a non‑success status for CI.
- Around line 1021-1025: The dedupe currently builds issueKey as
`${pkgName}|${version}` so reportedFreshPackageKeys suppresses findings across
different manifests; change the key generation in the block that computes
issueKey (where pkgName and version are used) to include a manifest-specific
identifier (e.g., manifest path or manifest id) — e.g.
`${manifestId}|${pkgName}|${version}` — and use that new key when checking and
adding to reportedFreshPackageKeys so deduping is limited to a single
manifest/workspace rather than the entire run.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 25567e7a-6817-4b91-a7bb-8d11dcd9f421

📥 Commits

Reviewing files that changed from the base of the PR and between a390199 and 4937743.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • CHANGELOG.md
  • ai-slop-detector.ts
  • karpeslop-bin.js
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • ai-slop-detector.ts

Comment thread karpeslop-bin.js
Comment thread karpeslop-bin.js Outdated
Comment thread karpeslop-bin.js Outdated
@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed 04f3ea1.

The fresh-package scan now preserves lockfile entry identity instead of keying package-lock entries only by package name. Each lockfile entry keeps its own source path plus package name/version, so workspace manifests and duplicate package names at different versions are reported separately.

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed 74a5e14.

  • Explicit file targets now honor .karpesloprc.json ignorePaths before any analysis runs.
  • Explicit targets outside the repo root are no longer dropped by root-relative exclusion logic.

Smoke-tested with:

  • an ignored explicit file inside the repo (Targeting 0 file(s))
  • an explicit file under /tmp (Targeting 1 file(s) and the scan ran)}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@karpeslop-cli.js`:
- Line 64: The primary child exit handler (handleChildExit) must be guarded so
it doesn't terminate the wrapper when a fallback is active: either remove
nodeChild.on('exit', handleChildExit) before starting the fallback or add a
guard inside handleChildExit to ignore exits from the original nodeChild when a
fallbackActive flag is true; update the code paths that start the fallback to
set fallbackActive (or otherwise mark which child is authoritative) and ensure
handleChildExit only acts on the currently authoritative child (reference
nodeChild, handleChildExit, and the new/updated
fallbackActive/authoritative-child marker).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d75a3c1d-dc9a-441c-83ce-14de4c487896

📥 Commits

Reviewing files that changed from the base of the PR and between 4937743 and 74a5e14.

📒 Files selected for processing (3)
  • ai-slop-detector.ts
  • karpeslop-bin.js
  • karpeslop-cli.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • karpeslop-bin.js
  • ai-slop-detector.ts

Comment thread karpeslop-cli.js
process.exit(1);
});

nodeChild.on('exit', handleChildExit);

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 | ⚡ Quick win

Guard primary child exit when fallback is active.

When ENOENT triggers fallback, the original child can still emit exit; handleChildExit at Line 70 may terminate the wrapper before nodeChild finishes, causing flaky fallback behavior and wrong final exit status.

Suggested fix
+let fallbackActive = false;
+
 function handleChildExit(code, signal) {
   if (signal) {
     process.exit(exitCodeForSignal(signal));
   }
   process.exit(code ?? 0);
 }
@@
 child.on('error', (err) => {
@@
   if (err.code === 'ENOENT') {
+    fallbackActive = true;
     const nodeChild = spawn(
       'node',
       ['--import', 'tsx', detectorPath, ...args],
       { stdio: 'inherit', cwd: process.cwd() }
     );
@@
     nodeChild.on('exit', handleChildExit);
   } else {
     process.exit(1);
   }
 });
 
-child.on('exit', handleChildExit);
+child.on('exit', (code, signal) => {
+  if (fallbackActive) return;
+  handleChildExit(code, signal);
+});

Also applies to: 70-70

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@karpeslop-cli.js` at line 64, The primary child exit handler
(handleChildExit) must be guarded so it doesn't terminate the wrapper when a
fallback is active: either remove nodeChild.on('exit', handleChildExit) before
starting the fallback or add a guard inside handleChildExit to ignore exits from
the original nodeChild when a fallbackActive flag is true; update the code paths
that start the fallback to set fallbackActive (or otherwise mark which child is
authoritative) and ensure handleChildExit only acts on the currently
authoritative child (reference nodeChild, handleChildExit, and the new/updated
fallbackActive/authoritative-child marker).

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed 8f6af27.

  • Root package.json / package-lock.json now respect ignorePaths in full scans and directory-target scans.
  • Explicit target scans now fail when resolution leaves no files to analyze instead of exiting 0.

Smoke-tested:

  • ignorePaths: ["package.json"] at repo root -> Found 0 files to analyze
  • karpeslop does-not-exist.ts -> exit code 1

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed b1199cd.

  • package-lock.json freshness scanning now supports legacy npm v6 / lockfile v1 dependencies trees, not just the newer packages map.
  • fresh_package_version is excluded from the KarpeSlop score so dependency-age warnings no longer inflate the Style bucket or total score.

Smoke-tested:

  • legacy lockfile with dependencies tree -> fresh_package_version reported once
  • same scan -> Style / Taste (Soul) : 0 pts, TOTAL KARPE-SLOP SCORE : 0 pts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
ai-slop-detector.ts (3)

1204-1206: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Modern lockfile scans still miss non-hoisted transitive packages.

Lines 1204-1206 drop every pkg.packages entry below the first node_modules/ level. In npm v2/v3 lockfiles, that's exactly where version-conflicted transitive deps live, so a fresh package that only resolves at node_modules/a/node_modules/b is never checked. Legacy v1 parsing walks nested dependencies, so the modern branch is currently less complete than the fallback.

Prefer collecting all pkg.packages entries and deduping by pkgName|version instead of excluding nested paths up front.

Also applies to: 1251-1257

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ai-slop-detector.ts` around lines 1204 - 1206, The code currently skips any
pkg.packages entries with nested "node_modules/" by checking pkgPath and
splitting on 'node_modules/', which omits non-hoisted transitive packages;
remove those early-continue checks and instead collect every pkg.packages entry,
compute a dedupe key like `${pkgName}|${version}` (using the package name and
resolved version from each pkg entry), track seen keys in a Set, and only
process a package if its key hasn't been seen; apply this change at both places
where pkgPath-based skipping occurs (the blocks referencing pkgPath and
pkg.packages) so nested node_modules entries are considered but final processing
is deduplicated by pkgName|version.

1296-1315: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Short-circuit remaining npm lookups after the first registry outage.

Once the fetch path starts failing, this method only suppresses duplicate warnings. Every later uncached package still pays its own 5s timeout, so an offline run against a large lockfile can stall for minutes even though the warning says freshness checks "will be skipped."

Suggested fix
   private npmPackageCache: Map<string, Record<string, string>> = new Map();
   private registryWarningLogged = false;
+  private registryUnavailable = false;

   private async getNpmPackageAge(pkgName: string, version: string): Promise<{ version: string; time: string; ageMs: number } | null> {
+    if (this.registryUnavailable) {
+      return null;
+    }
+
     const now = Date.now();
@@
     } catch {
+      this.registryUnavailable = true;
       if (!this.registryWarningLogged) {
         console.warn('⚠️  Could not reach npm registry to check package ages. fresh_package_version checks will be skipped.');
         this.registryWarningLogged = true;
       }
       return null;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ai-slop-detector.ts` around lines 1296 - 1315, The package-age fetch path
doesn't short-circuit after the first npm registry outage, causing repeated 5s
timeouts; add a persistent flag (e.g., this.registryUnavailable) and check it at
the start of the method that performs the fetch so you immediately return null
when set, and set this.registryUnavailable = true inside the catch block (in
addition to the existing this.registryWarningLogged update) so subsequent calls
skip the network lookup entirely; reference the existing
this.registryWarningLogged usage and the fetch block in the method to locate
where to add the early-return check and where to set the new flag.

573-577: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't bypass built-in exclusions for outside-root targets.

Lines 573-577 short-circuit all of the segment-based checks below, so explicit targets like ../other/dist/index.js, ../other/.git/hooks/*, or ../other/types/foo.ts are no longer filtered. resolveTargetPaths() uses this as its last gate, which means outside-repo scans can pull in generated/build artifacts and hidden files that the normal scan would skip.

Suggested fix
-    if (allowOutsideRoot && isOutsideRoot) {
-      return relativePath.endsWith('.d.ts') ||
-        relativePath.endsWith('ai-slop-detector.ts') ||
-        relativePath.endsWith('improved-ai-slop-detector.ts');
-    }
+    const candidatePath = (allowOutsideRoot && isOutsideRoot ? filePath : relativePath)
+      .replace(/\\/g, '/');

-    const segments = relativePath.split('/');
+    const segments = candidatePath.split('/');
     const excludedSegment = (name: string) => segments.includes(name);
     return excludedSegment('generated') ||
       excludedSegment('coverage') ||
       excludedSegment('.next') ||
       excludedSegment('node_modules') ||
       excludedSegment('dist') ||
       excludedSegment('build') ||
       excludedSegment('.git') ||
       excludedSegment('out') ||
       excludedSegment('temp') ||
       excludedSegment('types') ||
       segments.some(segment => segment.startsWith('.')) ||
-      relativePath.endsWith('.d.ts') ||
-      relativePath.endsWith('ai-slop-detector.ts') ||
-      relativePath.endsWith('improved-ai-slop-detector.ts');
+      candidatePath.endsWith('.d.ts') ||
+      candidatePath.endsWith('ai-slop-detector.ts') ||
+      candidatePath.endsWith('improved-ai-slop-detector.ts');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ai-slop-detector.ts` around lines 573 - 577, The current early return in the
allowOutsideRoot && isOutsideRoot branch (using relativePath.endsWith('.d.ts')
|| relativePath.endsWith('ai-slop-detector.ts') ||
relativePath.endsWith('improved-ai-slop-detector.ts')) bypasses the
segment-based exclusions used elsewhere and lets outside-root paths skip normal
filters; change this so that when allowOutsideRoot && isOutsideRoot you still
run the same segment-based checks that resolveTargetPaths() applies and only
then permit .d.ts or the detector files — i.e., remove the unconditional return
and instead combine the existing segment-filter logic with a final allowance of
those specific filenames so generated/build/hidden files are still excluded;
refer to allowOutsideRoot, isOutsideRoot, relativePath, and resolveTargetPaths()
to locate and integrate the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@ai-slop-detector.ts`:
- Around line 1204-1206: The code currently skips any pkg.packages entries with
nested "node_modules/" by checking pkgPath and splitting on 'node_modules/',
which omits non-hoisted transitive packages; remove those early-continue checks
and instead collect every pkg.packages entry, compute a dedupe key like
`${pkgName}|${version}` (using the package name and resolved version from each
pkg entry), track seen keys in a Set, and only process a package if its key
hasn't been seen; apply this change at both places where pkgPath-based skipping
occurs (the blocks referencing pkgPath and pkg.packages) so nested node_modules
entries are considered but final processing is deduplicated by pkgName|version.
- Around line 1296-1315: The package-age fetch path doesn't short-circuit after
the first npm registry outage, causing repeated 5s timeouts; add a persistent
flag (e.g., this.registryUnavailable) and check it at the start of the method
that performs the fetch so you immediately return null when set, and set
this.registryUnavailable = true inside the catch block (in addition to the
existing this.registryWarningLogged update) so subsequent calls skip the network
lookup entirely; reference the existing this.registryWarningLogged usage and the
fetch block in the method to locate where to add the early-return check and
where to set the new flag.
- Around line 573-577: The current early return in the allowOutsideRoot &&
isOutsideRoot branch (using relativePath.endsWith('.d.ts') ||
relativePath.endsWith('ai-slop-detector.ts') ||
relativePath.endsWith('improved-ai-slop-detector.ts')) bypasses the
segment-based exclusions used elsewhere and lets outside-root paths skip normal
filters; change this so that when allowOutsideRoot && isOutsideRoot you still
run the same segment-based checks that resolveTargetPaths() applies and only
then permit .d.ts or the detector files — i.e., remove the unconditional return
and instead combine the existing segment-filter logic with a final allowance of
those specific filenames so generated/build/hidden files are still excluded;
refer to allowOutsideRoot, isOutsideRoot, relativePath, and resolveTargetPaths()
to locate and integrate the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ea5218df-2e8f-4454-b354-59e4492cf617

📥 Commits

Reviewing files that changed from the base of the PR and between 74a5e14 and b1199cd.

📒 Files selected for processing (2)
  • ai-slop-detector.ts
  • karpeslop-bin.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • karpeslop-bin.js

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed 22c1a35.

  • Modern package-lock freshness scanning now includes nested packages below the first node_modules/ boundary.
  • Registry outages now short-circuit later uncached package checks after the first failure.
  • Explicit targets outside the repo root now still respect the normal generated/build/hidden exclusions.

Smoke-tested:

  • nested lockfile entry produced fresh-package findings for both the root and transitive entries
  • outage stub stopped after 5 fetches on a 20-package manifest
  • /tmp/outside/dist/foo.ts was rejected with the excluded-path warning and exit code 1

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed 89b0de9.

  • --quiet now still applies when [path...] is supplied, so explicit target scans keep the core-app-only filter.
  • Modern package-lock.json scans now include nested entries below node_modules/ and dedupe by package scope + name/version.
  • Registry outages stop dequeuing more package-age checks once the first failure is hit.

Smoke-tested:

  • karpeslop --quiet /tmp/karp-quiet filtered to the core app file only
  • nested lockfile entry under node_modules/parent/node_modules/lodash was reported
  • outage stub stopped after 5 fetches and completed without crashing

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Pushed d0314a5.

  • package-lock.json freshness scanning now skips workspace-local entries like packages/ui; only installed node_modules/ entries are checked.
  • Reports no longer show the CLEAN banner when the only findings are fresh_package_version warnings.

Smoke-tested:

  • workspace-only lockfile entry -> no freshness findings
  • fresh-only package.json scan -> prints Package freshness warnings only. Not counted in the KarpeSlop score. instead of CLEAN

@CodeDeficient

Copy link
Copy Markdown
Owner Author

Added mock-free behavior coverage in tests/ai-slop-detector.behavior.test.ts and a npm test script.

Covered:

  • quiet-mode path filtering for explicit targets
  • workspace-local lockfile entries vs registry-backed entries
  • fresh-only warnings not counting as a clean run

Also added a main-module guard so the source can be imported by tests without auto-running the CLI.

…ment-based exclusions, and parseVersionRange scope
@codeant-ai

codeant-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Jun 10, 2026
@codeant-ai

codeant-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown

CodeAnt AI Incremental review completed.

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

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

strict mode

1 participant