Skip to content

fix(formatter): omit null/undefined and indent array items in plain output#92

Merged
amondnet merged 5 commits into
mainfrom
amondnet/agent-mode
Jul 6, 2026
Merged

fix(formatter): omit null/undefined and indent array items in plain output#92
amondnet merged 5 commits into
mainfrom
amondnet/agent-mode

Conversation

@amondnet

@amondnet amondnet commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the plain-text formatter to skip null/undefined fields, use "- " item markers with aligned indentation for arrays, and properly indent nested objects. Also corrects an inline comment about TOON token-savings from an unreproducible 58.9% claim to a measured ~37% (using o200k/cl100k tokenizers on TaskView-shaped list data; ~6% savings observed on long-text detail views).

Changes

  • fix(formatter): omit null/undefined fields in plain-text output; use "- " markers with aligned indentation for array items; fix nested object indentation
  • docs(formatter): correct TOON token-savings claim in comment to measured ~37% (was an unreproducible 58.9%)

Related issue

None found — no issue linked.

Test Plan

  • All 561 tests pass (bun test)
  • Lint is clean (bun run lint)

Checklist

  • PR title follows Conventional Commits
  • Tests added or updated, and the suite passes (bun run test)
  • Lint/format pass (bun run lint)
  • Documentation updated if behavior changed
  • No breaking change, or a BREAKING CHANGE: note is included

Summary by cubic

Fix the plain-text formatter to omit null/undefined fields, mark array items with "- " and indent nested structures, and align continuation lines for multiline strings (including in arrays). Also updates the TOON comment to ~37% token savings vs JSON (was 58.9%).

  • Bug Fixes
    • Arrays use "- " markers; nested arrays/objects indent consistently.
    • Continuation lines of multiline strings align under their key or item marker.
    • Skip empty array/object bodies to avoid stray blank lines.

Written for commit b1284e7. Summary will update on new commits.

Summary by CodeRabbit

  • Documentation

    • Updated formatting comments to reflect a more accurate token-savings estimate.
  • Bug Fixes

    • Improved plain-text output formatting for nested data structures.
    • Arrays now render with clearer item markers and aligned continuation lines.
    • Objects now skip fields with empty values and indent nested content more consistently.
  • Tests

    • Added coverage for omitted empty fields and the updated multiline formatting behavior.

amondnet added 2 commits July 6, 2026 21:52
…utput

Plain format printed literal "undefined"/"null" for empty fields and
only indented the first key of each array item, collapsing item
boundaries. Items now use a "- " marker with aligned continuation
lines, nested objects indent under their parent key, and empty fields
are omitted (json format still keeps null for scripting).
The 58.9% figure was not reproducible; benchmarking real TaskView list
output (10-200 rows) with o200k/cl100k tokenizers measures ~37% vs
JSON, and ~6% on detail views dominated by long free text.
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
asana Ready Ready Preview, Comment Jul 6, 2026 1:10pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The plain-text output formatter was reworked to use indentation-aware helper functions, changing array rendering to use "- " markers and omitting object properties with undefined or null values. TOON documentation comments were updated with revised token-savings figures. Tests were added to cover the new formatting behavior.

Changes

Plain Formatter Rewrite

Layer / File(s) Summary
Plain formatter implementation and docs
src/utils/formatter.ts
formatPlain now dispatches to indentation-aware formatPlainArray/formatPlainObject/formatPlainScalar helpers; arrays render with "- " markers and aligned continuations; objects omit undefined/null values; TOON JSDoc comments updated with revised token-savings estimates.
Formatter test coverage
test/utils/formatter.test.ts
Adds tests verifying omission of undefined/null object properties and exact multiline indentation for arrays of objects and nested objects; updates TOON tab-delimiter token-savings comment to ~37%.

Estimated code review effort: 2 (Simple) | ~12 minutes

Poem

A rabbit hopped through nested keys,
Trimming nulls with careful ease,
Dashes marking each array line,
Indents aligned, the output fine,
Tests confirm the format's new decree. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main formatter changes: omitting null/undefined values and indenting array items in plain output.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch amondnet/agent-mode

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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the plain text formatter in src/utils/formatter.ts to support proper indentation for nested objects and arrays, and to omit null and undefined values. It also updates the documentation and tests accordingly. The review feedback correctly identifies a bug where nested arrays are improperly formatted as objects because typeof item === 'object' evaluates to true for arrays, and suggests adding a corresponding test case to verify nested array formatting.

Comment thread src/utils/formatter.ts
Comment thread test/utils/formatter.test.ts

@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 `@src/utils/formatter.ts`:
- Around line 148-155: The nested array/object handling in formatter.ts
currently pushes the result of formatPlainArray and formatPlainObject even when
they return an empty string, creating stray blank lines after the key label.
Update the logic in the Array.isArray(value) and typeof value === 'object'
branches to check the formatted body before pushing it, so empty arrays/objects
only emit the key line and nothing else. Keep the fix localized around the
formatter flow that uses formatPlainArray, formatPlainObject, and keyLabel.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 04b3fda1-c4b5-4b11-a6ef-4fce69af92ba

📥 Commits

Reviewing files that changed from the base of the PR and between f4e36a4 and 9e7e5e0.

📒 Files selected for processing (2)
  • src/utils/formatter.ts
  • test/utils/formatter.test.ts

Comment thread src/utils/formatter.ts
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR rewrites the plain-text formatter to produce cleaner human-readable output: null/undefined fields are omitted, arrays render with - markers and aligned continuation lines, nested objects are indented consistently, and multiline strings are aligned under their key. It also corrects a comment about TOON token savings from an unverifiable 58.9% to a measured ~37%.

  • Formatter logic (formatter.ts): formatPlainArray and formatPlainObject now receive an indent accumulator; the body.slice(itemIndent.length) trick strips the leading indent from the first line and replaces it with the - marker, keeping subsequent lines aligned. Null/undefined guard is added at the top of the object loop; an empty-body guard prevents spurious blank lines.
  • Test coverage (formatter.test.ts): 7 new focused tests verify null/undefined omission, array markers, nested arrays, nested objects, multiline string alignment, and the empty-array/object blank-line fix introduced in a prior iteration.

Confidence Score: 5/5

Safe to merge — the formatter changes are self-contained, well-covered by tests, and do not affect TOON or JSON output paths.

The recursive indent-accumulation approach is correctly implemented: body.slice(itemIndent.length) reliably strips the leading whitespace prefix because formatPlainObject/formatPlainArray always prefix their output with exactly the indent string passed in. The null/undefined guard fires before any branching. The empty-body guard from the previous review iteration is present and tested. Seven new snapshot-style tests pin the exact line-by-line output for every significant case introduced.

No files require special attention.

Important Files Changed

Filename Overview
src/utils/formatter.ts Formatter rewritten to pass indent context through recursive calls; null/undefined omission, "- " markers, and multiline alignment all work correctly for the covered cases. Logic is sound and tests confirm the expected output shapes.
test/utils/formatter.test.ts Seven new plain-format tests added; each pins exact line-by-line output with split('\n') + toEqual, giving strong regression coverage for the new formatting behaviour.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["formatPlain(data, colors)"] --> B{Array.isArray?}
    B -->|yes| C["formatPlainArray(data, colors, '')"]
    B -->|no| D{typeof object && not null?}
    D -->|yes| E["formatPlainObject(data, colors, '')"]
    D -->|no| F["String(data)"]

    C --> G["map each item with itemIndent = indent + '  '"]
    G --> H{item type?}
    H -->|Array| I["formatPlainArray(item, colors, itemIndent)\nbody.slice(itemIndent.length)\nreturn indent + '- ' + stripped"]
    H -->|Object non-null| J["formatPlainObject(item, colors, itemIndent)\nbody.slice(itemIndent.length)\nreturn indent + '- ' + stripped"]
    H -->|Scalar| K["formatPlainScalar → split on newline, join newline+indent+'  '\nreturn indent + '- ' + scalar"]

    E --> L["for each key/value entry"]
    L --> M{value null/undefined?}
    M -->|yes| N["skip (continue)"]
    M -->|no| O{value type?}
    O -->|Array| P["formatPlainArray(value, colors, indent+'  ')\npush key: then body if non-empty"]
    O -->|Object| Q["formatPlainObject(value, colors, indent+'  ')\npush key: then body if non-empty"]
    O -->|Scalar| R["formatPlainScalar → split newline join newline+indent+'  '\npush indent+key: +scalar"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["formatPlain(data, colors)"] --> B{Array.isArray?}
    B -->|yes| C["formatPlainArray(data, colors, '')"]
    B -->|no| D{typeof object && not null?}
    D -->|yes| E["formatPlainObject(data, colors, '')"]
    D -->|no| F["String(data)"]

    C --> G["map each item with itemIndent = indent + '  '"]
    G --> H{item type?}
    H -->|Array| I["formatPlainArray(item, colors, itemIndent)\nbody.slice(itemIndent.length)\nreturn indent + '- ' + stripped"]
    H -->|Object non-null| J["formatPlainObject(item, colors, itemIndent)\nbody.slice(itemIndent.length)\nreturn indent + '- ' + stripped"]
    H -->|Scalar| K["formatPlainScalar → split on newline, join newline+indent+'  '\nreturn indent + '- ' + scalar"]

    E --> L["for each key/value entry"]
    L --> M{value null/undefined?}
    M -->|yes| N["skip (continue)"]
    M -->|no| O{value type?}
    O -->|Array| P["formatPlainArray(value, colors, indent+'  ')\npush key: then body if non-empty"]
    O -->|Object| Q["formatPlainObject(value, colors, indent+'  ')\npush key: then body if non-empty"]
    O -->|Scalar| R["formatPlainScalar → split newline join newline+indent+'  '\npush indent+key: +scalar"]
Loading

Reviews (3): Last reviewed commit: "chore(formatter): indent multiline scala..." | Re-trigger Greptile

Comment thread src/utils/formatter.ts
- handle nested arrays in plain format via Array.isArray branch
  (gemini: nested arrays were rendered as index-keyed objects)
- skip empty array/object bodies to avoid stray blank lines
  (coderabbit/greptile: empty collections emitted a blank line)
- add regression tests for both cases
@amondnet

amondnet commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@cubic-dev-ai cubic-dev-ai 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.

1 issue found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/utils/formatter.ts">

<violation number="1" location="src/utils/formatter.ts:129">
P2: Nested arrays are incorrectly formatted as objects here. Since `typeof [] === 'object'` is `true` in JavaScript, any array element that is itself an array will be passed to `formatPlainObject`, which iterates over its numeric indices as keys. Add an `Array.isArray(item)` check before the object check to recurse correctly:

```ts
if (Array.isArray(item)) {
  const body = formatPlainArray(item, colors, itemIndent)
  return `${indent}- ${body.slice(itemIndent.length)}`
}
if (typeof item === 'object' && item !== null) {
  ...
}
```</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/utils/formatter.ts

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the plain text formatting utility to support proper indentation for nested arrays and objects, prefix array items with a dash, and omit null or undefined values. It also updates the documentation and tests to reflect these changes. The reviewer suggested adding a test case to verify that multiline strings are correctly indented in plain format, which is a valid improvement for test integrity.

Comment thread test/utils/formatter.test.ts
… output

Continuation lines of multiline scalar values now align under their
key (gemini review suggestion, with the behavior implemented alongside
the requested regression test)
@amondnet

amondnet commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the plain text formatter in src/utils/formatter.ts to support proper nested indentation, omit null/undefined values, and handle multiline strings. It also updates the documentation and tests regarding TOON format token savings. Feedback was provided regarding a missing indentation alignment for multiline strings when they are elements of an array.

Comment thread src/utils/formatter.ts Outdated
Continuation lines of multiline strings in array items now align under
the `- ` marker content, matching the object-value behavior (gemini
review suggestion, with regression test)
@amondnet

amondnet commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the plain text formatting logic in src/utils/formatter.ts to support proper indentation, item markers, omission of null/undefined values, and multiline strings, along with adding comprehensive unit tests. The reviewer suggested adding a test case to ensure Date objects are correctly formatted as ISO strings in plain text format rather than being treated as empty objects.

Comment thread test/utils/formatter.test.ts
@amondnet amondnet merged commit cd80c36 into main Jul 6, 2026
16 checks passed
@amondnet amondnet deleted the amondnet/agent-mode branch July 6, 2026 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant