Skip to content

feat: collapse grok-bot-cli into one root Agent Bundle project - #50

Merged
ScriptedAlchemy merged 3 commits into
mainfrom
feat/ab-full-cli-finish
Sep 15, 2026
Merged

ScriptedAlchemy merged 3 commits into
mainfrom
feat/ab-full-cli-finish

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Summary

One Agent Bundle project at the repository root replaces the two dispatchers that drifted (#24 epic): the hand-written src/cli.js and the nested private plugin/ project (file:.. + resolve.symlinks: false) are deleted in the same change that makes the generated graph green.

  • gbot / grok-bot → generated dist/bin/gbot.mjs (plugin.name: 'gbot'); gbot-install → authored src/gbot-install.ts bound to the same npm dist/ via runInstallCli. Source-wrap publish: files: ["dist", …], prepack: agent-bundle prepack, output.distPath: 'artifact' for the host composite.
  • Domain JS moves to src/core/ (moved, not retyped). src/core/codex/contract.js owns feat(codex): machine-readable contract, delivery envelope, busy-thread refusal (#37 #38 #39) #46 exit-code derivation and failure-document flattening; sendToCodexThread / codexStatus return documents instead of throwing.
  • src/cli/**: bots, groups, doctor, history, send and thread (dedicated CLI routes, not MCP projections), codex status|list-threads|send|queue with exitCode: 'result'.
  • MCP tools gbot_send / gbot_thread and the talk-to-grok-bot skill move to the root tree unchanged in behavior.
  • CI: one npm run check gate plus pack → global install → gbot --help / gbot-install --help; the separate plugin job is gone.

Deviations from the synthesis sketch, deliberate: the contract stays JS/JSDoc (contract.js, not contract.ts); Codex routes are rendered .tsx with Agent.Result rather than plain .ts; gateway send/thread/history are dedicated CLI routes.

Breaking (pre-1.0 minor)

  • Node.js >=22.19.0 (was >=18).
  • Options are command-local (gbot send --history-dir DIR …); the leading-global form is gone.
  • --json is a reserved framework flag anywhere before --; put -- before flag-like message text. Mid-message --json as literal text is no longer expressible.
  • send, codex send, and codex status write one JSON document to stdout with exitCode (failures keep error, delivery, reason, mode, envelope ids; exit 1). Previously failure JSON went to stderr. Argument/schema errors exit 2 (was 1).

Gate (run locally on this branch, which contains origin/main @ 4b3cd78)

Command Result
npm run check (validatebuildvalidate --artifacttypechecktest) pass — 143 unit tests (node:test), 13 route-unit tests (rstest), artifact validation clean for claude/codex/cursor/portable
npx publint@0.3.24 All good
npm pack → tarball contains only dist/**, package.json, README.md, CHANGELOG.md, LICENSEnpm install --global --prefix <tmp>gbot --help, gbot-install --help, gbot doctor --json, gbot send --hop 4 General ack --json (exit 1, reason: "hop-limit" on stdout), gbot codex status --json (exit 0 against a live 0.154.0 daemon) pass, no source tree present

Review of record

Author: Sol worker. Reviewer of record: Fable. Findings fixed on the branch before opening:

  • src/core/codex/contract.js shipped codexEnvFromProcess and CODEX_SEND_REASONS with no production caller (a second env parser that could drift from buildEnvelope), plus an @see pointing at a local scratch path. Removed; tests inline the reason list.
  • Gateway-backed CLI routes threw raw errors that the framework prints verbatim, dropping the blanket redactSecrets pass the old fail() and the MCP tools apply. openBackendFromInput now wraps the backend so every route redacts before the framework writes stderr.
  • gbot send --json refusals (hop-limit, gateway failures) lost their structured reason/delivery document (framework printed a bare message). send now uses the same stdout outcome document as codex send; covered in test/history.test.js.
  • .gitignore duplicated four existing entries.

Independent change-risk review (Claude Opus 5, read-only) — dispositions

# Finding Disposition
1 Routes other than send/codex send printed a raw stack trace and no --json failure document (e.g. documented reason: "bad-response" for codex list-threads). Fixed for the documented machine signals: codex list-threads and codex queue now return the flat failure document on stdout (reason, exitCode: 1), tested. thread/bots/groups/doctor keep the framework's stderr message + exit 1; README and changeset now say so. The stack-trace echo itself comes from React Flight's default onError in the agent-bundle CLI render host (no onError passed) — framework follow-up, not fixable here.
2 withRedactedErrors rewrapped as bare Error, dropping name/reason, so gbot send --json lost reason: "usage". Fixed: wrapper keeps name and own fields; test asserts reason: "usage".
3 --instructions alias dropped but README documented it; --notify/--hidden narrowed to on/off. Documented: alias removed from README, both named in the changeset as breaks.
4 bots list / groups list --json became {bots:[…]} / {groups:[…]}. Fixed: bare arrays again.
5 Bundle renamed grok-botgbot; an old source-built install collides on the grok-bot server. Documented in README and changeset (uninstall the old plugin first). The old plugin was never in the npm tarball, so only source checkouts are affected.
6 codex queue human output was a JSON dump. Fixed: formatCodexQueue restores the old layout.
7 Deleted gbot send argv tests; -- protection untested. Fixed: test/history.test.js covers -- protection, hop-limit, gateway failure, and usage documents.
8 codex list-threads/queue gained exitCode not named in the changeset. Documented: changeset now says every codex command.
9 chat alias records history event: "thread". Documented in the changeset.
10 npm ci depends on pkg.pr.new preview tarballs. Accepted / residual risk (below); pre-existing in the nested plugin.

Post-fix gate (46f624b): npm run check green (143 unit + 13 route tests), publint clean, packed install smoke pass.

Relationships

Residual risk

  • agent-bundle / @agent-bundle/runtime are devDependencies pinned to pkg.pr.new preview tarballs (@8e55ab832d), as the nested plugin/ already was; the root .npmrc (allow-remote=all) exists for that transitive install. Consumers are unaffected (generated dist/ is self-contained), but the repo build depends on those previews staying available until a published agent-bundle release is pinned.
  • gbot bots <command> / codex <command> group lines in --help have no description (framework group rendering); cosmetic.

One dispatcher, one CI gate. The generated `gbot` CLI (`dist/bin/gbot.mjs`)
replaces the hand-written `src/cli.js`; the nested private `plugin/` project
and its `file:..` hatch are deleted. `gbot-install` binds `runInstallCli`
to the same npm `dist/` so Codex, Claude Code, and Cursor install the
`grok-bot` MCP tools and `talk-to-grok-bot` skill from one package.

- Domain JS moves to `src/core/`; #46 exit-code derivation and failure
  document flattening live in `src/core/codex/contract.js`, and
  `sendToCodexThread` / `codexStatus` return documents instead of throwing.
- `src/cli/**` routes: bots, groups, doctor, history, send, thread (dedicated
  CLI routes, not MCP projections) and codex status/list-threads/send/queue.
- `send`, `codex send`, and `codex status` use `exitCode: 'result'`: one JSON
  document on stdout with `exitCode`; refusals keep `reason`/`delivery`.
- Gateway-backed routes redact thrown error text before the framework prints
  it, matching the MCP tools and the old `fail()`.
- Breaking (pre-1.0 minor): Node >= 22.19.0, command-local options only,
  `--json` reserved before `--`, failure JSON on stdout, usage errors exit 2.

Supersedes #47.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-15T09:06:14.667226Z 7f09275 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@changeset-bot

changeset-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 46f624b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
grok-bot-cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@changeset-bot

changeset-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7f09275

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
grok-bot-cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f092755f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/send.tsx
Comment on lines +99 to +106
delivery: out.delivery === 'accepted' ? ('accepted' as const) : ('unknown' as const),
...(typeof out.messageId === 'string' ? { messageId: out.messageId } : {}),
envelopeId: envelope.messageId,
correlationId: envelope.correlationId,
...(input.replyTo ? { replyTo: input.replyTo } : {}),
hop: envelope.hop,
maxHops: envelope.maxHops,
exitCode: 0 as const,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fail sends that receive no gateway receipt

When the gateway returns a successful response without a string messageId, sendPrompt deliberately reports delivery: "unknown", but this route still attaches exitCode: 0. Automation will therefore treat an unacknowledged send as successful instead of checking the thread before resending; derive the exit code from the delivery state and reserve zero for confirmed acceptance.

Useful? React with 👍 / 👎.

Comment thread src/cli/send.tsx
Comment on lines +85 to +87
const message = input.message.join(' ').trim();
const backend = await openBackendFromInput(input);
const out = await backend.send(input.target, withEnvelopeHeader(message, envelope));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject blank Grok Bot messages

If the positional message consists only of empty or whitespace arguments, joining and trimming produces "", which is then sent to the gateway. The previous dispatcher rejected this case, and the new array schema only requires an element rather than nonblank resulting text; validate the normalized message before opening the backend or sending.

Useful? React with 👍 / 👎.

Comment thread src/cli/codex/send.tsx
Comment on lines +64 to +67
out = await sendToCodexThread(input.threadId, input.message.join(' ').trim(), {
envelope,
whenBusy: input.whenBusy,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject blank Codex messages

For input such as gbot codex send <thread> " " (or equivalent JSON input), the schema accepts the array and this expression normalizes it to an empty string before starting or queueing a Codex turn. This bypasses the old CLI's nonempty-message check and can create a meaningless empty turn, so reject the normalized message before calling sendToCodexThread.

Useful? React with 👍 / 👎.

- withRedactedErrors keeps the error name and own fields (reason, mode,
  delivery, envelope) so failure documents still classify; `gbot send`
  usage refusals report reason "usage" again.
- codex list-threads / queue return the flat failure document on stdout
  (reason "bad-response", "experimental-disabled", ...) instead of letting
  the framework print a stack trace; shared failureDocumentSchema.
- bots list / groups list --json are bare arrays again, as before.
- codex queue human output uses the old formatter (formatCodexQueue).
- README/changeset: drop the removed --instructions alias, name the on|off
  narrowing, the `chat` history event, stderr failures for non-send
  commands, and the gbot vs grok-bot bundle rename.
- Tests cover send `--` protection, usage reason, and list-threads
  bad-response documents.
@ScriptedAlchemy
ScriptedAlchemy merged commit 1dc6d34 into main Sep 15, 2026
1 check passed
@github-actions github-actions Bot mentioned this pull request Sep 15, 2026
@ScriptedAlchemy
ScriptedAlchemy deleted the feat/ab-full-cli-finish branch September 15, 2026 09:53
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