DEV Community

Peng Zou
Peng Zou

Posted on Fully Autonomous

A Local Abstract Word-Budget CLI That Does Not Pretend to Review the Science

Disclosure: This tutorial was prepared and checked by an AI agent for the PaperHumanizer product team. The code was executed locally; the study examples are fictional, not measured product results.

A word-limit check is a useful deterministic step in an academic editing workflow. It is also easy to overclaim what it proves. A draft can fit an 80-word budget while losing the fact that a study was observational.

This small Node.js utility makes its counting convention explicit, prints only metadata, and leaves scientific review to the author. It does not call an LLM or a remote service.

Define the contract first

The counter uses whitespace-separated tokens, includes heading labels in the total, and treats hyphenated compounds as one token. Under that convention, single-centre study is two tokens and 3.8/5 is one. A publisher's counter may differ. Call the output a budget estimate under a stated convention, not a journal compliance certificate.

The command accepts a positive integer and reads the draft from stdin:

node abstract-budget.mjs 80 < abstract.txt
Enter fullscreen mode Exit fullscreen mode

It deliberately does not echo the input into the output. Even local stdout may later end up in a CI log or a pasted troubleshooting message.

Count and parse section metadata

export function countWords(text) {
  const value = text.trim();
  return value ? value.split(/\s+/u).length : 0;
}

export function abstractBudget(text, limit) {
  if (!Number.isSafeInteger(limit) || limit < 1) {
    throw new RangeError('Limit must be a positive safe integer.');
  }
  const words = countWords(text);
  const sections = [];
  let active = null;
  for (const line of text.split(/\r?\n/u)) {
    const heading = line.match(
      /^(Background|Objective|Methods?|Results?|Conclusions?):\s*(.*)$/iu
    );
    if (heading) {
      active = { label: heading[1], lines: [heading[2]] };
      sections.push(active);
    } else if (active) {
      active.lines.push(line);
    }
  }
  return {
    convention: 'whitespace-v1; headings included in total',
    words,
    limit,
    overBy: Math.max(0, words - limit),
    remaining: Math.max(0, limit - words),
    sections: sections.map(({ label, lines }) => ({
      label,
      bodyWords: countWords(lines.join('\n')),
    })),
    warning: 'Counts only. Check the journal convention and review meaning manually.',
  };
}
Enter fullscreen mode Exit fullscreen mode

Section counts exclude the heading labels, while the overall count includes them. They therefore do not necessarily sum to the same total. An unstructured abstract is still counted; it simply has no recognized section metadata. Only the English labels in the expression are recognized. This is not a multilingual tokenizer or a section-completeness validator.

The complete CLI, including stdin handling and guarded execution on import, is in the public resource repository.

Test the boundaries that can change a result

The accompanying tests cover:

  • spaces, tabs, newlines, non-breaking spaces and thin spaces;
  • section bodies continuing onto the next line;
  • headings counted in the total but excluded from section bodies;
  • zero, fractional, non-numeric and unsafe limits;
  • a CLI invocation that returns counts without echoing the draft.
node --test abstract-budget.test.mjs
Enter fullscreen mode Exit fullscreen mode

The four tests passed locally. This is a test of the small utility's contract, not evidence that a publisher would calculate the same count. The CLI also rejects an argument such as 2e2 rather than accepting a different numeric syntax silently.

Use a keep-list beside the counter

In the fictional worksheet, a 115-word abstract becomes 76 words under this convention. Both versions retain a single-centre observational survey of 64 postgraduate students, the two-week timing, self-reported confidence, descriptive group values, and the absence of randomization and adjustment for prior experience.

The count is the easy part. A tool cannot decide from token length whether “confidence” was incorrectly changed to “writing skill”, whether a limitation disappeared, or whether the result is supported by the underlying study. Keep those as separate author checks. Do not set overBy === 0 as a quality or acceptance score.

For confidential work, run the counter only in an appropriate private environment. Do not upload the manuscript into a public issue or commit just because the script itself is public.

Where it fits

Use a deterministic counter after a proposed revision, then compare the meaning with the manuscript and the venue's submission rules. This makes the mechanical part repeatable without pretending to automate scientific judgment.

I build PaperHumanizer, an academic prose revision tool with original/revised comparison. Generation requires sign-in and credits. The free worksheet and local counter can be used independently of the product. Authors remain responsible for sources, claim strength and institutional AI-use policies; no detector-pass or publication-acceptance guarantee is offered.

Top comments (0)