@ketchao/nextup 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +153 -2
  2. package/dist/cli.js +6509 -0
  3. package/package.json +35 -4
package/README.md CHANGED
@@ -1,3 +1,154 @@
1
- # Temporary Holding Version
1
+ # nextup (placeholder name)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Next-step menu for coding agents. MVP: **Claude Code** and **Codex** (same hook protocol).
4
+
5
+ > The name is a placeholder. The reference screenshot is from a product called "Then", so that name should not be used.
6
+
7
+ After each substantial turn, it shows up to 4 contextual options (with a cost indicator). The user answers by typing the number in the normal Claude Code box.
8
+
9
+ ```
10
+ ◆ What's next? Type a number · +1 2 = several
11
+ cost ▾
12
+ 1 Commit changes ★ suggested ■□□ low
13
+ 2 Review my changes ■■□ medium
14
+ 3 Write tests ■■■ high
15
+ 4 Leave it as is □□□ none
16
+ 5 Something else (free text)
17
+ ```
18
+
19
+ ## Try it (5 minutes)
20
+
21
+ ```bash
22
+ npm install
23
+ npm test # 103 tests
24
+ npm run preview # see the menu without Claude (iterate the design here)
25
+ npm run preview -- --scenario failed --width 60
26
+ npm run build
27
+
28
+ # in a test repo, from the published package:
29
+ npm install -g @ketchao/nextup
30
+ nextup init --agent codex
31
+ claude # ask for a small change and watch what happens when it finishes
32
+ ```
33
+
34
+ `init --user` installs the hooks globally; `init --remove` removes them. The first time, it writes a `settings.json.nextup.bak` backup.
35
+
36
+ Try a hook without Claude:
37
+
38
+ ```bash
39
+ cat fixtures/stop.json | NEXTUP_ALWAYS=1 node dist/cli.js hook stop
40
+ cat fixtures/prompt-submit.json | node dist/cli.js hook prompt-submit
41
+ ```
42
+
43
+ ## Try it with Codex (experimental)
44
+
45
+ `hook stop` and `hook prompt-submit` read and write the same JSON format Codex uses, so there is no new adapter: only the registration path changes.
46
+
47
+ ```bash
48
+ node dist/cli.js init --agent codex # writes .codex/hooks.json (or --user for ~/.codex/hooks.json)
49
+ codex # then type /hooks and approve (trust) the 2 nextup hooks
50
+ ```
51
+
52
+ Notes:
53
+ - Codex ignores new or changed hooks until you review them with `/hooks`.
54
+ - On native Windows (Codex CLI 0.166.0, cmd) the hooks **do run**. The `systemMessage` shows up as "Hook · ..." with ANSI colors and alignment intact.
55
+ - The transcript reader (`transcript.ts`) is best effort and understands Claude Code and Codex formats. It only reads the current turn (the `UserPromptSubmit` hook marks where it starts). If something looks wrong, turn on `debug` and check `debug.jsonl`: the `stop-signals` entry shows the detected commands and exit codes, plus a summary of the lines read.
56
+ - Codex runs hooks from a background server that **does not inherit your environment variables**: use `~/.nextup/config.json` (below) or start `codex --no-daemon`.
57
+
58
+ ## Configuration
59
+
60
+ Environment variables **or** the file `~/.nextup/config.json` (environment variables win):
61
+
62
+ ```json
63
+ {
64
+ "language": "en",
65
+ "suggestions": 4,
66
+ "showCustomOption": true,
67
+ "showTokenEstimation": true,
68
+ "autoSuggest": true,
69
+ "backgroundCheck": true,
70
+ "consumption": "measured",
71
+ "providers": { "claude": true, "codex": true, "opencode": true },
72
+ "debug": false,
73
+ "theme": "default"
74
+ }
75
+ ```
76
+
77
+ `suggestions` is the row cap, including "Leave it as is" (1–4). `showCustomOption` is the "Something else" row. `showTokenEstimation` is the cost column. `autoSuggest: false` stops new menus; a menu already on screen still accepts a number. `backgroundCheck` runs `npm test` (and `npm run lint` if it exists) before building the menu, with a short time cap: if 3 tests fail, the row says "3 tests failed, fix them". `false` turns that off. `consumption` is `measured` (real time: `1.5s`, or `—` if that action has not been measured yet) or `estimated` (low, medium, high, none). `providers` turns an agent off: Codex is recognized by a `.codex` path in the transcript. OpenCode has no adapter yet, so its flag is stored and does not cut anything off. `language` is the menu language when the prompt is not enough to tell (a number, an "ok", or a mix). The default is `en`. An existing `config.json` is not rewritten: switch it with `nextup config set language en`. A prompt that is a sentence in English shows the menu in English; a sentence in Spanish shows it in Spanish. `lang`, `mode`, `color`, `width`, `always`, and `disabled` still work. The environment overrides the file.
78
+
79
+ To view or change it without editing the JSON:
80
+
81
+ ```
82
+ nextup config
83
+ nextup config set autoSuggest false
84
+ nextup config set providers.codex false
85
+ nextup config set showCustomOption true
86
+ nextup config set suggestions 3
87
+ nextup config set theme blue
88
+ nextup config set language en
89
+ nextup config set consumption estimated
90
+ ```
91
+
92
+ Switches accept `true`/`false` (also `on`/`off` and `si`/`no`). In the agent box, without leaving Codex and without `nextup` on `PATH`:
93
+
94
+ ```
95
+ nextup theme blue
96
+ nextup config set consumption estimated
97
+ ```
98
+
99
+ The change is saved and the agent does not run it. It shows up on the next menu. `color` and `default` paint each row by cost. `blue`, `green`, `amber`, `pink`, `teal`, and `violet` paint the whole menu one color. `mono` leaves it uncolored. `azul`, `ambar`, `rosa`, and `cian` are accepted aliases. `nextup init` creates the file with everything on if it does not exist yet, and does not overwrite one you already have.
100
+
101
+ With `"debug": true`, each hook stores what it received and returned in `~/.nextup/state/debug.jsonl` (paths and text from your session; local only). Each menu also leaves a line in `~/.nextup/state/choices.jsonl`: whether you picked a number, left it as is, or typed something else. It does not store the sentence. If a row is offered three times on similar turns and you never pick it, it drops.
102
+
103
+ ## How it works
104
+
105
+ 1. **`Stop` hook**: reads the payload, gathers cheap signals (`git status` changes, `Bash` commands from the turn in the transcript, the final message), builds the menu, and shows it with `systemMessage`. Saves the pending menu in `~/.nextup/state/<session_id>.json`.
106
+ 2. The user types the row number (`2`) or several at once (`+2 4`, `1 3`, `1 y 3`). The `+` is optional.
107
+ 3. **`UserPromptSubmit` hook**: if a menu is pending and the reply is a selection, it injects the instructions as `additionalContext` (the hook cannot rewrite the prompt; Claude receives "3" plus the context that explains it). "Leave it as is" and "Something else" block the prompt and spend no tokens. Any other text discards the menu and passes through unchanged.
108
+
109
+ Design decisions (from the critique of the mock):
110
+ - **It does not suggest what already happened**: if the tests already passed, it does not offer to run them again.
111
+ - **It does not repeat what you just picked** (only for the next menu) **and it does not offer to run tests if the repo has none configured**: it suggests the commit instead.
112
+ - **0 to 4 options, and sometimes none**: no menu if nothing changed, or if the agent ended with a question.
113
+ - **The suggested row is never outward**: the schema (`SuggestionSchema`) rejects `recommended` + `outward` (push, deploy, ...).
114
+ - **The default menu paints the row**: green, amber, red, or gray by cost, and "Something else" in blue. The number sits on a lighter chip. `blue`, `green`, `amber`, `pink`, `teal`, and `violet` paint the whole menu one color. `mono` and `color: false` leave it plain text. The `☐` is not a checkbox: you pick with the number. In the agent box, with no slash: `nextup theme blue` (also `verde`, `azul`, `ambar`, `rosa`, `cian`). The slash command belongs to Codex and rejects `/nextup`.
115
+
116
+ ## Spike: what to verify first
117
+
118
+ These four things are **not confirmed**; the design depends on them:
119
+
120
+ | # | Question | How to test | If it fails |
121
+ |---|----------|-------------|-------------|
122
+ | 1 | Does a Stop `systemMessage` show without blocking? | Run `claude`, ask for a change, look when it finishes | Use `NEXTUP_MODE=block` |
123
+ | 2 | Does it keep ANSI and line breaks? | Compare with `NEXTUP_COLOR=0` | Leave color off by default |
124
+ | 3 | Does Claude act well on `"3"` plus injected context? | Pick options several times | Strengthen the `additionalContext` text |
125
+ | 4 | Is the detected width correct? | Resize the terminal; try `NEXTUP_WIDTH` | Fixed or more compact width |
126
+
127
+ Also measure real turn latency (the hook starts in ~60 ms; the rules are local, so there is no model call today) and try the VS Code/Cursor extension, whose UI may show hooks differently from the terminal.
128
+
129
+ ## Known limits
130
+
131
+ - The menu only looks at files whose diff changed this turn (baseline saved in `UserPromptSubmit`). If that mark is missing, it falls back to the full `git status`.
132
+ - The JSONL transcript is not a public contract: `transcript.ts` is best effort and degrades to "no signals".
133
+ - Test detection uses commands and exit codes from the transcript (real Codex format checked with `fixtures/codex-rollout-sample.jsonl`). If the failure looks environmental (EPERM, "Access is denied", ...), it is treated as *blocked*, not as "tests broken": the suggestion becomes review the diff. With no exit code, it falls back to a heuristic on the final message.
134
+ - No detection of headless runs (`claude -p`); if you later call `claude -p` from the hook, export `NEXTUP_INNER=1` on that call to avoid recursion.
135
+ - Claude and Codex share the hook protocol (`systemMessage` / `additionalContext`). OpenCode needs its own presenter (append to the prompt).
136
+
137
+ ## Next steps
138
+
139
+ 1. Pass the spike (table above) and use it for a week; record which options get picked.
140
+ 2. `llm.ts`: 1 or 2 specific suggestions from a small model (API key or `claude -p`), keeping the deterministic rules as the base.
141
+ 3. Measured cost (real duration of the last test run) instead of a fixed value per action type.
142
+ 4. OpenCode presenter (append to the prompt). Claude and Codex already share `src/adapters/shared/`.
143
+
144
+ ## Layout
145
+
146
+ ```
147
+ src/core/ agent-independent: types (zod), rules, render, parser, state
148
+ src/adapters/shared/ hook protocol: payloads, transcript, context, Stop and UserPromptSubmit
149
+ src/adapters/claude/ install into .claude/settings.json
150
+ src/adapters/codex/ install into .codex/hooks.json (timeoutSec)
151
+ src/cli/ preview, init, config, hook entry
152
+ test/ parse, suggest, render, hooks (temp git repo), adapters
153
+ fixtures/ sample payloads
154
+ ```