supipowers 1.5.2 → 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/package.json +1 -1
- package/src/commands/agents.ts +26 -11
- package/src/commands/model.ts +1 -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/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" },
|
|
@@ -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
|
|