<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Viacheslav Bogdanov</title>
    <description>The latest articles on DEV Community by Viacheslav Bogdanov (@vvbogdanov).</description>
    <link>https://dev.to/vvbogdanov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2024241%2F51104694-e5e1-44f6-9e41-c386c638ed8c.jpg</url>
      <title>DEV Community: Viacheslav Bogdanov</title>
      <link>https://dev.to/vvbogdanov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vvbogdanov"/>
    <language>en</language>
    <item>
      <title>Preserving Context When Moving from ChatGPT to Codex CLI</title>
      <dc:creator>Viacheslav Bogdanov</dc:creator>
      <pubDate>Tue, 30 Jun 2026 07:48:26 +0000</pubDate>
      <link>https://dev.to/vvbogdanov/preserving-context-when-moving-from-chatgpt-to-codex-cli-bpl</link>
      <guid>https://dev.to/vvbogdanov/preserving-context-when-moving-from-chatgpt-to-codex-cli-bpl</guid>
      <description>&lt;p&gt;A lot of useful technical work starts as a conversation.&lt;/p&gt;

&lt;p&gt;Maybe you are exploring an architecture decision in ChatGPT. Maybe you are debugging an idea before touching the codebase. Maybe you are asking broad questions first, then moving into a local development workflow once the direction is clear.&lt;/p&gt;

&lt;p&gt;That handoff is often awkward.&lt;/p&gt;

&lt;p&gt;The context starts in ChatGPT, but the implementation happens somewhere else: a terminal, an editor, a local agent, or a repo-specific workflow.&lt;/p&gt;

&lt;p&gt;For me, that "somewhere else" is often Codex CLI.&lt;/p&gt;

&lt;p&gt;So I built a small bridge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx chatgpt2codex https://chatgpt.com/share/&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;chatgpt2codex&lt;/code&gt; imports a shared ChatGPT conversation into a local Codex CLI session attached to your current project directory.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/vv-bogdanov/chatgpt2codex" rel="noopener noreferrer"&gt;https://github.com/vv-bogdanov/chatgpt2codex&lt;/a&gt;&lt;br&gt;&lt;br&gt;
npm: &lt;a href="https://www.npmjs.com/package/chatgpt2codex" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/chatgpt2codex&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The workflow gap
&lt;/h2&gt;

&lt;p&gt;ChatGPT share links are useful for handing context to another person, but they are not directly useful to local tooling.&lt;/p&gt;

&lt;p&gt;If I have a long conversation that contains design notes, constraints, tradeoffs, and implementation ideas, I do not want to manually copy pieces of it into a new Codex thread.&lt;/p&gt;

&lt;p&gt;I want the local coding agent to resume from the conversation as if it had been part of the local workflow from the beginning.&lt;/p&gt;

&lt;p&gt;That is what this tool tries to do.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Given a public ChatGPT share URL, &lt;code&gt;chatgpt2codex&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reads the shared conversation&lt;/li&gt;
&lt;li&gt;normalizes the messages&lt;/li&gt;
&lt;li&gt;writes a Codex rollout JSONL session&lt;/li&gt;
&lt;li&gt;attaches the session to a project directory&lt;/li&gt;
&lt;li&gt;indexes the session so modern Codex builds can show it in resume flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, it attaches the imported session to the current directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx chatgpt2codex https://chatgpt.com/share/&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can target another project directory with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx chatgpt2codex https://chatgpt.com/share/&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /path/to/project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can preview the import without writing anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx chatgpt2codex https://chatgpt.com/share/&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A few practical details
&lt;/h2&gt;

&lt;p&gt;The tool is intentionally conservative.&lt;/p&gt;

&lt;p&gt;If a Codex session already exists for the target project directory, it exits with an error instead of overwriting anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;A Codex session already exists &lt;span class="k"&gt;for&lt;/span&gt; /path/to/project.
Use &lt;span class="nt"&gt;--force&lt;/span&gt; to replace it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do want to replace the existing imported session, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx chatgpt2codex https://chatgpt.com/share/&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also options for overriding the imported title and Codex home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx chatgpt2codex https://chatgpt.com/share/&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"Architecture discussion"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--codex-home&lt;/span&gt; ~/.codex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The slightly tricky part
&lt;/h2&gt;

&lt;p&gt;The first version wrote a Codex session file, but that was not always enough for Codex to pick it up in the resume flow.&lt;/p&gt;

&lt;p&gt;Modern Codex builds use both rollout JSONL files and local SQLite metadata. So the current release writes the session file and also indexes the thread in Codex's local &lt;code&gt;state_5.sqlite&lt;/code&gt; database when that database exists.&lt;/p&gt;

&lt;p&gt;It also uses Codex-visible session metadata, so the imported conversation appears as a normal CLI-originated thread rather than being filtered out.&lt;/p&gt;

&lt;p&gt;That was the main lesson: for local agent tools, "write the file" is often only half the integration. The rest is making sure the surrounding state agrees with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveat
&lt;/h2&gt;

&lt;p&gt;ChatGPT share pages and Codex local session files are not official public import APIs.&lt;/p&gt;

&lt;p&gt;Because of that, I kept the implementation small and pragmatic, with tests around the parts most likely to break:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;parsing shared ChatGPT conversations&lt;/li&gt;
&lt;li&gt;writing Codex session metadata&lt;/li&gt;
&lt;li&gt;detecting duplicate sessions for the same project directory&lt;/li&gt;
&lt;li&gt;replacing an existing session with &lt;code&gt;--force&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool requires Node.js 22.13.0 or newer because Codex's local SQLite index is important for the current workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I like this shape
&lt;/h2&gt;

&lt;p&gt;This is not a big framework or a new platform.&lt;/p&gt;

&lt;p&gt;It is just a small CLI tool that closes a specific gap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ChatGPT conversation -&amp;gt; shared URL -&amp;gt; &lt;span class="nb"&gt;local &lt;/span&gt;Codex session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is enough to make the handoff from exploration to implementation feel much smoother.&lt;/p&gt;

&lt;p&gt;If you use Codex CLI and sometimes start your thinking in ChatGPT, I would love to hear whether this fits your workflow.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/vv-bogdanov/chatgpt2codex" rel="noopener noreferrer"&gt;https://github.com/vv-bogdanov/chatgpt2codex&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openai</category>
      <category>npm</category>
      <category>ai</category>
      <category>cli</category>
    </item>
    <item>
      <title>Add a 50x+ faster duplicate-code gate to GitHub Actions with jscpd-rs</title>
      <dc:creator>Viacheslav Bogdanov</dc:creator>
      <pubDate>Thu, 04 Jun 2026 06:46:24 +0000</pubDate>
      <link>https://dev.to/vvbogdanov/add-a-50x-faster-duplicate-code-gate-to-github-actions-with-jscpd-rs-kml</link>
      <guid>https://dev.to/vvbogdanov/add-a-50x-faster-duplicate-code-gate-to-github-actions-with-jscpd-rs-kml</guid>
      <description>&lt;p&gt;Duplicate-code checks are useful, but they often become one more slow quality gate that teams run less often than they should.&lt;/p&gt;

&lt;p&gt;That trade-off is getting worse. Large teams already create repeated code through parallel feature work. AI coding agents make code generation even cheaper, which also makes accidental copy-paste cheaper to create. Reviewers still need deterministic checks that catch repetition before it settles into the codebase.&lt;/p&gt;

&lt;p&gt;This post shows how to add a fast duplicate-code gate to GitHub Actions with &lt;a href="https://github.com/vv-bogdanov/jscpd-rs" rel="noopener noreferrer"&gt;&lt;code&gt;jscpd-rs&lt;/code&gt;&lt;/a&gt;, a native Rust implementation of the common &lt;a href="https://github.com/kucherenko/jscpd" rel="noopener noreferrer"&gt;&lt;code&gt;jscpd&lt;/code&gt;&lt;/a&gt; workflow.&lt;/p&gt;

&lt;p&gt;If you only want the CI snippet, jump to the GitHub Actions workflow below and adjust the threshold and ignore list for your repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is jscpd?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;jscpd&lt;/code&gt; is a copy-paste detector for source code. It scans a project, finds duplicated fragments across files, writes reports for humans and CI systems, and can fail a build when the duplicated-line percentage crosses a configured threshold.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jscpd-rs&lt;/code&gt; keeps the familiar workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scan source trees from the CLI;&lt;/li&gt;
&lt;li&gt;load &lt;code&gt;.jscpd.json&lt;/code&gt; or &lt;code&gt;package.json#jscpd&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;generate console, JSON, SARIF, HTML, XML, CSV, Markdown, badge, and Xcode reports;&lt;/li&gt;
&lt;li&gt;fail CI on a threshold;&lt;/li&gt;
&lt;li&gt;expose the &lt;code&gt;jscpd&lt;/code&gt;, &lt;code&gt;jscpd-rs&lt;/code&gt;, and &lt;code&gt;jscpd-server&lt;/code&gt; command names.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is the hot path: file discovery, tokenization, matching, and reporting run natively in Rust. The npm package uses prebuilt platform binaries for Linux, macOS, and Windows. On those platforms, npm users do not need a Rust toolchain just to run the check. Unsupported platforms can install the CLI through Cargo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick local check
&lt;/h2&gt;

&lt;p&gt;Try it locally before putting it in CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; jscpd-rs &lt;span class="nt"&gt;--threshold&lt;/span&gt; 5 &lt;span class="nt"&gt;--exitCode&lt;/span&gt; 1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command scans the current project and exits with code &lt;code&gt;1&lt;/code&gt; if duplication is above 5%.&lt;/p&gt;

&lt;p&gt;The examples use &lt;code&gt;npx --yes&lt;/code&gt; so CI does not stop on an interactive package install prompt.&lt;/p&gt;

&lt;p&gt;For a more useful first run, write machine-readable reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; jscpd-rs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--threshold&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exitCode&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--reporters&lt;/span&gt; console,json,sarif,html &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; report &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore&lt;/span&gt; &lt;span class="s2"&gt;"node_modules/**"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore&lt;/span&gt; &lt;span class="s2"&gt;"dist/**"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore&lt;/span&gt; &lt;span class="s2"&gt;"coverage/**"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore&lt;/span&gt; &lt;span class="s2"&gt;"target/**"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important outputs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;terminal summary from &lt;code&gt;console&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;report/jscpd-report.json&lt;/code&gt; for scripts;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;report/jscpd-sarif.json&lt;/code&gt; for GitHub Code Scanning;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;report/html/&lt;/code&gt; for a browsable local report.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add it to GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/duplicate-code.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;duplicate-code&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jscpd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;security-events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v5&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run duplicate-code check&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npx --yes jscpd-rs \&lt;/span&gt;
            &lt;span class="s"&gt;--threshold 5 \&lt;/span&gt;
            &lt;span class="s"&gt;--exitCode 1 \&lt;/span&gt;
            &lt;span class="s"&gt;--reporters console,json,sarif \&lt;/span&gt;
            &lt;span class="s"&gt;--output report \&lt;/span&gt;
            &lt;span class="s"&gt;--ignore "node_modules/**" \&lt;/span&gt;
            &lt;span class="s"&gt;--ignore "dist/**" \&lt;/span&gt;
            &lt;span class="s"&gt;--ignore "coverage/**" \&lt;/span&gt;
            &lt;span class="s"&gt;--ignore "target/**" \&lt;/span&gt;
            &lt;span class="s"&gt;.&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload SARIF&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/codeql-action/upload-sarif@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;sarif_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;report/jscpd-sarif.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not use GitHub Code Scanning, remove the &lt;code&gt;security-events: write&lt;/code&gt; permission and the &lt;code&gt;Upload SARIF&lt;/code&gt; step.&lt;/p&gt;

&lt;p&gt;The duplicate-code step is still the gate. The SARIF upload only makes findings visible in the GitHub Security tab and code-scanning UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a config file for real projects
&lt;/h2&gt;

&lt;p&gt;For a real repository, I prefer moving the policy into &lt;code&gt;.jscpd.json&lt;/code&gt; and keeping the CI command short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minLines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reporters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sarif"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dist/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"coverage/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"target/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;".next/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"generated/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"**/*.snap"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gitignore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"noTips"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the workflow step becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run duplicate-code check&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx --yes jscpd-rs .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is easier to maintain because the threshold, ignored paths, and reporters live with the code-quality policy instead of being hidden inside YAML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a threshold
&lt;/h2&gt;

&lt;p&gt;Start with a threshold that does not block the whole team on day one.&lt;/p&gt;

&lt;p&gt;For existing codebases, I usually recommend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the tool without failing CI and inspect the report.&lt;/li&gt;
&lt;li&gt;Ignore generated files, build output, snapshots, and vendored code.&lt;/li&gt;
&lt;li&gt;Set the threshold slightly above the current duplicated percentage.&lt;/li&gt;
&lt;li&gt;Ratchet it down over time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a new project, a stricter threshold is reasonable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; jscpd-rs &lt;span class="nt"&gt;--threshold&lt;/span&gt; 3 &lt;span class="nt"&gt;--exitCode&lt;/span&gt; 1 src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point is not to delete every repeated line immediately. The point is to stop new accidental duplication from entering silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why speed matters
&lt;/h2&gt;

&lt;p&gt;Slow checks are the first checks teams disable, move to nightly jobs, or run only before releases. That is exactly the wrong place for duplicate-code detection: copy-paste is cheapest to fix when the pull request is still fresh.&lt;/p&gt;

&lt;p&gt;The current public benchmark suite for &lt;code&gt;jscpd-rs&lt;/code&gt; uses pinned React, Next.js, and Prometheus revisions and compares against upstream &lt;code&gt;jscpd&lt;/code&gt; with the same high-level inputs and options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Case&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;jscpd-rs avg&lt;/th&gt;
&lt;th&gt;upstream jscpd avg&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React &lt;code&gt;f0dfee3&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;0.197325s&lt;/td&gt;
&lt;td&gt;10.413453s&lt;/td&gt;
&lt;td&gt;52.77x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js &lt;code&gt;2bbb67b9&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;0.270786s&lt;/td&gt;
&lt;td&gt;14.983243s&lt;/td&gt;
&lt;td&gt;55.33x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prometheus &lt;code&gt;a0524ee&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;0.083162s&lt;/td&gt;
&lt;td&gt;4.842499s&lt;/td&gt;
&lt;td&gt;58.23x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those numbers are not a guarantee that every repository will see the same ratio. They are a public baseline for the current release gate: large enough to be useful, pinned enough to be reproducible, and focused on the kind of check you would actually run in CI.&lt;/p&gt;

&lt;p&gt;The benchmark table is also published in the &lt;a href="https://github.com/vv-bogdanov/jscpd-rs#performance" rel="noopener noreferrer"&gt;README performance section&lt;/a&gt;, along with the release-candidate command used to rerun the public suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compatibility model
&lt;/h2&gt;

&lt;p&gt;The goal is practical compatibility with upstream &lt;code&gt;jscpd&lt;/code&gt;, not a new tool with similar-looking output.&lt;/p&gt;

&lt;p&gt;For the current 0.x releases, the compatibility gate is coverage-first: on the same inputs and options, &lt;code&gt;jscpd-rs&lt;/code&gt; must not miss duplicated source lines reported by upstream &lt;code&gt;jscpd&lt;/code&gt;. Extra Rust findings are visible in compatibility reports and treated as follow-up work when they are noisy.&lt;/p&gt;

&lt;p&gt;That model is useful for CI adoption because missing real duplicated ranges is the dangerous failure mode. Exact clone-pair identity can differ, especially for multi-way clones, while still covering the same duplicated source ranges.&lt;/p&gt;

&lt;p&gt;There are also intentional first-release limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dynamic npm reporters, stores, listeners, and plugins are not loaded;&lt;/li&gt;
&lt;li&gt;HTML output is practical and self-contained, not pixel-perfect;&lt;/li&gt;
&lt;li&gt;exact token totals may differ from upstream;&lt;/li&gt;
&lt;li&gt;this is a native CLI and Rust library, not a JavaScript package API clone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  npm or Cargo?
&lt;/h2&gt;

&lt;p&gt;Use npm when Node is already part of your workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; jscpd-rs
jscpd &lt;span class="nt"&gt;--threshold&lt;/span&gt; 5 &lt;span class="nt"&gt;--exitCode&lt;/span&gt; 1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run it without a global install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; jscpd-rs &lt;span class="nt"&gt;--threshold&lt;/span&gt; 5 &lt;span class="nt"&gt;--exitCode&lt;/span&gt; 1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use Cargo when Rust is the natural toolchain for the project or when npm prebuilt binaries are not available for your platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;jscpd-rs &lt;span class="nt"&gt;--locked&lt;/span&gt;
jscpd &lt;span class="nt"&gt;--threshold&lt;/span&gt; 5 &lt;span class="nt"&gt;--exitCode&lt;/span&gt; 1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/vv-bogdanov/jscpd-rs" rel="noopener noreferrer"&gt;https://github.com/vv-bogdanov/jscpd-rs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href="https://www.npmjs.com/package/jscpd-rs" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/jscpd-rs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;crates.io: &lt;a href="https://crates.io/crates/jscpd-rs" rel="noopener noreferrer"&gt;https://crates.io/crates/jscpd-rs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;docs.rs: &lt;a href="https://docs.rs/jscpd-rs" rel="noopener noreferrer"&gt;https://docs.rs/jscpd-rs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;User guide: &lt;a href="https://github.com/vv-bogdanov/jscpd-rs/blob/main/docs/user-guide.md" rel="noopener noreferrer"&gt;https://github.com/vv-bogdanov/jscpd-rs/blob/main/docs/user-guide.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Migration notes: &lt;a href="https://github.com/vv-bogdanov/jscpd-rs/blob/main/docs/migrating-from-jscpd.md" rel="noopener noreferrer"&gt;https://github.com/vv-bogdanov/jscpd-rs/blob/main/docs/migrating-from-jscpd.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Longer launch note: &lt;a href="https://vv-bogdanov.github.io/posts/fast-duplicate-code-detection-for-agents/" rel="noopener noreferrer"&gt;https://vv-bogdanov.github.io/posts/fast-duplicate-code-detection-for-agents/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would especially like feedback on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repositories where &lt;code&gt;jscpd-rs&lt;/code&gt; misses duplicates found by upstream &lt;code&gt;jscpd&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;report compatibility issues in JSON, SARIF, HTML, XML, CSV, or Markdown;&lt;/li&gt;
&lt;li&gt;npm install friction on Linux, macOS, or Windows;&lt;/li&gt;
&lt;li&gt;public benchmark cases that represent real monorepos;&lt;/li&gt;
&lt;li&gt;formats where generic tokenization is too noisy or not sensitive enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If duplicate-code checks are currently too slow to keep in every pull request, try running this once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; jscpd-rs &lt;span class="nt"&gt;--threshold&lt;/span&gt; 5 &lt;span class="nt"&gt;--exitCode&lt;/span&gt; 1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should be enough to see whether the check is cheap enough for your normal CI loop.&lt;/p&gt;

</description>
      <category>npm</category>
      <category>performance</category>
      <category>devops</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
