Six items from gemini, claude initial review, and claude follow-up:
- turnstile.ts: Update misleading comment that claimed the metric filters
on `event=turnstile_siteverify` and `outcome=success`. The actual
Cloud Monitoring metrics in `monitoring/metrics/` filter on
`siteverifyDurationMs` field presence, which uniformly captures both
the parallel success log (event=turnstile_siteverify) and the four
rejectAndLog failure paths (event=pack_completed). The comment
contradicted README and YAML and would mislead future readers.
- turnstile.ts: Wrap rejectAndLog in a local `rejectWithDuration` helper
so every post-siteverify branch automatically carries
`siteverifyDurationMs`. Prevents drift if a fifth reject reason gets
added later.
- client.ts: Split the wire-protocol `PackProgressStage` (server-emitted
SSE values) from the display-only `DisplayProgressStage` superset that
adds `verifying`. Keeping the synthetic stage out of the wire type
prevents silent divergence with the server's `PackProgressStage`.
- usePackRequest.ts, TryItLoading.vue, TryItResult.vue: Switch the
display-side type to `DisplayProgressStage`. `onProgress` callbacks
still take the wire `PackProgressStage`.
- usePackRequest.ts: Clear `progressStage` on token-acquisition failure
branches (aborted / error). Functionally invisible since loading=false
hides the loading UI, but prevents the next submit's verifying flash
from briefly showing the previous run's stale state.
- monitoring/metrics/turnstile_siteverify_duration.yaml: Retune the
exponential bucket layout for the 100ms-1s SLO band where decisions
get made. Doubling buckets only placed ~3 boundaries between 100ms
and 1s; growthFactor=1.5 with scale=10 places ~8 boundaries there.
18 finite buckets cover 10ms to ~9.85s, comfortably above the 5s
siteverify timeout so timeouts don't land in overflow.
- monitoring/README.md: Document that pre-network rejections
(secret_missing, missing_token, token_too_long) intentionally don't
carry siteverifyDurationMs, so they're excluded from both metrics
but still appear in the existing pack_requests metric.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes targeting the visible "..." gap between Pack click and the
first SSE progress event observed after PR #1544 landed:
- Client: add a synthetic `verifying` PackProgressStage so the loading
UI displays "Verifying request..." while the server runs Turnstile
siteverify (typically 100-1000ms before the first 'cache-check' SSE
event arrives). The first onProgress callback from handlePackRequest
overwrites it with the real server-reported stage.
- Server: time the siteverify round-trip in `turnstileMiddleware` and
emit `siteverifyDurationMs` on every outcome (success / network
failure / rejected / action mismatch / hostname mismatch). Success
path adds a structured log with `event: turnstile_siteverify` so
Cloud Monitoring can build a log-based distribution metric for
p50/p95/p99 latency and alert on regressions during Cloudflare
incidents.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- useTurnstile: Make takeToken() one-shot under concurrency. Two callers
awaiting the same shared mintPromise both received the same token,
which siteverify rejects as `timeout-or-duplicate`. Claim the cache
atomically post-await and loop into a fresh mint if another caller won.
- usePackRequest: Drop pending pre-mint debounce timer at submitRequest
start and on unmount, and skip scheduling while loading is true. Stops
a debounce-firing-during-submit from minting an extra Turnstile
challenge alongside the click path's mint.
- TryItPackOptions: Emit userInput from option handlers and wire to
markUserTouched in TryIt. Without this, users hydrating via `?repo=`
who only tweak format/include patterns/checkboxes never tripped the
pre-mint gate, so their click path always cold-minted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intent(latency-and-counter): The Cloudflare Turnstile dashboard's "challenges issued" counter sits at roughly 1.25× of GA page_view, which means simply loading `api.js` on every visitor (the PR #1541 pre-warm) is already inflating the counter — render() and execute() are not the only trigger. At the same time, click→token latency is the main remaining UX cost. This PR reshapes pre-warm so script load + challenge happen only when the user has shown real intent (filled a valid URL or chose a file *and* interacted with the form), achieving both a lower counter and a near-zero click→token latency.
fix(useTurnstile-api): Replace the single `getToken()` entry point with a `preMintToken()` / `takeToken()` pair backed by an in-memory `{ token, mintedAt, consumed }` cache. `preMintToken()` runs the challenge in the background and stashes the resulting token; `takeToken()` consumes the cache synchronously (instant submit) or awaits the in-flight mint, falling back to a cold mint with the supplied AbortSignal. `invalidateCache()` lets the caller drop the cache without minting a new token. TTL is bounded at 240 s — Cloudflare hard-caps tokens at 300 s, the margin absorbs clock skew and network round-trips so a token that's "almost expired" is never sent to siteverify.
fix(useTurnstile-no-mount-prewarm): Stop calling `loadTurnstileScript()` from `setContainer()`. The mount-time script load was the source of the page-view-shaped counter inflation. Pre-warm now only runs from the intent-gated trigger in `usePackRequest`, so visitors who never interact with the form never appear on the dashboard.
fix(usePackRequest-intent-gate): Add a `userTouched` ref that flips on real user interaction (input event, file upload, mode switch) and never goes back. A debounced (500 ms) `watch(isSubmitValid && userTouched)` calls `preMintToken()`, so URL-parameter hydration (`?repo=`), browser form restoration, and autofill never pre-mint. `submitRequest()` switches from `getToken()` to `takeToken()` so the cached token is consumed on the first click, with the cold mint path as a transparent fallback.
fix(TryItUrlInput-user-input-event): Emit a new `userInput` event from the URL field's actual `@input` handler. `TryIt.vue` wires it to `markUserTouched()`. Watching `inputUrl` directly would have re-fired during onMounted hydration and defeated the gate.
learned(cloudflare-counter-includes-script-load): Even with `execution: 'execute'`, the Cloudflare Turnstile dashboard counts api.js loads toward "challenges issued" (verified by comparing GA page_view ≈ 106 with CF issued ≈ 132 in the same 30 min window after the PR #1541 deploy). Treat any new place that loads api.js as a billable analytics side effect.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment out the auto-execution block in TryIt.vue so pages loaded
with `?repo=...` no longer trigger an automatic pack request on
mount. URL parameter parsing and form reflection are preserved, so
the query still pre-fills the form — only the implicit submit is
suppressed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Always show the detail line with '...' as fallback when no message
is available, avoiding a layout jump when messages start arriving.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Main text stays as "Processing repository..." (same as main branch).
Stage and detailed messages (e.g., "Cloning repository...",
"Searching for files...") are shown below in smaller gray text.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Display stage message (e.g., "Processing files...") as the main
text, with the detailed pack() message (e.g., "Collecting files...")
shown below in smaller gray text. Long messages are truncated to
60 characters with ellipsis.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename CSS classes in TryItFileSelection.vue to match the new
character-based display: tokens-column → chars-column,
tokens-cell → chars-cell, file-tokens → file-chars.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove tokenCountTree option from website server to avoid expensive
token count calculation for all files. The file selection UI now uses
character counts (which are computed from content.length at no cost)
instead of token counts. Summary totalTokens and top files token counts
remain accurate as they are calculated independently.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Applebot and other JS-capable crawlers were visiting permalink URLs
(repomix.com/?repo=xxx), executing the frontend JS which auto-triggers
POST /api/pack on mount. This caused massive parallel git clone
operations that exceeded the 1024 MiB memory limit on Cloud Run,
resulting in OOM crash loops.
- Add server-side botGuardMiddleware using `isbot` package to reject
bot requests to /api/* with 403 before they consume resources
- Add frontend bot detection to skip auto-pack execution in onMounted
when the user agent is a known crawler
- Place bot guard before rate limiter to avoid counting bot requests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this flag, windows opened by the star button inherit sandbox
restrictions, preventing login and starring on the GitHub page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Restore GitHub icon in socialLinks for repo navigation
- Position star button after nav menu using CSS order
- Add scrolling="no" to prevent iframe vertical scrollbar
- Use var(--vp-nav-height) for parent height with flex centering
- Adjust padding and width for cleaner layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add title attribute for accessibility (WCAG 2.1 H64)
- Add sandbox="allow-scripts allow-popups" for iframe security
- Remove deprecated HTML attributes (frameborder, allowtransparency)
- Use v-if with matchMedia instead of CSS display:none to prevent
double iframe loading on mobile
- Restore GitHub icon in socialLinks for repo navigation
- Remove nav-screen-content-after slot (no longer needed with v-if)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the GitHub social link icon with an interactive GitHub star button
(via github-buttons) that displays the live star count. The button is shown
in the navbar on desktop and in the hamburger menu on mobile.
Also fix the server Dockerfile for local development by downgrading npm to
avoid the --min-release-age / --before conflict with Node 24.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename shadowed variable `result` to `suspiciousResult` in map callback
to avoid confusion with outer scope variable. Use composite key for v-for
loop on suspicious messages for better rendering stability.
When security check detects files containing potentially sensitive
information in zip uploads, show a warning section in the result metadata
panel listing the excluded files and detection reasons.
Define CliCommandPackOptions interface locally in cliCommand.ts instead of
importing PackOptions from usePackOptions.ts which depends on Vue module.
This prevents tsc from following the import chain to Vue in CI.
Address PR review comments:
- Add shell escaping for user-controlled values (repositoryUrl, includePatterns, ignorePatterns)
to prevent command injection when users copy-paste the generated command
- Skip --remote flag for uploaded file names by validating with isValidRemoteValue
- Add unit tests for generateCliCommand covering all option combinations
Display equivalent CLI command in result page to encourage npm usage.
The banner shows the npx command with all selected options, allowing
users to easily copy and run the same pack locally.
- Add generateCliCommand utility for consistent command generation
- Share command generation logic between result and error pages
- Style as prominent banner with gradient background
Add --split-output feature documentation to 12 non-English languages:
- German (de), Spanish (es), French (fr)
- Hindi (hi), Indonesian (id), Italian (it)
- Japanese (ja), Korean (ko), Portuguese-BR (pt-br)
- Russian (ru), Vietnamese (vi)
- Chinese Simplified (zh-cn), Chinese Traditional (zh-tw)
Each language includes updates to:
- command-line-options.md: --split-output CLI option
- configuration.md: output.splitOutput config option
- usage.md: New "Split Output" section with examples
Based on PR #1013 changes to English documentation.
- Change messages to direct CTAs: "Become a sponsor" and "Star this project"
- Add subtle underline to link text with hover effect
- Underline color transitions from gray to brand color on hover
Change spinner and text layout to horizontal with smaller spinner:
- Wrap spinner and text in flex container for horizontal alignment
- Reduce spinner size from 40px to 20px
- Add flex-shrink: 0 to prevent spinner from collapsing
Extract SupportMessage as a reusable component and display it during
pack processing. The component shows sponsor/star messages randomly,
matching the existing result screen behavior.
Add event.detail > 0 condition to handleClick to only trigger cancel on actual
mouse clicks, not on Enter key form submission. This prevents the pack button
from canceling requests when users press Enter during processing.
- Mouse clicks (event.detail > 0): trigger cancel when loading
- Enter key (event.detail === 0): allow normal form submission, no cancel
Improve type safety and code quality based on PR review comments:
- Convert PackButton.vue to TypeScript with proper type definitions
- Fix AbortSignal.reason comparison using strict equality instead of String()
- Extract magic number constant for API timeout in server configuration
These changes enhance type safety, improve code robustness, and increase
maintainability as suggested by automated code reviewers.
Implement cancel button for pack requests with improved user experience.
The button now shows "Cancel" on hover during processing and allows users
to abort ongoing requests. Also improved error messages for better clarity.
- Add cancel functionality to PackButton with hover state
- Implement request cancellation using AbortController
- Improve error messages for remote repository processing failures
- Update timeout handling and server configuration
- Add proper event handling to prevent form submission conflicts
- Add onAbort callback to distinguish timeouts from errors
- Implement errorType system to differentiate warnings from errors
- Update TryItResultErrorContent to show warning styling for timeouts
- Change timeout message to suggest using Include/Ignore Patterns
- Timeout now displays with yellow warning icon instead of red error
- Add oxlint as dev dependency and integrate into npm run lint
- Create oxlint configuration with warning levels for gradual adoption
- Add oxlint CI job to GitHub Actions
- Fix regex patterns flagged by oxlint:
- Remove unnecessary escape characters in file regex patterns
- Fix regex patterns in website validation and PHP test files
- Update lint script order: biome -> oxlint -> ts -> secretlint
oxlint provides 50-100x faster linting with 500+ rules from ESLint ecosystem.
Current warnings are configured as non-blocking to allow gradual improvement.
- Add try-catch around structuredClone with JSON fallback
- Fixes DataCloneError in environments where structuredClone fails
- Ensures compatibility across different browser environments
- Extract constants: FILE_SELECTION_WARNING_THRESHOLD and TabType to dedicated files
- Extract warning message to reusable FileSelectionWarning component
- Optimize performance: replace deep watch with shallow watch and use structuredClone
- Enhance accessibility: add comprehensive aria-label attributes to all interactive elements
- Strengthen error handling: add zero division protection for percentage calculation
- Improve code quality: enhance comments and maintain consistent type definitions
This refactoring improves code maintainability, performance, and user experience
while following Vue.js and accessibility best practices.
- Add local reactive state to avoid mutating props directly
- Use deep copying via watch to sync props.allFiles to localFiles
- Remove unnecessary @change handler since v-model handles changes
- This addresses Vue anti-pattern of mutating props identified by code review
Fixes the issue where the component was directly mutating the `selected`
property of files passed via props, which violates Vue's one-way data flow.
- Replace forEach loops with for...of loops for better performance
- Fix import ordering and spacing inconsistencies
- Simplify interface definitions and type declarations
- Add proper padding to tokens column in file selection table
- Remove unnecessary line breaks in computed properties
- Improve code readability and consistency across components
- Create PackIcon.vue component with configurable size prop
- Replace inline SVG in PackButton.vue with PackIcon component
- Replace inline SVG in TryItFileSelection.vue with PackIcon component
- Improve maintainability and consistency across pack-related UI elements
- Remove unused Package import from lucide-vue-next
- Convert file list to table format with clickable rows and compact layout
- Add warning message when selecting more than 500 files
- Increase include patterns limit from 1,000 to 100,000 characters
- Auto-switch to Result tab when Re-pack button is clicked
- Fix re-pack loading state to use shared loading status
- Sort files by token count instead of character count
- Replace Package icon with custom SVG for re-pack button
- Add row hover effects and full-row click selection
- Implement proper file selection state management during re-pack
Add tabbed navigation to switch between result content and file selection:
- Add tab navigation UI with Result and File Selection tabs
- Implement tab switching logic with proper state management
- Use v-show for performance optimization to maintain component state
- Add defensive form submission handling to prevent accidental packing
- Integrate file selection component with repack functionality
- Maintain clean separation between result display and file management
The tabbed interface improves UX by organizing content and preventing
accidental form submissions from UI interaction buttons.
Separates loading state (spinner + sponsor section) into a dedicated
component to prevent future conflicts:
- Create TryItLoading.vue with loading spinner and Warp sponsor section
- Update TryItResult.vue to use the new loading component
- Remove loading-related CSS from TryItResult.vue
- Improve component separation of concerns
This change isolates sponsor-related updates from core functionality
changes, reducing merge conflicts when sponsor content is updated.
Adds essential file selection capabilities while preserving the original
TryItResult design structure:
- Import FileInfo type and TryItFileSelection component
- Add TypeScript interfaces for Props and Emits
- Implement handleRepack function for file re-packing
- Add TryItFileSelection component after result content
- Maintain original styling and layout structure
This provides the core file selection feature without disrupting
the existing UI design patterns.
Reverts TryItResult.vue back to the original design to maintain
consistency with existing UI patterns. Removes the file selection
integration that was disrupting the component's original layout
and styling structure.
Updates Warp sponsor section with:
- New sponsor URL (go.warp.dev/repomix)
- Updated brand image from Warp's official assets
- Revised messaging emphasizing AI agent support
- Improved loading state layout with sponsor content first
Reverts the DEV/PROD logic change in API_BASE_URL back to the standard
pattern used throughout the codebase. The original change was
unintentional and inconsistent with project conventions.
The Warp sponsor section was accidentally removed from the loading state
in the file selection feature implementation. This commit restores the
sponsor section with proper styling to maintain the original UX during
repository processing.
- Add FileInfo interface for individual file metadata
- Extend PackResult to include allFiles array with complete file information
- Create TryItFileSelection component with checkboxes for each file
- Add bulk selection controls (Select All/Deselect All)
- Implement re-packing functionality for selected files only
- Add live statistics showing selected files and token counts
- Fix TypeScript configuration for proper import.meta.env support
- Add responsive design for mobile and desktop
- Include scrollable file list with proper overflow handling
Resolves the GitHub issue requesting checkboxes for file inclusion/exclusion
on the website UI. Provides tree-based selection interface after initial
packing process as suggested by maintainer.
# Conflicts:
# website/client/components/Home/TryItResult.vue