second-advisor 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kenryu42
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # Second Advisor
2
+
3
+ [![CI](https://github.com/kenryu42/second-advisor/actions/workflows/ci.yml/badge.svg)](https://github.com/kenryu42/second-advisor/actions/workflows/ci.yml)
4
+ [![Version](https://img.shields.io/github/v/tag/kenryu42/second-advisor?label=version&color=blue)](https://github.com/kenryu42/second-advisor)
5
+ [![Codex](https://img.shields.io/badge/Codex-white)](#supported-advisors)
6
+ [![Claude Code](https://img.shields.io/badge/Claude%20Code-D27656)](#supported-advisors)
7
+ [![OpenCode](https://img.shields.io/badge/OpenCode-black)](#supported-advisors)
8
+ [![Grok](https://img.shields.io/badge/Grok-111111)](#supported-advisors)
9
+ [![Pi](https://img.shields.io/badge/Pi%20Coding-22262E)](#supported-advisors)
10
+ [![Droid](https://img.shields.io/badge/Droid-3DDC84)](#supported-advisors)
11
+ [![Amp](https://img.shields.io/badge/Amp-615CED)](#supported-advisors)
12
+ [![Kimi Code](https://img.shields.io/badge/Kimi%20Code-5587FF)](#supported-advisors)
13
+ [![License: MIT](https://img.shields.io/badge/License-MIT-red.svg)](LICENSE)
14
+
15
+ `second-advisor` is a small CLI that sends a prompt to another coding CLI for a second opinion. It is intended for review-oriented workflows where your primary agent or editor can ask a configured advisor to inspect substantial work before a final response.
16
+
17
+ The CLI stores one configured advisor, model or mode, and thinking/effort setting, then builds the correct non-interactive command for that advisor.
18
+
19
+ ## Features
20
+
21
+ - Interactive initialization with installed advisor detection.
22
+ - Prompt forwarding to supported coding CLIs.
23
+ - Short command alias: `sa`.
24
+ - Debug output for the raw command that will be executed.
25
+ - Doctor command for checking config and advisor executables.
26
+ - Model/mode listing for advisors that expose model discovery.
27
+ - Setup command that appends second-advisor workflow instructions to `AGENTS.md` and/or `CLAUDE.md`.
28
+
29
+ ## Requirements
30
+
31
+ - Bun
32
+ - At least one supported coding CLI installed on `PATH`
33
+
34
+ The command entrypoint is `src/index.ts` with `#!/usr/bin/env bun`, so Bun must be available on `PATH` even when using a linked command.
35
+
36
+ Supported advisors:
37
+
38
+ | Advisor | Executable | Notes |
39
+ | --- | --- | --- |
40
+ | Claude | `claude` | Uses built-in aliases: `haiku`, `sonnet`, `opus`. |
41
+ | Codex | `codex` | Lists bundled models with `codex debug models --bundled`. |
42
+ | opencode | `opencode` | Lists models with `opencode models`. |
43
+ | Grok | `grok` | Lists models with `grok models`. |
44
+ | Pi | `pi` | Lists models with `pi --list-models`. |
45
+ | Droid | `droid` | Extracts models from `droid exec --help`. |
46
+ | Amp | `amp` | Uses modes: `rush`, `smart`, `deep`. |
47
+ | Kimi | `kimi` | Lists models with `kimi provider list --json`; no thinking setting. |
48
+
49
+ ## Installation
50
+
51
+ Install dependencies:
52
+
53
+ ```sh
54
+ bun install
55
+ ```
56
+
57
+ Run locally:
58
+
59
+ ```sh
60
+ bun run src/index.ts
61
+ ```
62
+
63
+ For local command usage, link the package so the `bin` entries are available:
64
+
65
+ ```sh
66
+ bun link
67
+ ```
68
+
69
+ Depending on your Bun setup, you may also need Bun's global bin directory on `PATH`.
70
+
71
+ After linking, both commands point to the same CLI:
72
+
73
+ ```sh
74
+ second-advisor --help
75
+ sa --help
76
+ ```
77
+
78
+ ## Quick Start
79
+
80
+ Initialize the advisor config:
81
+
82
+ ```sh
83
+ second-advisor init
84
+ ```
85
+
86
+ Send a prompt:
87
+
88
+ ```sh
89
+ second-advisor "review this implementation for correctness and missed edge cases"
90
+ ```
91
+
92
+ Use the short alias:
93
+
94
+ ```sh
95
+ sa "say hi"
96
+ ```
97
+
98
+ Print the raw advisor command before running it:
99
+
100
+ ```sh
101
+ second-advisor "say hi" --debug
102
+ ```
103
+
104
+ Example debug output:
105
+
106
+ ```sh
107
+ amp --mode rush --effort low --execute 'say hi'
108
+ ```
109
+
110
+ ## Commands
111
+
112
+ ### `second-advisor`
113
+
114
+ With no arguments, opens the interactive menu. If no config exists, the menu offers initialization. If a config exists, the menu shows current setup and lets you change advisor, model/mode, thinking/effort, list models, or run doctor.
115
+
116
+ ```sh
117
+ second-advisor
118
+ ```
119
+
120
+ ### `second-advisor init`
121
+
122
+ Creates or replaces the config at:
123
+
124
+ ```text
125
+ ~/.config/second-advisor/config.json
126
+ ```
127
+
128
+ The initializer detects supported CLIs on `PATH`, asks which advisor to use, then prompts for a model/mode and thinking/effort setting when applicable.
129
+
130
+ ### `second-advisor "<prompt>"`
131
+
132
+ Runs the configured advisor with the given prompt. The prompt is formed by joining all non-command arguments with spaces.
133
+
134
+ ```sh
135
+ second-advisor "what risks do you see in this change?"
136
+ ```
137
+
138
+ If no config exists, the command exits with an error and asks you to run `second-advisor init`.
139
+
140
+ The first argument is reserved when it is one of the command names: `init`, `doctor`, `models`, `setup`, or `status`. For example, `second-advisor init` runs initialization instead of sending `init` as a prompt.
141
+
142
+ ### `second-advisor "<prompt>" --debug`
143
+
144
+ Prints the exact advisor command before executing it.
145
+
146
+ ```sh
147
+ second-advisor "review this diff" --debug
148
+ ```
149
+
150
+ The debug line is written to stderr so advisor stdout remains usable.
151
+
152
+ ### `second-advisor status`
153
+
154
+ Shows the current config, config file path, and whether the configured advisor executable is present on `PATH`.
155
+
156
+ ```sh
157
+ second-advisor status
158
+ ```
159
+
160
+ ### `second-advisor doctor`
161
+
162
+ Checks installed advisor CLI versions, loads the config, confirms the configured executable exists, and verifies Amp mode validity.
163
+
164
+ ```sh
165
+ second-advisor doctor
166
+ ```
167
+
168
+ ### `second-advisor models [advisor]`
169
+
170
+ Lists models or modes for an advisor.
171
+
172
+ ```sh
173
+ second-advisor models
174
+ second-advisor models amp
175
+ second-advisor models codex
176
+ ```
177
+
178
+ If no advisor is passed, the command uses the configured advisor. Claude and Amp are listed from built-in aliases/modes. Other supported advisors use their model-list commands when available.
179
+
180
+ During interactive setup, advisors with model-list commands fall back to manual model entry if model discovery does not return choices.
181
+
182
+ ### `second-advisor setup`
183
+
184
+ Looks for `AGENTS.md` and `CLAUDE.md` in the current working directory and appends the Second Advisor Review instructions to each file found.
185
+
186
+ ```sh
187
+ second-advisor setup
188
+ ```
189
+
190
+ Behavior:
191
+
192
+ - Updates both files if both exist.
193
+ - Skips a file if it already contains `## Second Advisor Review`.
194
+ - Exits with code `1` if neither `AGENTS.md` nor `CLAUDE.md` exists in the current directory.
195
+ - Does not search parent directories.
196
+
197
+ The appended block tells agents to ask for a second opinion after substantial work and to use:
198
+
199
+ ```sh
200
+ second-advisor "<review prompt>"
201
+ ```
202
+
203
+ ## Configuration
204
+
205
+ Config is stored at:
206
+
207
+ ```text
208
+ ~/.config/second-advisor/config.json
209
+ ```
210
+
211
+ Example Amp config:
212
+
213
+ ```json
214
+ {
215
+ "advisor": "amp",
216
+ "model": "rush",
217
+ "thinking": "low"
218
+ }
219
+ ```
220
+
221
+ Example Kimi config:
222
+
223
+ ```json
224
+ {
225
+ "advisor": "kimi",
226
+ "model": "kimi-k2"
227
+ }
228
+ ```
229
+
230
+ Validation rules:
231
+
232
+ - `advisor` must be one of the supported advisors.
233
+ - Most advisors require `model` and `thinking`.
234
+ - Kimi requires `model` and does not support `thinking`.
235
+ - Claude models must be one of `haiku`, `sonnet`, or `opus`.
236
+ - Claude thinking must be one of `low`, `medium`, `high`, `xhigh`, or `max`.
237
+ - Codex thinking must be one of `low`, `medium`, `high`, or `xhigh`.
238
+ - Pi thinking must be one of `off`, `minimal`, `low`, `medium`, `high`, or `xhigh`.
239
+ - Amp modes must be one of `rush`, `smart`, or `deep`.
240
+ - Amp `rush` supports only `none`, `minimal`, `low`, and `medium` effort.
241
+
242
+ ## Exit Codes
243
+
244
+ - Prompt execution forwards the configured advisor command's exit code.
245
+ - Prompt execution exits with code `1` when no config exists.
246
+ - `setup` exits with code `1` when neither `AGENTS.md` nor `CLAUDE.md` exists in the current working directory.
247
+ - Unhandled config parse errors are printed by the CLI and exit with code `1`.
248
+
249
+ ## Advisor Command Mapping
250
+
251
+ `second-advisor` runs advisors non-interactively:
252
+
253
+ | Advisor | Command shape |
254
+ | --- | --- |
255
+ | Claude | `claude --model <model> --effort <thinking> -p <prompt>` |
256
+ | Codex | `codex exec -m <model> --config model_reasoning_effort=<thinking> <prompt>` |
257
+ | opencode | `opencode run -m <model> --variant <thinking> <prompt>` |
258
+ | Grok | `grok -m <model> --reasoning-effort <thinking> -p <prompt>` |
259
+ | Pi | `pi --model <model> --thinking <thinking> -p <prompt>` |
260
+ | Droid | `droid exec -m <model> --reasoning-effort <thinking> <prompt>` |
261
+ | Amp | `amp --mode <mode> --effort <thinking> --execute <prompt>` |
262
+ | Kimi | `kimi -m <model> -p <prompt>` |
263
+
264
+ Use `--debug` to inspect the exact command for your current config.
265
+
266
+ ## Development
267
+
268
+ Install dependencies:
269
+
270
+ ```sh
271
+ bun install
272
+ ```
273
+
274
+ Run the CLI during development:
275
+
276
+ ```sh
277
+ bun run src/index.ts
278
+ ```
279
+
280
+ Or use the project script:
281
+
282
+ ```sh
283
+ bun run sa
284
+ ```
285
+
286
+ Run the full verification suite:
287
+
288
+ ```sh
289
+ bun run check
290
+ ```
291
+
292
+ `bun run check` runs typecheck, Biome lint, Knip, copy-paste detection, and tests.
293
+
294
+ Useful scripts:
295
+
296
+ ```sh
297
+ bun run test
298
+ bun run test:watch
299
+ bun run typecheck
300
+ bun run lint
301
+ bun run publish:dry-run
302
+ ```
303
+
304
+ ## Troubleshooting
305
+
306
+ ### `second-advisor is not initialized`
307
+
308
+ Run:
309
+
310
+ ```sh
311
+ second-advisor init
312
+ ```
313
+
314
+ ### Advisor executable is missing
315
+
316
+ Make sure the configured advisor CLI is installed and available on `PATH`, then run:
317
+
318
+ ```sh
319
+ second-advisor doctor
320
+ ```
321
+
322
+ If `second-advisor init` reports that no supported coding CLI was found, install one of the supported advisor CLIs or fix your `PATH`.
323
+
324
+ ### Model listing fails
325
+
326
+ Some advisors depend on their own CLI model-list command. Confirm that advisor is authenticated and that its model-list command works directly.
327
+
328
+ ### Setup cannot find instruction files
329
+
330
+ `second-advisor setup` only checks the current working directory. Run it from the directory that contains `AGENTS.md` or `CLAUDE.md`.
331
+
332
+ ## License
333
+
334
+ MIT. See `LICENSE`.
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "second-advisor",
3
+ "version": "0.1.0",
4
+ "description": "Ask another coding CLI for a second opinion.",
5
+ "type": "module",
6
+ "bin": {
7
+ "second-advisor": "src/index.ts",
8
+ "sa": "src/index.ts"
9
+ },
10
+ "files": [
11
+ "src"
12
+ ],
13
+ "keywords": [
14
+ "ai",
15
+ "agent",
16
+ "cli",
17
+ "code-review",
18
+ "codex"
19
+ ],
20
+ "author": {
21
+ "name": "J Liew",
22
+ "email": "jliew@420024lab.com"
23
+ },
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/kenryu42/second-advisor.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/kenryu42/second-advisor/issues"
31
+ },
32
+ "homepage": "https://github.com/kenryu42/second-advisor#readme",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "engines": {
37
+ "bun": ">=1.3.0"
38
+ },
39
+ "scripts": {
40
+ "sa": "bun run src/index.ts",
41
+ "test": "bun test",
42
+ "test:watch": "bun test --watch",
43
+ "coverage": "bun test --coverage",
44
+ "lint": "biome check .",
45
+ "lint:ci": "biome ci .",
46
+ "lint:staged": "biome check --write --",
47
+ "format": "biome check --write .",
48
+ "typecheck": "tsc --noEmit",
49
+ "prepare": "lefthook install",
50
+ "hooks:install": "lefthook install",
51
+ "knip": "knip",
52
+ "cpd": "cpd src tests --reporters ai --exit-code 1 --no-tips",
53
+ "check": "bun run typecheck && bun run lint && bun run knip && bun run cpd && AGENT=1 bun test",
54
+ "check:ci": "bun run typecheck && bun run lint:ci && bun run knip && bun run cpd && AGENT=1 bun test --coverage --coverage-reporter=lcov",
55
+ "publish:dry-run": "bun scripts/publish.ts --dry-run",
56
+ "changelog": "bun scripts/changelog.ts"
57
+ },
58
+ "devDependencies": {
59
+ "@biomejs/biome": "latest",
60
+ "@types/bun": "latest",
61
+ "cpd": "latest",
62
+ "knip": "latest",
63
+ "lefthook": "latest",
64
+ "typescript": "latest"
65
+ },
66
+ "dependencies": {
67
+ "@clack/prompts": "^1.6.0",
68
+ "commander": "^15.0.0"
69
+ }
70
+ }
@@ -0,0 +1,205 @@
1
+ export const advisorChoices = [
2
+ "claude",
3
+ "codex",
4
+ "opencode",
5
+ "grok",
6
+ "pi",
7
+ "droid",
8
+ "amp",
9
+ "kimi",
10
+ ] as const;
11
+
12
+ export const ampModes = ["rush", "smart", "deep"] as const;
13
+ export const ampThinking = [
14
+ "none",
15
+ "minimal",
16
+ "low",
17
+ "medium",
18
+ "high",
19
+ "xhigh",
20
+ "max",
21
+ ] as const;
22
+ export const ampThinkingByMode = {
23
+ deep: ampThinking,
24
+ rush: ["none", "minimal", "low", "medium"],
25
+ smart: ampThinking,
26
+ } as const;
27
+ export const claudeModels = ["haiku", "sonnet", "opus"] as const;
28
+ export const claudeThinking = [
29
+ "low",
30
+ "medium",
31
+ "high",
32
+ "xhigh",
33
+ "max",
34
+ ] as const;
35
+ export const codexThinking = ["low", "medium", "high", "xhigh"] as const;
36
+ export const piThinking = [
37
+ "off",
38
+ "minimal",
39
+ "low",
40
+ "medium",
41
+ "high",
42
+ "xhigh",
43
+ ] as const;
44
+
45
+ export type Advisor = (typeof advisorChoices)[number];
46
+ export type CommandSpec = { command: string; args: string[] };
47
+ type ConfigBase<T extends Advisor> = { advisor: T; model: string };
48
+ type ConfigWithThinking<T extends Advisor, U extends string> = ConfigBase<T> & {
49
+ thinking: U;
50
+ };
51
+
52
+ export type Config =
53
+ | (ConfigWithThinking<"claude", (typeof claudeThinking)[number]> & {
54
+ model: (typeof claudeModels)[number];
55
+ })
56
+ | ConfigWithThinking<"codex", (typeof codexThinking)[number]>
57
+ | ConfigWithThinking<"opencode", string>
58
+ | ConfigWithThinking<"grok", string>
59
+ | ConfigWithThinking<"pi", (typeof piThinking)[number]>
60
+ | ConfigWithThinking<"droid", string>
61
+ | {
62
+ advisor: "amp";
63
+ model: (typeof ampModes)[number];
64
+ thinking: (typeof ampThinking)[number];
65
+ }
66
+ | ConfigBase<"kimi">;
67
+
68
+ export function getModelListCommand(advisor: Advisor): CommandSpec | null {
69
+ if (advisor === "codex")
70
+ return { command: "codex", args: ["debug", "models", "--bundled"] };
71
+ if (advisor === "opencode") return { command: "opencode", args: ["models"] };
72
+ if (advisor === "grok") return { command: "grok", args: ["models"] };
73
+ if (advisor === "pi") return { command: "pi", args: ["--list-models"] };
74
+ if (advisor === "droid")
75
+ return { command: "droid", args: ["exec", "--help"] };
76
+ if (advisor === "kimi")
77
+ return { command: "kimi", args: ["provider", "list", "--json"] };
78
+ return null;
79
+ }
80
+
81
+ export function buildAdvisorCommand(
82
+ config: Config,
83
+ prompt: string,
84
+ ): CommandSpec {
85
+ if (config.advisor === "claude") {
86
+ return {
87
+ command: "claude",
88
+ args: [
89
+ "--model",
90
+ config.model,
91
+ "--effort",
92
+ config.thinking,
93
+ "-p",
94
+ prompt,
95
+ ],
96
+ };
97
+ }
98
+
99
+ if (config.advisor === "codex") {
100
+ return {
101
+ command: "codex",
102
+ args: [
103
+ "exec",
104
+ "-m",
105
+ config.model,
106
+ "--config",
107
+ `model_reasoning_effort=${config.thinking}`,
108
+ prompt,
109
+ ],
110
+ };
111
+ }
112
+
113
+ if (config.advisor === "opencode") {
114
+ return {
115
+ command: "opencode",
116
+ args: ["run", "-m", config.model, "--variant", config.thinking, prompt],
117
+ };
118
+ }
119
+
120
+ if (config.advisor === "grok") {
121
+ return {
122
+ command: "grok",
123
+ args: [
124
+ "-m",
125
+ config.model,
126
+ "--reasoning-effort",
127
+ config.thinking,
128
+ "-p",
129
+ prompt,
130
+ ],
131
+ };
132
+ }
133
+
134
+ if (config.advisor === "pi") {
135
+ return {
136
+ command: "pi",
137
+ args: [
138
+ "--model",
139
+ config.model,
140
+ "--thinking",
141
+ config.thinking,
142
+ "-p",
143
+ prompt,
144
+ ],
145
+ };
146
+ }
147
+
148
+ if (config.advisor === "droid") {
149
+ return {
150
+ command: "droid",
151
+ args: [
152
+ "exec",
153
+ "-m",
154
+ config.model,
155
+ "--reasoning-effort",
156
+ config.thinking,
157
+ prompt,
158
+ ],
159
+ };
160
+ }
161
+
162
+ if (config.advisor === "amp") {
163
+ return {
164
+ command: "amp",
165
+ args: [
166
+ "--mode",
167
+ config.model,
168
+ "--effort",
169
+ config.thinking,
170
+ "--execute",
171
+ prompt,
172
+ ],
173
+ };
174
+ }
175
+
176
+ return { command: "kimi", args: ["-m", config.model, "-p", prompt] };
177
+ }
178
+
179
+ export function summarizeConfig(config: Config) {
180
+ if (config.advisor === "amp") {
181
+ return `advisor amp, mode ${config.model}, thinking ${config.thinking}`;
182
+ }
183
+ if (config.advisor === "kimi") return `advisor kimi, model ${config.model}`;
184
+ return `advisor ${config.advisor}, model ${config.model}, thinking ${config.thinking}`;
185
+ }
186
+
187
+ export function getThinkingOptions(
188
+ advisor: Exclude<Advisor, "kimi">,
189
+ model?: string,
190
+ ) {
191
+ if (advisor === "claude") return [...claudeThinking];
192
+ if (advisor === "codex") return [...codexThinking];
193
+ if (advisor === "pi") return [...piThinking];
194
+ if (advisor === "amp") {
195
+ if (model && model in ampThinkingByMode) {
196
+ return [...ampThinkingByMode[model as keyof typeof ampThinkingByMode]];
197
+ }
198
+ return [...ampThinking];
199
+ }
200
+ return ["low", "medium", "high", "xhigh"];
201
+ }
202
+
203
+ export function isAdvisor(value: unknown): value is Advisor {
204
+ return typeof value === "string" && advisorChoices.includes(value as Advisor);
205
+ }