Agent ergonomics: shorthands, batch writes, schema refresh, and a drift-guarded skill doc - #1
Merged
Merged
Conversation
The workflow watched `main`, which this repo does not have, so push builds never ran — only pull_request ones did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mcli is driven by LLM agents in steady state, where INTERNAL reads as "mcli broke, retry" while USAGE reads as "your call was wrong, fix it". Cobra's validation failures arrived unclassified and exited 5, inviting agents to retry calls that can never succeed. Flag *parse* failures now route through a FlagErrorFunc, and the checks cobra runs after parsing — which bypass that func entirely — are matched on their message markers: argument counts, flag groups, and MarkFlagRequired violations. The last is the most common malformed call mcli sees, since nearly every item and column command requires --board. Namespace commands also reject unknown subcommands instead of printing help and exiting 0, so `mcli item lst` no longer looks like a success. Adds errs.AllCodes so docs can be verified against the real set of codes, and drops CodeConflict, which was specified but never emitted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Writing an item previously meant looking up a column ID and hand-encoding monday's raw column JSON. Shorthands (--status, --date/--due, --number, --text, --checkbox) address the board's single column of that type and are validated before anything is sent. Where a board has 0 or 2+ columns of a type, the command fails with USAGE and names the candidates rather than guessing. Passing `-` in place of --name/<id> reads rows from stdin (JSON array or one object per line, max 500) and writes them in one request against monday's per-minute complexity budget instead of N. Every row is validated up front, so a bad label fails the batch without a partial write, and the response reports errors[].index so only the failed rows are retried — item creation has no dedupe key, so re-sending the whole batch would duplicate. --dry-run prints the exact column_values that would be sent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every 'mcli api' operation is generated from the embedded schema, so a stale schema silently misrepresents the API surface — an operation that exists reads as missing. 'mcli api' now warns on stderr (never stdout, which carries the JSON contract) when the schema is old, and 'mcli schema status' / 'mcli schema refresh' report which schema is in use and replace it. The embed carries a fetched_at stamp so age is a fact rather than a guess, and 'config set api-version' reuses the shared fetch-and-cache path instead of duplicating token resolution and file writing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A column description is the only place a board records what a column *means*, which matters most to the next agent reading it. 'board column create --description' wrote one, but no read path returned it: the field was missing from every column selection, so 'board column list' and 'board get' dropped it and 'column describe' never echoed the text it had just set. Adds description to all five column selections (the two reads, plus the three mutations so writes echo back), and surfaces it in --pretty. The two column tables now share one renderer instead of each carrying its own tabwriter, and the duplicate boardColumn struct — field-for-field identical to columnOutput — is gone; that duplication is what let the two views drift. DESCRIPTION is the last column so long prose cannot widen the columns to its left. truncate now counts runes rather than bytes: it is fed user-authored descriptions, where byte slicing would split a rune and emit U+FFFD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The skill doc is mcli's primary contract for LLM callers, and nothing tied it to the command tree — which is how it came to omit `board create --workspace` entirely, a flag whose absence produces `API: User unauthorized to perform action`, an error that says nothing about workspaces. Meanwhile the optional Semantic Layer section spent 23% of the doc's budget. The doc is now organised by what an agent actually does, in frequency order: discover IDs, read, write, recover from an error. Board setup, previously first, is the rarest operation. It gains a "Failures Worth Knowing About In Advance" section for the cases the error message alone will not explain, and the error table now says what to do next per code rather than just naming it. It moves out of a Go string literal into skill.md, embedded with //go:embed. Markdown is full of backticks and a Go raw string cannot contain one, so the old `+ "`" +` form made the doc effectively unreviewable — which is precisely how it drifted. TestSkillDoc_MatchesCommandTree resolves every command path and flag the doc mentions (85 and 79 respectively, including inside fenced blocks) against the real cobra tree, and TestSkillDoc_AuditCatchesDrift proves that audit can still fail. TestSkill_Concise now budgets bytes rather than lines, because bytes are what the doc costs an agent's context: reflowing a paragraph changes the line count without changing the cost, while one dense table row costs far more than one short line. Also: README and demo examples refreshed to real output, two new demos, ADR-002 records the never-emitted CONFLICT code's removal, and the stale plan docs are deleted per the repo's convention that committed docs describe shipped reality (git history preserves them). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mcli is driven by LLM agents in steady state, and this branch fixes the places where it made agents guess or gave them a misleading answer. Six commits, each building and testing green on its own.
What changes
fix(cli): malformed calls reportUSAGE, notINTERNAL.INTERNALreads as "mcli broke, retry";USAGEreads as "your call was wrong, fix it". Cobra's post-parse validation bypassesFlagErrorFuncentirely, somcli item update <id> --status Done(no--board) exited 5 and invited an agent to retry a call that can never succeed. Also dropsCodeConflict, which was specified but never emitted, and addserrs.AllCodesso docs can be tested against the real set.feat(item): typed shorthands and stdin batch writes.--status Doneaddresses the board's single status column instead of requiring a column lookup plus hand-encoded monday JSON. Where a board has 0 or 2+ columns of a type, the command fails and names the candidates rather than picking one. Passing-reads up to 500 rows from stdin and writes them in one request against monday's per-minute complexity budget instead of N; every row is validated before the first request, and the response reportserrors[].indexso only failed rows are retried — item creation has no dedupe key, so re-sending the batch would duplicate.feat(api): schema staleness reporting and refresh. A stale embedded schema silently misrepresents the API surface, so an operation that exists reads as missing.mcli apinow warns on stderr (never stdout, which carries the JSON contract), andmcli schema status/refreshmake the schema's age a fact rather than a guess.feat(board): column descriptions are actually readable.--descriptionwrote one, but the field was missing from every column selection — soboard column listandboard getdropped it, andcolumn describenever echoed the text it had just set. Added to all five selections, surfaced in--pretty, and the two column tables now share one renderer instead of each carrying its own tabwriter (the duplicateboardColumnstruct is gone — that duplication is what let the views drift).docs: the skill doc is rewritten and guarded. Nothing tied it to the command tree, which is how it omittedboard create --workspace— a flag whose absence returnsAPI: User unauthorized to perform action, an error that says nothing about workspaces. It moves out of a Go string literal intoskill.md(//go:embed): a Go raw string cannot contain a backtick, so the old+ "\" +form made the doc unreviewable, which is precisely how it drifted.TestSkillDoc_MatchesCommandTreenow resolves 85 command paths and 79 flags against the real cobra tree, including inside fenced blocks, andTestSkillDoc_AuditCatchesDrift` proves that audit can still fail.Verification
gofmt,go vet,golangci-lint run(0 issues),go test ./...— greendescribe, and both--prettytables, including a non-ASCII description to exercise rune-safe truncation🤖 Generated with Claude Code