supipowers 1.5.1 → 1.5.3
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/README.md +10 -1
- package/bin/install.ts +12 -7
- package/package.json +1 -1
- package/src/commands/agents.ts +26 -11
- package/src/commands/model.ts +1 -1
- package/src/commands/update.ts +38 -1
- package/src/review/agent-loader.ts +79 -20
- package/src/review/multi-agent-runner.ts +1 -1
- package/src/review/types.ts +10 -0
- package/src/types.ts +2 -0
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Run the interactive installer:
|
|
|
23
23
|
bunx supipowers
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
The installer detects
|
|
26
|
+
The installer detects Pi (`~/.pi`) and OMP (`~/.omp`) — when both are present it offers a multiselect to install to one or both. It registers the extension, removes legacy external context-mode MCP registrations from `agent/mcp.json` and cleans up the old `settings/mcp.json` if present, and can install missing optional tooling such as LSP servers, `mcpc`, and Playwright CLI.
|
|
27
27
|
|
|
28
28
|
> [!TIP]
|
|
29
29
|
> Run `/supi:update` at any time to upgrade to the latest version, or `/supi:doctor` to check your setup.
|
|
@@ -159,6 +159,15 @@ Configuration is a three-layer deep-merge (lowest to highest priority):
|
|
|
159
159
|
|
|
160
160
|
Three built-in channels are available: `github` (GitHub Release via `gh` CLI), `gitlab` (GitLab Release via `glab` CLI), and `gitea` (Gitea Release via `tea` CLI). Channels are selected per-project in `release.channels`.
|
|
161
161
|
|
|
162
|
+
Release notes are generated from conventional commits scoped to the paths listed in `package.json`'s `files` field — commits touching files outside those paths are excluded.
|
|
163
|
+
|
|
164
|
+
`/supi:release` accepts two optional flags:
|
|
165
|
+
|
|
166
|
+
| Flag | Effect |
|
|
167
|
+
| ----------- | ------ |
|
|
168
|
+
| `--raw` | Skip AI polish of release notes; use raw conventional-commit output |
|
|
169
|
+
| `--dry-run` | Run the full release flow without publishing |
|
|
170
|
+
|
|
162
171
|
Custom channels can be defined in `release.customChannels`:
|
|
163
172
|
|
|
164
173
|
```json
|
package/bin/install.ts
CHANGED
|
@@ -229,6 +229,12 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
229
229
|
cpSync(join(packageRoot, "src"), join(extDir, "src"), { recursive: true });
|
|
230
230
|
cpSync(join(packageRoot, "bin"), join(extDir, "bin"), { recursive: true });
|
|
231
231
|
cpSync(join(packageRoot, "package.json"), join(extDir, "package.json"));
|
|
232
|
+
// skills/ must live inside the extension dir — src/commands/agents.ts
|
|
233
|
+
// uses a static `import from "../../skills/..."` resolved relative to src/.
|
|
234
|
+
const skillsSrc = join(packageRoot, "skills");
|
|
235
|
+
if (existsSync(skillsSrc)) {
|
|
236
|
+
cpSync(skillsSrc, join(extDir, "skills"), { recursive: true });
|
|
237
|
+
}
|
|
232
238
|
log(` files copied to ${extDir}`);
|
|
233
239
|
|
|
234
240
|
// Rewrite package.json for the installed extension.
|
|
@@ -242,13 +248,12 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
242
248
|
type: sourcePkg.type,
|
|
243
249
|
omp: sourcePkg.omp,
|
|
244
250
|
dependencies: {
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
"@oh-my-pi/pi-tui": "*",
|
|
251
|
+
// Runtime deps from the published package.json (handlebars, etc.)
|
|
252
|
+
...(sourcePkg.dependencies ?? {}),
|
|
253
|
+
// Peer deps that OMP provides at its global level. On macOS Bun resolves
|
|
254
|
+
// these from the global install, but on Windows the extension's own
|
|
255
|
+
// node_modules must contain them or Bun's import fails.
|
|
256
|
+
...(sourcePkg.peerDependencies ?? {}),
|
|
252
257
|
},
|
|
253
258
|
};
|
|
254
259
|
writeFileSync(join(extDir, "package.json"), JSON.stringify(runtimePkg, null, 2));
|
package/package.json
CHANGED
package/src/commands/agents.ts
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
getGlobalReviewAgentsDir,
|
|
9
9
|
getGlobalReviewAgentsConfigPath,
|
|
10
10
|
} from "../review/agent-loader.js";
|
|
11
|
-
import { selectModelFromList } from "./model.js";
|
|
12
|
-
import type { ConfiguredReviewAgent } from "../types.js";
|
|
11
|
+
import { selectModelFromList, THINKING_LEVELS } from "./model.js";
|
|
12
|
+
import type { ConfiguredReviewAgent, ThinkingLevel } from "../types.js";
|
|
13
13
|
import creatingAgentsSkill from "../../skills/creating-supi-agents/SKILL.md" with { type: "text" };
|
|
14
14
|
|
|
15
15
|
// ── List View ──────────────────────────────────────────────────
|
|
@@ -17,23 +17,25 @@ import creatingAgentsSkill from "../../skills/creating-supi-agents/SKILL.md" wit
|
|
|
17
17
|
function buildAgentDashboard(agents: ConfiguredReviewAgent[]): string {
|
|
18
18
|
const nameCol = 18;
|
|
19
19
|
const modelCol = 24;
|
|
20
|
+
const thinkingCol = 12;
|
|
20
21
|
const scopeCol = 10;
|
|
21
22
|
|
|
22
23
|
const lines: string[] = [
|
|
23
24
|
"\n Review Agents\n",
|
|
24
|
-
` ${"name".padEnd(nameCol)} ${"model".padEnd(modelCol)} ${"scope".padEnd(scopeCol)} focus`,
|
|
25
|
+
` ${"name".padEnd(nameCol)} ${"model".padEnd(modelCol)} ${"thinking".padEnd(thinkingCol)} ${"scope".padEnd(scopeCol)} focus`,
|
|
25
26
|
];
|
|
26
27
|
|
|
27
28
|
for (const agent of agents) {
|
|
28
29
|
const name = agent.name.padEnd(nameCol);
|
|
29
|
-
const model = (agent.model ?? "
|
|
30
|
+
const model = (agent.model ?? "\u2014").padEnd(modelCol);
|
|
31
|
+
const thinking = (agent.thinkingLevel ?? "\u2014").padEnd(thinkingCol);
|
|
30
32
|
const scope = (agent.scope ?? "project").padEnd(scopeCol);
|
|
31
33
|
const focus = agent.focus
|
|
32
34
|
? agent.focus.length > 40
|
|
33
35
|
? agent.focus.slice(0, 37) + "..."
|
|
34
36
|
: agent.focus
|
|
35
|
-
: "
|
|
36
|
-
lines.push(` ${name} ${model} ${scope} ${focus}`);
|
|
37
|
+
: "\u2014";
|
|
38
|
+
lines.push(` ${name} ${model} ${thinking} ${scope} ${focus}`);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
const globalCount = agents.filter((a) => a.scope === "global").length;
|
|
@@ -95,7 +97,16 @@ export async function runAgentCreateFlow(platform: Platform, ctx: any): Promise<
|
|
|
95
97
|
const modelInput = await selectModelFromList(ctx);
|
|
96
98
|
// null means "inherit default" — that's fine
|
|
97
99
|
|
|
98
|
-
// Step 4:
|
|
100
|
+
// Step 4: Thinking level
|
|
101
|
+
const thinkingChoice = await ctx.ui.select(
|
|
102
|
+
"Thinking level",
|
|
103
|
+
THINKING_LEVELS.map((t) => t.label),
|
|
104
|
+
{ helpText: "Select thinking level · Esc to cancel" },
|
|
105
|
+
);
|
|
106
|
+
if (!thinkingChoice) return;
|
|
107
|
+
const thinkingLevel: ThinkingLevel | null = THINKING_LEVELS.find((t) => t.label === thinkingChoice)?.value ?? null;
|
|
108
|
+
|
|
109
|
+
// Step 5: Prompt source
|
|
99
110
|
const promptChoice = await ctx.ui.select("Agent prompt", [
|
|
100
111
|
"Send a prompt",
|
|
101
112
|
"Create from zero",
|
|
@@ -105,7 +116,7 @@ export async function runAgentCreateFlow(platform: Platform, ctx: any): Promise<
|
|
|
105
116
|
|
|
106
117
|
if (promptChoice === "Create from zero") {
|
|
107
118
|
// Load the creating-supi-agents skill via steer
|
|
108
|
-
loadSkillAndSteer(platform, ctx, scope, agentName, modelInput);
|
|
119
|
+
loadSkillAndSteer(platform, ctx, scope, agentName, modelInput, thinkingLevel);
|
|
109
120
|
return;
|
|
110
121
|
}
|
|
111
122
|
|
|
@@ -119,7 +130,7 @@ export async function runAgentCreateFlow(platform: Platform, ctx: any): Promise<
|
|
|
119
130
|
return;
|
|
120
131
|
}
|
|
121
132
|
|
|
122
|
-
// Step
|
|
133
|
+
// Step 6: Save
|
|
123
134
|
const agentsDir = scope === "global"
|
|
124
135
|
? getGlobalReviewAgentsDir(platform.paths)
|
|
125
136
|
: getReviewAgentsDir(platform.paths, ctx.cwd);
|
|
@@ -138,6 +149,7 @@ export async function runAgentCreateFlow(platform: Platform, ctx: any): Promise<
|
|
|
138
149
|
enabled: true,
|
|
139
150
|
data: fileName,
|
|
140
151
|
model: modelInput,
|
|
152
|
+
thinkingLevel,
|
|
141
153
|
});
|
|
142
154
|
|
|
143
155
|
ctx.ui.notify(`Agent "${agentName}" created (${scope})`, "info");
|
|
@@ -151,6 +163,7 @@ function loadSkillAndSteer(
|
|
|
151
163
|
scope: "global" | "project",
|
|
152
164
|
agentName: string,
|
|
153
165
|
model: string | null,
|
|
166
|
+
thinkingLevel: ThinkingLevel | null,
|
|
154
167
|
): void {
|
|
155
168
|
const agentsDir = scope === "global"
|
|
156
169
|
? getGlobalReviewAgentsDir(platform.paths)
|
|
@@ -159,7 +172,7 @@ function loadSkillAndSteer(
|
|
|
159
172
|
? getGlobalReviewAgentsConfigPath(platform.paths)
|
|
160
173
|
: getReviewAgentsConfigPath(platform.paths, ctx.cwd);
|
|
161
174
|
|
|
162
|
-
const prompt = buildSkillSteerPrompt(agentName, scope, agentsDir, configPath, model);
|
|
175
|
+
const prompt = buildSkillSteerPrompt(agentName, scope, agentsDir, configPath, model, thinkingLevel);
|
|
163
176
|
|
|
164
177
|
platform.sendMessage(
|
|
165
178
|
{
|
|
@@ -177,6 +190,7 @@ function buildSkillSteerPrompt(
|
|
|
177
190
|
agentsDir: string,
|
|
178
191
|
configPath: string,
|
|
179
192
|
model: string | null,
|
|
193
|
+
thinkingLevel: ThinkingLevel | null,
|
|
180
194
|
): string {
|
|
181
195
|
return `You are guiding the user through creating a new AI review agent for supipowers' multi-agent code review pipeline.
|
|
182
196
|
|
|
@@ -184,6 +198,7 @@ function buildSkillSteerPrompt(
|
|
|
184
198
|
- **Name**: ${agentName}
|
|
185
199
|
- **Scope**: ${scope}
|
|
186
200
|
- **Model**: ${model ?? "inherit default"}
|
|
201
|
+
- **Thinking Level**: ${thinkingLevel ?? "inherit default"}
|
|
187
202
|
- **Target directory**: ${agentsDir}
|
|
188
203
|
- **Config path**: ${configPath}
|
|
189
204
|
|
|
@@ -197,7 +212,7 @@ Once the user approves the agent design, save it:
|
|
|
197
212
|
1. Write the markdown file to: ${agentsDir}/${agentName}.md
|
|
198
213
|
- The file MUST have YAML frontmatter (name, description, focus) and a prompt body ending with {output_instructions}
|
|
199
214
|
2. Update config at: ${configPath}
|
|
200
|
-
- Add entry: { name: "${agentName}", enabled: true, data: "${agentName}.md", model: ${model ? `"${model}"` : "null"} }
|
|
215
|
+
- Add entry: { name: "${agentName}", enabled: true, data: "${agentName}.md", model: ${model ? `"${model}"` : "null"}, thinkingLevel: ${thinkingLevel ? `"${thinkingLevel}"` : "null"} }
|
|
201
216
|
|
|
202
217
|
Use the \`writeAgentFile\` and \`addAgentToConfig\` functions from \`src/review/agent-loader.ts\` if you have tool access, or write the files directly.
|
|
203
218
|
|
package/src/commands/model.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { ModelAction, ModelAssignment, ThinkingLevel } from "../types.js";
|
|
|
11
11
|
import { getBundledProviders, getBundledModels, type GeneratedProvider } from "@oh-my-pi/pi-ai";
|
|
12
12
|
import { createModelPicker, type AvailableModelSet } from "./model-picker.js";
|
|
13
13
|
|
|
14
|
-
const THINKING_LEVELS: Array<{ label: string; value: ThinkingLevel | null }> = [
|
|
14
|
+
export const THINKING_LEVELS: Array<{ label: string; value: ThinkingLevel | null }> = [
|
|
15
15
|
{ label: "Inherit (model default)", value: null },
|
|
16
16
|
{ label: "Off", value: "off" },
|
|
17
17
|
{ label: "Minimal", value: "minimal" },
|
package/src/commands/update.ts
CHANGED
|
@@ -98,7 +98,44 @@ async function updateSupipowers(
|
|
|
98
98
|
if (existsSync(binSource)) {
|
|
99
99
|
cpSync(binSource, join(extDir, "bin"), { recursive: true });
|
|
100
100
|
}
|
|
101
|
-
|
|
101
|
+
// skills/ must live inside the extension dir — src/commands/agents.ts
|
|
102
|
+
// uses a static `import from "../../skills/..."` resolved relative to src/.
|
|
103
|
+
const skillsDirSource = join(downloadedRoot, "skills");
|
|
104
|
+
if (existsSync(skillsDirSource)) {
|
|
105
|
+
cpSync(skillsDirSource, join(extDir, "skills"), { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Rewrite package.json: merge runtime deps + peer deps so Bun on Windows
|
|
109
|
+
// can resolve all imports from the extension's own node_modules.
|
|
110
|
+
const sourcePkg = JSON.parse(readFileSync(join(downloadedRoot, "package.json"), "utf8"));
|
|
111
|
+
const runtimePkg = {
|
|
112
|
+
name: sourcePkg.name,
|
|
113
|
+
version: sourcePkg.version,
|
|
114
|
+
type: sourcePkg.type,
|
|
115
|
+
omp: sourcePkg.omp,
|
|
116
|
+
dependencies: {
|
|
117
|
+
...(sourcePkg.dependencies ?? {}),
|
|
118
|
+
...(sourcePkg.peerDependencies ?? {}),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
writeFileSync(join(extDir, "package.json"), JSON.stringify(runtimePkg, null, 2));
|
|
122
|
+
|
|
123
|
+
// Install runtime dependencies (handlebars, etc.)
|
|
124
|
+
// Without this, the extension fails to load because node_modules/ was deleted above.
|
|
125
|
+
ctx.ui.notify("Installing dependencies...", "info");
|
|
126
|
+
const bunInstall = await platform.exec("bun", ["install", "--production"], { cwd: extDir });
|
|
127
|
+
if (bunInstall.code !== 0) {
|
|
128
|
+
// Fallback to npm if bun is not available (e.g. Windows without global bun)
|
|
129
|
+
const npmInstall = await platform.exec("npm", ["install", "--omit=dev"], { cwd: extDir });
|
|
130
|
+
if (npmInstall.code !== 0) {
|
|
131
|
+
ctx.ui.notify(
|
|
132
|
+
"Could not install extension dependencies.\n" +
|
|
133
|
+
"Commands may not appear. Run manually:\n" +
|
|
134
|
+
` cd ${extDir} && bun install`,
|
|
135
|
+
"warning",
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
102
139
|
|
|
103
140
|
// Copy skills
|
|
104
141
|
const skillsSource = join(downloadedRoot, "skills");
|
|
@@ -29,9 +29,9 @@ const DEFAULT_AGENT_TEMPLATES: Record<string, string> = {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
const DEFAULT_REVIEW_AGENTS_CONFIG: ReviewAgentConfig[] = [
|
|
32
|
-
{ name: "security", enabled: true, data: "security.md", model: null },
|
|
33
|
-
{ name: "correctness", enabled: true, data: "correctness.md", model: null },
|
|
34
|
-
{ name: "maintainability", enabled: true, data: "maintainability.md", model: null },
|
|
32
|
+
{ name: "security", enabled: true, data: "security.md", model: null, thinkingLevel: "low" },
|
|
33
|
+
{ name: "correctness", enabled: true, data: "correctness.md", model: null, thinkingLevel: "low" },
|
|
34
|
+
{ name: "maintainability", enabled: true, data: "maintainability.md", model: null, thinkingLevel: "low" },
|
|
35
35
|
];
|
|
36
36
|
|
|
37
37
|
export interface LoadedReviewAgents {
|
|
@@ -41,19 +41,39 @@ export interface LoadedReviewAgents {
|
|
|
41
41
|
agents: ConfiguredReviewAgent[];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const CONFIG_HEADER = [
|
|
45
|
+
"# Review Agents Configuration",
|
|
46
|
+
"#",
|
|
47
|
+
"# Options:",
|
|
48
|
+
"# name: string - agent identifier (kebab-case)",
|
|
49
|
+
"# enabled: boolean - true | false",
|
|
50
|
+
"# data: string - markdown file name in the agents directory",
|
|
51
|
+
"# model: string - model id (e.g. \"anthropic/claude-sonnet-4-20250514\") or null to inherit",
|
|
52
|
+
"# thinkingLevel: string - off | minimal | low | medium | high | xhigh | null to inherit",
|
|
53
|
+
"#",
|
|
54
|
+
].join("\n");
|
|
55
|
+
|
|
56
|
+
function serializeConfigYaml(agents: ReviewAgentConfig[]): string {
|
|
45
57
|
return [
|
|
58
|
+
CONFIG_HEADER,
|
|
59
|
+
"",
|
|
46
60
|
"agents:",
|
|
47
|
-
...
|
|
48
|
-
|
|
49
|
-
`
|
|
50
|
-
`
|
|
51
|
-
|
|
61
|
+
...agents.flatMap((a, i) => [
|
|
62
|
+
...(i > 0 ? [""] : []),
|
|
63
|
+
` - name: ${a.name}`,
|
|
64
|
+
` enabled: ${a.enabled}`,
|
|
65
|
+
` data: ${a.data}`,
|
|
66
|
+
` model: ${a.model ?? "null"}`,
|
|
67
|
+
` thinkingLevel: ${a.thinkingLevel ?? "null"}`,
|
|
52
68
|
]),
|
|
53
69
|
"",
|
|
54
70
|
].join("\n");
|
|
55
71
|
}
|
|
56
72
|
|
|
73
|
+
function buildDefaultConfigText(): string {
|
|
74
|
+
return serializeConfigYaml(DEFAULT_REVIEW_AGENTS_CONFIG);
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
function writeIfMissing(filePath: string, content: string): void {
|
|
58
78
|
if (fs.existsSync(filePath)) {
|
|
59
79
|
return;
|
|
@@ -62,6 +82,51 @@ function writeIfMissing(filePath: string, content: string): void {
|
|
|
62
82
|
fs.writeFileSync(filePath, content);
|
|
63
83
|
}
|
|
64
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Migrate pre-existing config.yml files:
|
|
87
|
+
* - adds the comment header if missing
|
|
88
|
+
* - backfills thinkingLevel on agents that lack it
|
|
89
|
+
*/
|
|
90
|
+
function migrateConfigIfNeeded(configPath: string): void {
|
|
91
|
+
if (!fs.existsSync(configPath)) return;
|
|
92
|
+
|
|
93
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
94
|
+
if (raw.startsWith("# Review Agents Configuration")) return;
|
|
95
|
+
|
|
96
|
+
// Parse the bare YAML by hand — we only need name/enabled/data/model/thinkingLevel.
|
|
97
|
+
// The file is small and always has the same shape.
|
|
98
|
+
const agents: ReviewAgentConfig[] = [];
|
|
99
|
+
let current: Partial<ReviewAgentConfig> | null = null;
|
|
100
|
+
|
|
101
|
+
for (const line of raw.split("\n")) {
|
|
102
|
+
const trimmed = line.trim();
|
|
103
|
+
if (trimmed.startsWith("- name:")) {
|
|
104
|
+
if (current?.name) agents.push(current as ReviewAgentConfig);
|
|
105
|
+
current = { name: trimmed.slice("- name:".length).trim() };
|
|
106
|
+
} else if (current && trimmed.startsWith("enabled:")) {
|
|
107
|
+
current.enabled = trimmed.slice("enabled:".length).trim() === "true";
|
|
108
|
+
} else if (current && trimmed.startsWith("data:")) {
|
|
109
|
+
current.data = trimmed.slice("data:".length).trim();
|
|
110
|
+
} else if (current && trimmed.startsWith("model:")) {
|
|
111
|
+
const val = trimmed.slice("model:".length).trim();
|
|
112
|
+
current.model = val === "null" ? null : val;
|
|
113
|
+
} else if (current && trimmed.startsWith("thinkingLevel:")) {
|
|
114
|
+
const val = trimmed.slice("thinkingLevel:".length).trim();
|
|
115
|
+
current.thinkingLevel = val === "null" ? null : (val as any);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (current?.name) agents.push(current as ReviewAgentConfig);
|
|
119
|
+
|
|
120
|
+
// Backfill thinkingLevel for agents that didn't have it
|
|
121
|
+
for (const agent of agents) {
|
|
122
|
+
if (agent.thinkingLevel === undefined) {
|
|
123
|
+
agent.thinkingLevel = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fs.writeFileSync(configPath, serializeConfigYaml(agents));
|
|
128
|
+
}
|
|
129
|
+
|
|
65
130
|
function validateReviewAgentsConfig(data: unknown): ReviewAgentsConfig {
|
|
66
131
|
const errors = formatReviewValidationErrors(collectReviewValidationErrors(ReviewAgentsConfigSchema, data));
|
|
67
132
|
if (errors.length > 0) {
|
|
@@ -145,6 +210,7 @@ export function ensureDefaultReviewAgents(paths: PlatformPaths, cwd: string): vo
|
|
|
145
210
|
|
|
146
211
|
// Default agent markdown files are installed globally only.
|
|
147
212
|
writeIfMissing(getReviewAgentsConfigPath(paths, cwd), buildDefaultConfigText());
|
|
213
|
+
migrateConfigIfNeeded(getReviewAgentsConfigPath(paths, cwd));
|
|
148
214
|
}
|
|
149
215
|
|
|
150
216
|
export async function loadReviewAgentsConfig(paths: PlatformPaths, cwd: string): Promise<ReviewAgentsConfig> {
|
|
@@ -183,6 +249,7 @@ export async function loadReviewAgents(paths: PlatformPaths, cwd: string): Promi
|
|
|
183
249
|
enabled: agent.enabled,
|
|
184
250
|
data: agent.data,
|
|
185
251
|
model: agent.model,
|
|
252
|
+
thinkingLevel: agent.thinkingLevel ?? null,
|
|
186
253
|
} satisfies ConfiguredReviewAgent;
|
|
187
254
|
});
|
|
188
255
|
|
|
@@ -213,6 +280,7 @@ export function ensureGlobalDefaultReviewAgents(paths: PlatformPaths): void {
|
|
|
213
280
|
}
|
|
214
281
|
|
|
215
282
|
writeIfMissing(getGlobalReviewAgentsConfigPath(paths), buildDefaultConfigText());
|
|
283
|
+
migrateConfigIfNeeded(getGlobalReviewAgentsConfigPath(paths));
|
|
216
284
|
}
|
|
217
285
|
|
|
218
286
|
export async function loadGlobalReviewAgentsConfig(paths: PlatformPaths): Promise<ReviewAgentsConfig> {
|
|
@@ -245,6 +313,7 @@ export async function loadGlobalReviewAgents(paths: PlatformPaths): Promise<Load
|
|
|
245
313
|
enabled: agent.enabled,
|
|
246
314
|
data: agent.data,
|
|
247
315
|
model: agent.model,
|
|
316
|
+
thinkingLevel: agent.thinkingLevel ?? null,
|
|
248
317
|
scope: "global" as const,
|
|
249
318
|
} satisfies ConfiguredReviewAgent;
|
|
250
319
|
});
|
|
@@ -321,15 +390,5 @@ export async function addAgentToConfig(
|
|
|
321
390
|
config.agents.push(agent);
|
|
322
391
|
}
|
|
323
392
|
|
|
324
|
-
|
|
325
|
-
"agents:",
|
|
326
|
-
...config.agents.flatMap((a) => [
|
|
327
|
-
` - name: ${a.name}`,
|
|
328
|
-
` enabled: ${a.enabled}`,
|
|
329
|
-
` data: ${a.data}`,
|
|
330
|
-
` model: ${a.model ?? "null"}`,
|
|
331
|
-
]),
|
|
332
|
-
"",
|
|
333
|
-
].join("\n");
|
|
334
|
-
fs.writeFileSync(configPath, text);
|
|
393
|
+
fs.writeFileSync(configPath, serializeConfigYaml(config.agents));
|
|
335
394
|
}
|
package/src/review/types.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
ReviewScopeStats,
|
|
18
18
|
ReviewSession,
|
|
19
19
|
ReviewSessionArtifacts,
|
|
20
|
+
ThinkingLevel,
|
|
20
21
|
} from "../types.js";
|
|
21
22
|
export type {
|
|
22
23
|
ConfiguredReviewAgent,
|
|
@@ -122,6 +123,15 @@ export const ReviewAgentConfigSchema = Type.Object(
|
|
|
122
123
|
enabled: Type.Boolean(),
|
|
123
124
|
data: Type.String({ minLength: 1 }),
|
|
124
125
|
model: Type.Union([Type.String({ minLength: 1 }), Type.Null()]),
|
|
126
|
+
thinkingLevel: Type.Optional(Type.Union([
|
|
127
|
+
Type.Literal("off"),
|
|
128
|
+
Type.Literal("minimal"),
|
|
129
|
+
Type.Literal("low"),
|
|
130
|
+
Type.Literal("medium"),
|
|
131
|
+
Type.Literal("high"),
|
|
132
|
+
Type.Literal("xhigh"),
|
|
133
|
+
Type.Null(),
|
|
134
|
+
])),
|
|
125
135
|
},
|
|
126
136
|
{ additionalProperties: false },
|
|
127
137
|
);
|
package/src/types.ts
CHANGED
|
@@ -174,6 +174,7 @@ export interface ReviewAgentConfig {
|
|
|
174
174
|
enabled: boolean;
|
|
175
175
|
data: string;
|
|
176
176
|
model: string | null;
|
|
177
|
+
thinkingLevel: ThinkingLevel | null;
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
/** Top-level review agent pipeline config */
|
|
@@ -194,6 +195,7 @@ export interface ConfiguredReviewAgent extends ReviewAgentDefinition {
|
|
|
194
195
|
enabled: boolean;
|
|
195
196
|
data: string;
|
|
196
197
|
model: string | null;
|
|
198
|
+
thinkingLevel: ThinkingLevel | null;
|
|
197
199
|
scope?: "global" | "project";
|
|
198
200
|
}
|
|
199
201
|
|