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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supipowers",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Workflow extension for OMP coding agents.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -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 ?? "").padEnd(modelCol);
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: Prompt source
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 5: Save
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
 
@@ -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
- function buildDefaultConfigText(): string {
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
- ...DEFAULT_REVIEW_AGENTS_CONFIG.flatMap((agent) => [
48
- ` - name: ${agent.name}`,
49
- ` enabled: ${agent.enabled}`,
50
- ` data: ${agent.data}`,
51
- " model: null",
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
- const text = [
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
  }
@@ -88,7 +88,7 @@ async function runConfiguredAgent(
88
88
  };
89
89
  },
90
90
  model: agent.model ?? input.model,
91
- thinkingLevel: input.thinkingLevel ?? null,
91
+ thinkingLevel: agent.thinkingLevel ?? input.thinkingLevel ?? null,
92
92
  timeoutMs: input.timeoutMs ?? 120_000,
93
93
  });
94
94
 
@@ -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