Skip to content

feat(chat): chip recognised slash-commands anywhere in the message buffer - #1699

Merged
AbirAbbas merged 6 commits into
devfrom
task/highlight-slash-commands-anywher-1c4788
Oct 1, 2026
Merged

AbirAbbas merged 6 commits into
devfrom
task/highlight-slash-commands-anywher-1c4788

Conversation

@ZeroPoint95

@ZeroPoint95 ZeroPoint95 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Ready to review. All four open items from the first review are done, and a reviewer found a gap with waiting messages, which is now fixed too. CI is running on the newest commit, 68db4a0a2.

What changes

Every slash command codeaf recognises is now drawn as a chip, wherever it appears in the message: in the box, on home's line and in the transcript.

1699
try running /senior-dev on this   →  /senior-dev is chipped (it used to be plain)
explain /compact this             →  /compact is chipped, and enter sends it as ordinary words
/standing tag this                →  no change, /standing still routes
say /standing  + backspace        →  plain in the box, and still plain after sending

What stays the same

Pressing enter does exactly what it did before:

  1. A command at the start of the message runs.
  2. /standing, /orders and /task still act anywhere in the message.
  3. Any other command in the middle of a sentence is sent as text, even though it now has a chip.

So the chip used to mean "enter will act on this". Now it means "codeaf knows this command".

The three commits

  1. 745fc854c: chips anywhere. commandSpans chips any recognised command, and the transcript draws the same wider chips.
  2. 617ed0555: a tag backspaced to plain words stays plain once sent.
    • enterLine copies the backspaced ranges (plainTags) before the box is cleared, and passes them on to the transcript entry.
    • wrapWithOffsets records where each wrapped row starts, so transcriptCommandSpans can skip those ranges on every row. tabExpandedSegments adjusts the ranges when a tab is widened to spaces.
    • actedTags is gone, and plainTags replaces it.
    • Adds the change entry, and commands.md and screen.md now say a chip means "codeaf knows this command".
  3. 68db4a0a2: the same holds for a message typed while an answer is still coming. Such a message waits in a queue until the answer finishes. That queue used to drop the backspaced ranges, so the tag came back chipped.
    • Each waiting message now keeps its ranges: park stores them, and both sendParked and the background-conversation send pass them on.
    • Pressing ↑ to pull a waiting message back into the box keeps the tag plain there too.
    • plainWithoutTag handles the /standing-tag case: that path queues the words without the tag, so the ranges have to be moved to match.

Tests

  • The full go test ./internal/tui3 and ./internal/manual suites pass on 68db4a0a2, and go vet is clean.
  • 4 new tests: a waiting message stays plain, a recalled message stays plain, a backspaced tag beside a live /standing stays plain, and a table test for plainWithoutTag. The first three fail when the fix is removed.

Try it

  1. Run make build, then bin/codeaf.
  2. Ask something long. While the answer streams, type say /standing, press backspace, then enter.
  3. When the answer finishes and the message sends, /standing is plain, with no chip.

Trial binary: sha256 2744a5f6d27e6a8b1798be1b89ffadc17997c6a536a3f5b3f4da9dde55c00900, commit 68db4a0a2.

🤖 Generated with Claude Code

agentfield-bot and others added 3 commits September 30, 2026 17:27
…ffer

A recognised command now wears its chip wherever it stands — in the
composer, on home's line, and in the sent transcript — instead of only at
the head of the draft. "try running /senior-dev on this" chips
"/senior-dev" in the box and keeps the chip after the message is sent, and
so does every other command resolved through the table (aliases included).

The chip is now a recognition mark rather than a send promise: send is
unchanged, and only a leading command runs the dispatcher. The tag
machinery (liveTags, editTags, demoteTagBehindCaret, the refusal and the
hint) stays keyed on commandDoor, and the entry's acted tags are recorded
door-only.

A path like "/Users/santosh/notes.md" and an unknown word like "/nope"
stay plain, because neither resolves through knownCommand.

Tests updated in internal/tui3/slashchip_test.go, using /senior-dev as the
acceptance sentence.
The chip became a recognition mark in the previous commit, so every
recognised command is highlighted in the box and the sent message. One
promise was lost with the widening: a send-door tag backspaced to plain
words wore its chip again in the transcript, though the box and the manual
both say it stays plain.

The demoted ranges live in the editor's own rune coordinates and are
cleared by the send's reset before the entry is built, so enterLine
snapshots them and threads them through the submit doors to the entry's
plainTags. The transcript subtracts them against the pre-wrap text, with
each wrapped row rebased by its own offset — the wrap drops the spaces at
its breaks, so a whole-entry subtraction would mis-chip across rows. A
wrapped demotion is tested as well as an unwrapped one.

The actedTags field became plainTags, its door-only filter and comment are
gone, and commandSpans is collapsed to its shell guard plus a direct
recognizedCommandSpans return now that the filter can never fail.

The manual pages that still said a chip promised enter would act are
rewritten for recognition semantics, including screen.md's "a tinted
background is always a slash command that acts". The changelog entry for
PR 1699 records the invalidation.
Enter over a running answer parks the message before any submit runs, and
the parked record had nowhere to keep the ranges the person backspaced to
plain words, so the transcript chipped them again when the message went. A
parked message now carries those ranges: park takes them, sendParked and the
held conversation's draw pass them to the entry, and recalling the message
into the box restores them. The /standing tag's own road parks the words
without the tag, so plainWithoutTag carries the ranges into those words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ZeroPoint95
ZeroPoint95 marked this pull request as ready for review October 1, 2026 02:13
AbirAbbas and others added 3 commits October 1, 2026 11:43
…ept plain

A picked harness dropped the demoted ranges, so `say /standing` made plain
with backspace came back chipped once sent. The guard's send to the
conversation, a reopened conversation (or a second window on it), a follow-up
and a question's replacement or clarification built their entries with no
ranges at all, and drew a door word nobody acted on as a chip. A sentence
steered into a running answer and a message waiting for the answer drew no
chips at all, while the box and the transcript did.

The harness door now carries the ranges; the roads that never act on a
send-door tag draw a door word away from the head plain (restingDoorWords),
which is how every mid-sentence door word was drawn before; the steered row
and the waiting block paint through the transcript's painter. The tab stop
is one constant shared by both wrappers and the range mapper. The manual
and the change entry say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Compare slash command chips with the current conversation row in /resume,
which still uses the selected ground. Remove the two stale screen claims
that the model in use wears that ground, and describe its bold accent ink
with no band of its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AbirAbbas

Copy link
Copy Markdown
Collaborator

Taking over: @ZeroPoint95, checked by hand in the real binary on both roads (engine and chat --no-host, at 120 and 62 columns). Chips show in the box, on home's line and in the transcript. explain /compact this is sent as words, and /standing mid-sentence still routes. A backspaced tag stays plain through a direct send, ↑ recall and a cmd+enter waiting message. Paths, URLs, /modelx and (/model) stay plain.

Pushed:

  • b469e7311 tui3: covers the places a message is drawn that the PR missed. A send with a picked harness dropped the demoted ranges, so say /standing made plain came back chipped. The guard's "send to conversation", a reopened conversation or a second window on it, a follow-up, and a question's replacement or clarification all drew a door word nobody acted on as a chip. On those roads a door word away from the head now stays plain, as it did before. Plain enter while an answer streams now steers the sentence into the turn (the └ row), and that row drew no chips at all, so /compact was chipped in the box and plain once sent. The waiting block had the same gap. Both now paint through the transcript's painter. The tab stop is now one constant, and the manual and the change entry are updated to match.
  • 0597dff3d merges dev, which includes chat: the /model menu's current model wears no band and the picker owns the wheel #1696.
  • 7ab7bc6e3 docs(chat): since chat: the /model menu's current model wears no band and the picker owns the wheel #1696, the model in use in /model has no band. The chip section and screen.md no longer say it shares the chosen row's tint.

Left for later:

  • hi<TAB>/model is plain in the box but chipped once sent.
  • Spaced inline code (` /model `) and fenced code blocks chip commands.
  • A second window's replay draws /senior-dev plain, because the rows are cached before the program commands install.
  • The rows are scanned one at a time, so a hard-wrapped fragment can chip, and a command split across narrow rows loses its chip.
  • When chat: ctrl+enter queues a follow-up, and a click takes a queued message back #1714 lands, its recallQueuedAt should carry the demoted ranges, and its input.go conflict should keep plain (a.park(line, false, plain), a.submitShown(line, shownLine, plain)).

@AbirAbbas AbirAbbas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verified by hand in the real binary on both roads (120 and 62 columns). Merging.

@AbirAbbas
AbirAbbas merged commit 654cf26 into dev Oct 1, 2026
4 checks passed
@AbirAbbas
AbirAbbas deleted the task/highlight-slash-commands-anywher-1c4788 branch October 1, 2026 16:21
AbirAbbas added a commit that referenced this pull request Oct 1, 2026
Brings in #1699 (slash chips anywhere) and #1723. enterLine keeps this
branch's shape (no marked argument) and #1699's plain-tag snapshot:
park(line, false, plain), submitShown/submitImagesShown(..., plain).

A queued follow-up's own demoted ranges join #1699's resting door words on
its transcript line, so a slash word backspaced to plain before ctrl+enter
stays plain when its turn starts. A draft holding a live send-door tag is
a command, so ctrl+enter takes enter's road for it instead of queueing the
words and dropping the tag.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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