Skip to content

feat: TTY-Aware Output Rendering#20

Merged
sahil-noon merged 4 commits into
mainfrom
260613-kfcl-tty-aware-output-rendering
Jun 13, 2026
Merged

feat: TTY-Aware Output Rendering#20
sahil-noon merged 4 commits into
mainfrom
260613-kfcl-tty-aware-output-rendering

Conversation

@sahil-noon

Copy link
Copy Markdown
Collaborator

Meta

ID Type Confidence Plan Review
kfcl feat 3.5/5.0 10/10 tasks, 0/20 acceptance ✓ 1 cycle

Pipeline: intake ✓ → apply ✓ → review ✓ → hydrate ✓ → ship → review-pr

Impact: +814/−26 code (excluding fab/, docs/) · +1349/−49 total

Summary

idea ls and idea prune (dry-run) print each idea's full untruncated text, and ideas in this project are frequently paragraph-length. On a TTY these soft-wrap into many visual rows each, so the prune --force confirm hint scrolls off-screen and ls becomes unscannable. This change adds four TTY-aware DX features for the idea CLI to make list and prune output scannable on a terminal — all gated so piped/redirected output stays full and canonical, preserving the pipe-friendliness contract (Constitution VI). Display-only: the backlog format, parser, and --json schema are unchanged.

Changes

  • A — TTY-aware truncation, --full flag, and ls [id...] filter: On a TTY (unless --full), each idea's text portion is truncated to fit the terminal width with a ellipsis; the [id] date: prefix is never truncated. Truncation is rune-safe and clips multiline ideas at the first newline. idea ls gains a --full override and an optional variadic [id...] positional filter (unknown IDs warn on stderr and the rest still list). Non-TTY output is always full/canonical.
  • B — idea prune dry-run count header: A one-line N done idea(s) would be pruned … summary is printed to stderr before the list, so the action is the first thing a human sees while stdout still carries exactly the removable lines (2>/dev/null suppresses the header consistently).
  • D — Color / styling (TTY + NO_COLOR-gated): When stdout is a TTY and NO_COLOR is unset, the [id] date: prefix is dimmed and the done [x] checkbox is green; color is applied after truncation so width math counts visible runes. Plain output otherwise.
  • E — Interactive [y/N] confirm for idea prune: On a TTY without --force, prune lists the prunable ideas then prompts Prune N done idea(s)? [y/N] on stderr as the last line at the cursor — inherently un-buryable. --force still skips the prompt (script behavior unchanged); non-TTY never prompts (would hang a pipe) and falls back to today's dry-run.

Dependency: adds golang.org/x/term (pinned v0.27.0) as the first non-stdlib/cobra direct dependency, used for isatty + terminal-width detection in a new internal/idea/term.go seam (stdlib has no terminal-width primitive). Justified under the constitution's Dependency Discipline. The go directive is kept at 1.22 — v0.27.0 is the latest x/term that does not bump the minimum Go version.

See change folder: fab/changes/260613-kfcl-tty-aware-output-rendering/.

🤖 Generated with Claude Code

sahil87 added 2 commits June 13, 2026 13:47
Four display-only DX features, all TTY-gated to preserve the
pipe-friendliness contract (Constitution VI) — piped/redirected
output stays full and canonical:

- A: rune-safe text truncation with --full override and an
  optional 'idea ls [id...]' positional filter
- B: 'idea prune' dry-run stderr count header
- D: TTY + NO_COLOR-gated color (dim prefix, green [x])
- E: interactive [y/N] confirm for 'idea prune' (--force skips)

Adds golang.org/x/term (pinned v0.27.0) as the first non-stdlib/cobra
direct dependency for isatty + terminal width, justified under
Dependency Discipline; go directive kept at 1.22. Backlog format,
parser, and --json schema are unchanged.

Copilot AI 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.

Pull request overview

This PR adds TTY-aware rendering behavior to the idea CLI so ls/prune output remains scannable on terminals while preserving canonical, pipe-friendly output when stdout is redirected/piped.

Changes:

  • Introduces an internal/idea terminal seam (IsTTY, TermWidth, UseColor, DisplayListLine) supporting truncation, multiline clipping, and optional ANSI styling.
  • Updates idea list/ls to support --full and optional [id...] filtering, and routes list output through a shared TTY-aware renderer.
  • Updates idea prune to add a stderr count header in dry-run mode and a TTY-gated interactive [y/N] confirmation flow; adds/extends unit + integration tests.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/internal/idea/term.go New terminal seam: TTY detection, width resolution, NO_COLOR-gated ANSI styling, and display-line truncation logic.
src/internal/idea/term_test.go Unit tests for terminal width fallback, TTY detection, color helpers, and truncation behavior.
src/cmd/idea/output.go Shared printIdeaLines helper to centralize TTY-aware list/prune rendering policy.
src/cmd/idea/list.go Adds --full, optional [id...] filter, and uses shared output helper for TTY-aware rendering.
src/cmd/idea/prune.go Adds stderr count header, --full, and TTY-only interactive confirmation prior to deletion.
src/cmd/idea/main_test.go Expands CLI integration coverage for list ID filtering, piped canonical output, prune header/decision matrix, and confirm logic.
src/go.mod Adds direct dependency on golang.org/x/term v0.27.0.
src/go.sum Adds golang.org/x/term and indirect golang.org/x/sys checksums.
docs/memory/cli/structure.md Documents new terminal seam and shared renderer path; updates dependency notes.
docs/memory/cli/list.md New doc describing list TTY behavior, --full, [id...] filter, and pipe contract.
docs/memory/cli/prune.md Updates prune docs with stderr header + TTY confirm decision matrix and rendering behavior.
docs/memory/cli/index.md Adds list doc entry and updates descriptions (but has inconsistent “Last Updated” values).
fab/changes/260613-kfcl-tty-aware-output-rendering/intake.md Change intake/spec for the feature bundle.
fab/changes/260613-kfcl-tty-aware-output-rendering/plan.md Implementation plan and acceptance checklist.
fab/changes/260613-kfcl-tty-aware-output-rendering/.status.yaml Pipeline/status metadata for the change.
fab/changes/260613-kfcl-tty-aware-output-rendering/.history.jsonl Change history log.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +31 to +35
if tt.setEnv {
t.Setenv("COLUMNS", tt.columns)
} else {
os.Unsetenv("COLUMNS")
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — replaced os.Unsetenv("COLUMNS") with a t.Cleanup-based unsetEnvForTest helper that captures the prior value and restores it after the test, so the unset state no longer leaks. (bc17df0)

Comment on lines +73 to +77
// Not a TTY: always false, even with NO_COLOR unset.
os.Unsetenv("NO_COLOR")
if UseColor(f) {
t.Error("UseColor(non-tty) = true, want false (TTY gate)")
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — NO_COLOR is now unset via the same t.Cleanup-based unsetEnvForTest helper, capturing and restoring the prior value so it cannot leak into other tests. (bc17df0)

Comment thread src/internal/idea/term_test.go
Comment thread src/cmd/idea/list.go
Comment on lines +132 to +136
for _, id := range wantIDs {
if !found[id] {
fmt.Fprintf(cmd.ErrOrStderr(), "warning: no idea with ID %q\n", id)
}
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — filterByIDs now tracks warned IDs in a set so each missing ID is warned at most once, even when passed multiple times (e.g. idea ls zzzz zzzz). (bc17df0)

Comment thread src/cmd/idea/prune.go Outdated
Comment on lines 76 to 83
// Confirmed: perform the deletion via the same force path.
_, backfilled, err = idea.Prune(path, true)
if err != nil {
return err
}
printBackfillNotice(cmd, backfilled)
fmt.Printf("Pruned %d done idea(s).\n", len(pruned))
return nil

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — the confirmed idea.Prune(path, true) call now assigns its returned pruned slice, and the final count is taken from that slice rather than the earlier dry run, so the reported total is accurate even if the file changed in between. (bc17df0)

Comment thread docs/memory/cli/list.md Outdated
Comment thread docs/memory/cli/index.md Outdated
Comment on lines +10 to +13
| [edit](edit.md) | `idea edit` two-form contract: inline replacement (two-arg form) vs. $EDITOR round-trip (`edit <query>`) — editor resolution chain, temp-file mechanics, edge/exit semantics, and the fake-editor test seam | 2026-06-12 |
| [prune](prune.md) | Bulk-remove subcommand (`idea prune`): dry-run-by-default/`--force` contract, stdout/stderr channel split, exit codes, the deliberate non-archival design, and the `removeIdeaAt` seam shared with `Rm` | 2026-06-12 |
| [structure](structure.md) | Source tree layout (cmd/idea + internal/idea), root command factory, command aliases vs. the bare-text shorthand, backlog line lifecycle (lenient read / canonical write incl. the escaped-text convention for multiline ideas and the explicit `idea fmt` canonicalizer with bare-checkbox adoption), help-dump contract, and version stamping | 2026-06-12 |
| [list](list.md) | `idea list`/`ls` rendering contract: TTY-aware rune-safe text truncation, the `--full` flag, the optional `[id...]` positional filter, ANSI color (NO_COLOR-gated), and the pipe contract that keeps piped output canonical | — |
| [prune](prune.md) | Bulk-remove subcommand (`idea prune`): the TTY × `--force` decision matrix (pipe dry-run vs. interactive `[y/N]` confirm), the leading stderr count header, TTY-aware truncation/color/`--full` listing, stdout/stderr channel split, exit codes, the deliberate non-archival design, and the `removeIdeaAt` seam shared with `Rm` | 2026-06-12 |
| [structure](structure.md) | Source tree layout (cmd/idea + internal/idea), root command factory, command aliases vs. the bare-text shorthand, backlog line lifecycle (lenient read / canonical write incl. the escaped-text convention for multiline ideas and the explicit `idea fmt` canonicalizer with bare-checkbox adoption), the TTY/width/color/truncation seam (internal/idea/term.go) + shared printIdeaLines render path, the golang.org/x/term direct dependency, help-dump contract, and version stamping | 2026-06-12 |

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — regenerated the index via fab memory-index; list, prune, and structure now correctly show 2026-06-13 (their commit date in this PR), consistent with the generated-from-git-log contract. (bc17df0)

@sahil-noon sahil-noon marked this pull request as ready for review June 13, 2026 09:33
@sahil-noon sahil-noon merged commit 62f3235 into main Jun 13, 2026
2 checks passed
@sahil-noon sahil-noon deleted the 260613-kfcl-tty-aware-output-rendering branch June 13, 2026 09:33
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.

3 participants