supipowers 1.2.5 → 1.3.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/package.json +1 -1
- package/skills/context-mode/SKILL.md +3 -7
- package/src/bootstrap.ts +3 -0
- package/src/commands/commit.ts +8 -0
- package/src/commands/fix-pr.ts +20 -49
- package/src/commands/mcp.ts +14 -0
- package/src/commands/model.ts +3 -2
- package/src/commands/optimize-context.ts +202 -0
- package/src/commands/plan.ts +8 -9
- package/src/commands/qa.ts +15 -0
- package/src/commands/release.ts +16 -1
- package/src/commands/review.ts +7 -8
- package/src/commands/supi.ts +1 -0
- package/src/config/model-resolver.ts +87 -0
- package/src/context/analyzer.ts +57 -0
- package/src/context/optimizer.ts +199 -0
- package/src/context-mode/compressor.ts +14 -11
- package/src/context-mode/event-extractor.ts +45 -16
- package/src/context-mode/event-store.ts +225 -16
- package/src/context-mode/hooks.ts +62 -7
- package/src/context-mode/routing.ts +9 -14
- package/src/context-mode/snapshot-builder.ts +243 -7
- package/src/fix-pr/config.ts +0 -5
- package/src/fix-pr/prompt-builder.ts +7 -6
- package/src/fix-pr/types.ts +0 -11
- package/src/git/commit.ts +74 -26
- package/src/planning/approval-flow.ts +14 -1
- package/src/platform/omp.ts +5 -2
- package/src/platform/types.ts +2 -1
package/package.json
CHANGED
|
@@ -33,10 +33,6 @@ Instead use:
|
|
|
33
33
|
- `ctx_execute(language: "shell", code: "find ...")` to run in sandbox
|
|
34
34
|
- `ctx_batch_execute(commands, queries)` for multiple searches
|
|
35
35
|
|
|
36
|
-
### Read (full-file, no limit) — BLOCKED
|
|
37
|
-
Reading an entire file without a `limit` parameter is blocked.
|
|
38
|
-
- If you need to **Edit** the file → re-call Read with `limit` parameter (e.g., `limit: 200`)
|
|
39
|
-
- If you need to **analyze or explore** → use `ctx_execute_file(path, language, code)` instead. Only your printed summary enters context.
|
|
40
36
|
|
|
41
37
|
## REDIRECTED tools — use sandbox equivalents
|
|
42
38
|
|
|
@@ -46,9 +42,9 @@ For everything else, use:
|
|
|
46
42
|
- `ctx_batch_execute(commands, queries)` — run multiple commands + search in ONE call
|
|
47
43
|
- `ctx_execute(language: "shell", code: "...")` — run in sandbox, only stdout enters context
|
|
48
44
|
|
|
49
|
-
### Read (
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
### Read (large files)
|
|
46
|
+
Reads are never blocked — they always go through OMP's native read tool so hashline anchors (`N#XX`) are preserved for the edit contract. Large file reads (>110 lines) are automatically compressed to head (80 lines) + tail (30 lines) with a `sel` hint for the omitted section.
|
|
47
|
+
For analysis-only reads where hashlines aren't needed, `ctx_execute_file(path, language, code)` remains more efficient — only your printed summary enters context.
|
|
52
48
|
|
|
53
49
|
## Tool selection hierarchy
|
|
54
50
|
|
package/src/bootstrap.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { registerModelCommand, handleModel } from "./commands/model.js";
|
|
|
17
17
|
import { executeManagerAction } from "./mcp/manager-tool.js";
|
|
18
18
|
import { registerFixPrCommand } from "./commands/fix-pr.js";
|
|
19
19
|
import { registerContextCommand, handleContext } from "./commands/context.js";
|
|
20
|
+
import { registerOptimizeContextCommand, handleOptimizeContext } from "./commands/optimize-context.js";
|
|
20
21
|
import { registerCommitCommand, handleCommit } from "./commands/commit.js";
|
|
21
22
|
import { loadConfig } from "./config/loader.js";
|
|
22
23
|
import { registerContextModeHooks } from "./context-mode/hooks.js";
|
|
@@ -37,6 +38,7 @@ const TUI_COMMANDS: Record<string, (platform: Platform, ctx: any, args?: string)
|
|
|
37
38
|
"supi:mcp": (platform, ctx) => handleMcp(platform, ctx),
|
|
38
39
|
"supi:model": (platform, ctx) => handleModel(platform, ctx),
|
|
39
40
|
"supi:context": (platform, ctx) => handleContext(platform, ctx),
|
|
41
|
+
"supi:optimize-context": (platform, ctx) => handleOptimizeContext(platform, ctx),
|
|
40
42
|
"supi:commit": (platform, ctx, args) => handleCommit(platform, ctx, args),
|
|
41
43
|
"supi:release": (platform, ctx, args) => handleRelease(platform, ctx, args),
|
|
42
44
|
};
|
|
@@ -68,6 +70,7 @@ export function bootstrap(platform: Platform): void {
|
|
|
68
70
|
registerMcpCommand(platform);
|
|
69
71
|
registerModelCommand(platform);
|
|
70
72
|
registerContextCommand(platform);
|
|
73
|
+
registerOptimizeContextCommand(platform);
|
|
71
74
|
registerCommitCommand(platform);
|
|
72
75
|
|
|
73
76
|
|
package/src/commands/commit.ts
CHANGED
|
@@ -5,8 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Platform } from "../platform/types.js";
|
|
7
7
|
import type { PlatformContext } from "../platform/types.js";
|
|
8
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
8
9
|
import { analyzeAndCommit } from "../git/commit.js";
|
|
9
10
|
|
|
11
|
+
modelRegistry.register({
|
|
12
|
+
id: "commit",
|
|
13
|
+
category: "command",
|
|
14
|
+
label: "Commit",
|
|
15
|
+
harnessRoleHint: "default",
|
|
16
|
+
});
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* Register the command for autocomplete and /help listing.
|
|
12
20
|
* Actual execution goes through handleCommit via the TUI dispatch.
|
package/src/commands/fix-pr.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Platform } from "../platform/types.js";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { loadFixPrConfig, saveFixPrConfig
|
|
4
|
+
import { loadFixPrConfig, saveFixPrConfig } from "../fix-pr/config.js";
|
|
5
5
|
import { buildFixPrOrchestratorPrompt } from "../fix-pr/prompt-builder.js";
|
|
6
6
|
import type { FixPrConfig, CommentReplyPolicy } from "../fix-pr/types.js";
|
|
7
7
|
import {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "../storage/fix-pr-sessions.js";
|
|
13
13
|
import { notifyInfo, notifyError, notifyWarning } from "../notifications/renderer.js";
|
|
14
14
|
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
15
|
-
import { resolveModelForAction, createModelBridge } from "../config/model-resolver.js";
|
|
15
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
16
16
|
import { loadModelConfig } from "../config/model-config.js";
|
|
17
17
|
import { detectBotReviewers } from "../fix-pr/bot-detector.js";
|
|
18
18
|
|
|
@@ -23,6 +23,14 @@ modelRegistry.register({
|
|
|
23
23
|
harnessRoleHint: "default",
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
modelRegistry.register({
|
|
27
|
+
id: "task",
|
|
28
|
+
category: "sub-agent",
|
|
29
|
+
parent: "fix-pr",
|
|
30
|
+
label: "Task (sub-agent)",
|
|
31
|
+
harnessRoleHint: "default",
|
|
32
|
+
});
|
|
33
|
+
|
|
26
34
|
function getScriptsDir(): string {
|
|
27
35
|
return path.join(path.dirname(new URL(import.meta.url).pathname), "..", "fix-pr", "scripts");
|
|
28
36
|
}
|
|
@@ -42,6 +50,12 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
42
50
|
platform.registerCommand("supi:fix-pr", {
|
|
43
51
|
description: "Fix PR review comments with token-optimized agent orchestration",
|
|
44
52
|
async handler(args: string | undefined, ctx: any): Promise<void> {
|
|
53
|
+
// Resolve and apply model override early — before any logic that might fail
|
|
54
|
+
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
55
|
+
const bridge = createModelBridge(platform);
|
|
56
|
+
const resolved = resolveModelForAction("fix-pr", modelRegistry, modelConfig, bridge);
|
|
57
|
+
await applyModelOverride(platform, ctx, "fix-pr", resolved);
|
|
58
|
+
|
|
45
59
|
// ── Step 1: Detect PR ──────────────────────────────────────────
|
|
46
60
|
let prNumber: number | null = null;
|
|
47
61
|
let repo: string | null = null;
|
|
@@ -178,6 +192,9 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
// ── Step 6: Build and send prompt ──────────────────────────────
|
|
195
|
+
// Resolve task model (sub-agents: planner, fixer). Falls back to fix-pr model.
|
|
196
|
+
const taskResolved = resolveModelForAction("task", modelRegistry, modelConfig, bridge);
|
|
197
|
+
const taskModel = taskResolved.model ?? resolved.model ?? "claude-sonnet-4-6";
|
|
181
198
|
const prompt = buildFixPrOrchestratorPrompt({
|
|
182
199
|
prNumber,
|
|
183
200
|
repo,
|
|
@@ -187,15 +204,9 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
187
204
|
config,
|
|
188
205
|
iteration: ledger.iteration,
|
|
189
206
|
skillContent,
|
|
207
|
+
taskModel,
|
|
190
208
|
});
|
|
191
209
|
|
|
192
|
-
// Resolve model for this action
|
|
193
|
-
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
194
|
-
const bridge = createModelBridge(platform);
|
|
195
|
-
const resolved = resolveModelForAction("fix-pr", modelRegistry, modelConfig, bridge);
|
|
196
|
-
if (resolved.source !== "main" && platform.setModel && resolved.model) {
|
|
197
|
-
platform.setModel(resolved.model);
|
|
198
|
-
}
|
|
199
210
|
|
|
200
211
|
platform.sendMessage(
|
|
201
212
|
{
|
|
@@ -234,10 +245,6 @@ const ITERATION_OPTIONS = [
|
|
|
234
245
|
"5",
|
|
235
246
|
];
|
|
236
247
|
|
|
237
|
-
const MODEL_TIER_OPTIONS = [
|
|
238
|
-
"high — thorough reasoning, more tokens",
|
|
239
|
-
"low — fast execution, fewer tokens",
|
|
240
|
-
];
|
|
241
248
|
|
|
242
249
|
async function runSetupWizard(ctx: any): Promise<FixPrConfig | null> {
|
|
243
250
|
|
|
@@ -270,46 +277,10 @@ async function runSetupWizard(ctx: any): Promise<FixPrConfig | null> {
|
|
|
270
277
|
if (!iterChoice) return null;
|
|
271
278
|
const maxIterations = parseInt(iterChoice, 10);
|
|
272
279
|
|
|
273
|
-
// 4. Model preferences
|
|
274
|
-
const orchestratorTier = await ctx.ui.select(
|
|
275
|
-
"Orchestrator model tier (assessment & grouping)",
|
|
276
|
-
MODEL_TIER_OPTIONS,
|
|
277
|
-
{ helpText: "Higher tier = more thorough analysis" },
|
|
278
|
-
);
|
|
279
|
-
if (!orchestratorTier) return null;
|
|
280
|
-
|
|
281
|
-
const plannerTier = await ctx.ui.select(
|
|
282
|
-
"Planner model tier (fix planning)",
|
|
283
|
-
MODEL_TIER_OPTIONS,
|
|
284
|
-
{ helpText: "Higher tier = more detailed plans" },
|
|
285
|
-
);
|
|
286
|
-
if (!plannerTier) return null;
|
|
287
|
-
|
|
288
|
-
const fixerTier = await ctx.ui.select(
|
|
289
|
-
"Fixer model tier (code changes)",
|
|
290
|
-
MODEL_TIER_OPTIONS,
|
|
291
|
-
{ helpText: "Lower tier usually sufficient for execution" },
|
|
292
|
-
);
|
|
293
|
-
if (!fixerTier) return null;
|
|
294
|
-
|
|
295
280
|
const config: FixPrConfig = {
|
|
296
281
|
reviewer: { type: "none", triggerMethod: null },
|
|
297
282
|
commentPolicy,
|
|
298
283
|
loop: { delaySeconds, maxIterations },
|
|
299
|
-
models: {
|
|
300
|
-
orchestrator: {
|
|
301
|
-
...DEFAULT_FIX_PR_CONFIG.models.orchestrator,
|
|
302
|
-
tier: orchestratorTier.startsWith("high") ? "high" : "low",
|
|
303
|
-
},
|
|
304
|
-
planner: {
|
|
305
|
-
...DEFAULT_FIX_PR_CONFIG.models.planner,
|
|
306
|
-
tier: plannerTier.startsWith("high") ? "high" : "low",
|
|
307
|
-
},
|
|
308
|
-
fixer: {
|
|
309
|
-
...DEFAULT_FIX_PR_CONFIG.models.fixer,
|
|
310
|
-
tier: fixerTier.startsWith("high") ? "high" : "low",
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
284
|
};
|
|
314
285
|
|
|
315
286
|
return config;
|
package/src/commands/mcp.ts
CHANGED
|
@@ -8,6 +8,16 @@ import { generateReadme, writeReadme, writeToolsCache, generateSkill, writeSkill
|
|
|
8
8
|
import { MCPC_EXIT } from "../mcp/types.js";
|
|
9
9
|
import type { McpTool, ServerConfig, HostMcpServer } from "../mcp/types.js";
|
|
10
10
|
import { lookupMcpServer, pickBestMatch } from "../mcp/registry.js";
|
|
11
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
12
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
13
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
14
|
+
|
|
15
|
+
modelRegistry.register({
|
|
16
|
+
id: "mcp",
|
|
17
|
+
category: "command",
|
|
18
|
+
label: "MCP",
|
|
19
|
+
harnessRoleHint: "slow",
|
|
20
|
+
});
|
|
11
21
|
|
|
12
22
|
export interface ParsedMcpArgs {
|
|
13
23
|
subcommand?: string;
|
|
@@ -789,6 +799,10 @@ export function registerMcpCommand(platform: Platform): void {
|
|
|
789
799
|
platform.registerCommand("supi:mcp", {
|
|
790
800
|
description: "Manage MCP servers — add, remove, enable, disable, refresh",
|
|
791
801
|
async handler(args: string | undefined, ctx: any) {
|
|
802
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
803
|
+
const bridge = createModelBridge(platform);
|
|
804
|
+
const resolved = resolveModelForAction("mcp", modelRegistry, modelCfg, bridge);
|
|
805
|
+
await applyModelOverride(platform, ctx, "mcp", resolved);
|
|
792
806
|
if (args) {
|
|
793
807
|
// CLI mode — parse and dispatch
|
|
794
808
|
await handleMcpCli(platform, ctx, parseCliArgs(args));
|
package/src/commands/model.ts
CHANGED
|
@@ -39,7 +39,7 @@ function buildDashboard(
|
|
|
39
39
|
bridge: ModelPlatformBridge,
|
|
40
40
|
): string {
|
|
41
41
|
const config = loadModelConfig(paths, cwd);
|
|
42
|
-
const lines: string[] = ["
|
|
42
|
+
const lines: string[] = ["\n Model Configuration\n", ` ${"action".padEnd(20)} ${"model".padEnd(24)} ${"thinking".padEnd(10)} source`];
|
|
43
43
|
|
|
44
44
|
let lastCategory: "command" | "sub-agent" | null = null;
|
|
45
45
|
let lastParent: string | undefined = undefined;
|
|
@@ -56,10 +56,11 @@ function buildDashboard(
|
|
|
56
56
|
const modelDisplay = (resolved.source === "main" && source === "main"
|
|
57
57
|
? "—"
|
|
58
58
|
: resolved.model) ?? "—";
|
|
59
|
+
const thinkingDisplay = resolved.thinkingLevel ?? "—";
|
|
59
60
|
const sourceDisplay = formatSource(source);
|
|
60
61
|
|
|
61
62
|
lines.push(
|
|
62
|
-
` ${action.id.padEnd(20)} ${modelDisplay.padEnd(
|
|
63
|
+
` ${action.id.padEnd(20)} ${modelDisplay.padEnd(24)} ${thinkingDisplay.padEnd(10)} ${sourceDisplay}`,
|
|
63
64
|
);
|
|
64
65
|
|
|
65
66
|
lastCategory = action.category;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { Platform, PlatformContext } from "../platform/types.js";
|
|
2
|
+
import {
|
|
3
|
+
parseSystemPrompt,
|
|
4
|
+
parseIndividualSkills,
|
|
5
|
+
} from "../context/analyzer.js";
|
|
6
|
+
import {
|
|
7
|
+
detectTechStack,
|
|
8
|
+
buildContextReport,
|
|
9
|
+
} from "../context/optimizer.js";
|
|
10
|
+
import type { ContextReport } from "../context/optimizer.js";
|
|
11
|
+
|
|
12
|
+
function formatTokens(n: number): string {
|
|
13
|
+
return n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function handleOptimizeContext(platform: Platform, ctx: PlatformContext): void {
|
|
17
|
+
void (async () => {
|
|
18
|
+
if (!ctx.hasUI) return;
|
|
19
|
+
|
|
20
|
+
// 1. Detect tech stack
|
|
21
|
+
const techStack = await detectTechStack(platform, ctx.cwd);
|
|
22
|
+
|
|
23
|
+
// 2. Get system prompt
|
|
24
|
+
let systemPrompt = "";
|
|
25
|
+
try {
|
|
26
|
+
systemPrompt = (ctx as any).getSystemPrompt?.() ?? "";
|
|
27
|
+
} catch {
|
|
28
|
+
// getSystemPrompt not available
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!systemPrompt) {
|
|
32
|
+
ctx.ui.notify("System prompt unavailable", "warning");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 3. Parse
|
|
37
|
+
const sections = parseSystemPrompt(systemPrompt);
|
|
38
|
+
const skills = parseIndividualSkills(systemPrompt);
|
|
39
|
+
|
|
40
|
+
// 4. Build raw report
|
|
41
|
+
const report = buildContextReport(sections, skills, techStack);
|
|
42
|
+
|
|
43
|
+
// 5. Show TUI
|
|
44
|
+
await showReport(platform, ctx, report);
|
|
45
|
+
})().catch((err) => {
|
|
46
|
+
ctx.ui.notify(`Optimize error: ${(err as Error).message}`, "error");
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function registerOptimizeContextCommand(platform: Platform): void {
|
|
51
|
+
platform.registerCommand("supi:optimize-context", {
|
|
52
|
+
description: "Analyze context usage and suggest token optimizations",
|
|
53
|
+
async handler(_args: string | undefined, ctx: any) {
|
|
54
|
+
handleOptimizeContext(platform, ctx);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── Internal ──────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
async function showReport(
|
|
62
|
+
platform: Platform,
|
|
63
|
+
ctx: PlatformContext,
|
|
64
|
+
report: ContextReport,
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
const techList = [
|
|
67
|
+
...report.techStack.languages,
|
|
68
|
+
...(report.techStack.runtime ? [report.techStack.runtime] : []),
|
|
69
|
+
...report.techStack.frameworks,
|
|
70
|
+
...report.techStack.tools,
|
|
71
|
+
].join(", ");
|
|
72
|
+
|
|
73
|
+
const lines: string[] = [
|
|
74
|
+
`Tech: ${techList || "unknown"}`,
|
|
75
|
+
`Current: ~${formatTokens(report.totalTokens)} tokens | Target: ~8.0K tokens`,
|
|
76
|
+
"",
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
// Skills breakdown
|
|
80
|
+
if (report.skills.length === 0) {
|
|
81
|
+
lines.push("No skills detected in system prompt.");
|
|
82
|
+
} else {
|
|
83
|
+
const totalSkillTokens = report.skills.reduce((sum, s) => sum + s.tokens, 0);
|
|
84
|
+
lines.push(`Skills (${report.skills.length} loaded, ~${formatTokens(totalSkillTokens)} tok total):`);
|
|
85
|
+
lines.push("");
|
|
86
|
+
|
|
87
|
+
const sorted = [...report.skills].sort((a, b) => b.tokens - a.tokens);
|
|
88
|
+
for (const s of sorted) {
|
|
89
|
+
lines.push(` ${s.name.padEnd(32)} ~${formatTokens(s.tokens)} tok`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Non-skill sections
|
|
94
|
+
if (report.sections.length > 0) {
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push("Other sections:");
|
|
97
|
+
lines.push("");
|
|
98
|
+
|
|
99
|
+
for (const sec of report.sections) {
|
|
100
|
+
const tok = formatTokens(sec.tokens);
|
|
101
|
+
const note = sec.note ? ` (${sec.note})` : "";
|
|
102
|
+
lines.push(` ${sec.label.padEnd(28)} ~${tok} tok${note}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const message = lines.join("\n");
|
|
107
|
+
|
|
108
|
+
// confirm() may not be available on all platforms
|
|
109
|
+
const shouldOptimize = ctx.ui.confirm
|
|
110
|
+
? await ctx.ui.confirm("Context Optimization", message)
|
|
111
|
+
: (await ctx.ui.select("Context Optimization", [message, "▶ Optimize with AI", "Close"]))?.includes("Optimize");
|
|
112
|
+
|
|
113
|
+
if (!shouldOptimize) return;
|
|
114
|
+
|
|
115
|
+
platform.sendMessage(
|
|
116
|
+
{
|
|
117
|
+
customType: "optimize-context",
|
|
118
|
+
content: [{ type: "text", text: buildOptimizationPrompt(report) }],
|
|
119
|
+
display: "none",
|
|
120
|
+
},
|
|
121
|
+
{ deliverAs: "steer", triggerTurn: true },
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildOptimizationPrompt(report: ContextReport): string {
|
|
126
|
+
const lines: string[] = [];
|
|
127
|
+
|
|
128
|
+
const techList = [
|
|
129
|
+
...report.techStack.languages,
|
|
130
|
+
...(report.techStack.runtime ? [report.techStack.runtime] : []),
|
|
131
|
+
...report.techStack.frameworks,
|
|
132
|
+
...report.techStack.tools,
|
|
133
|
+
].join(", ");
|
|
134
|
+
|
|
135
|
+
lines.push("# Context Optimization Request");
|
|
136
|
+
lines.push("");
|
|
137
|
+
lines.push(`Current system prompt is **~${formatTokens(report.totalTokens)} tokens**. Target is **< 8K tokens**.`);
|
|
138
|
+
lines.push(`Project tech stack: **${techList || "unknown"}**`);
|
|
139
|
+
lines.push("");
|
|
140
|
+
|
|
141
|
+
// Skill inventory
|
|
142
|
+
if (report.skills.length > 0) {
|
|
143
|
+
lines.push("## Skills currently loaded");
|
|
144
|
+
lines.push("");
|
|
145
|
+
lines.push("| Skill | Tokens |");
|
|
146
|
+
lines.push("|-------|--------|");
|
|
147
|
+
const sorted = [...report.skills].sort((a, b) => b.tokens - a.tokens);
|
|
148
|
+
for (const s of sorted) {
|
|
149
|
+
lines.push(`| ${s.name} | ~${formatTokens(s.tokens)} |`);
|
|
150
|
+
}
|
|
151
|
+
lines.push("");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Section inventory
|
|
155
|
+
if (report.sections.length > 0) {
|
|
156
|
+
lines.push("## Other prompt sections");
|
|
157
|
+
lines.push("");
|
|
158
|
+
for (const sec of report.sections) {
|
|
159
|
+
const note = sec.note ? ` — ${sec.note}` : "";
|
|
160
|
+
lines.push(`- **${sec.label}**: ~${formatTokens(sec.tokens)} tok${note}`);
|
|
161
|
+
}
|
|
162
|
+
lines.push("");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
lines.push("## Your task");
|
|
166
|
+
lines.push("");
|
|
167
|
+
lines.push("Classify each loaded skill into one of these actions:");
|
|
168
|
+
lines.push("");
|
|
169
|
+
lines.push("| Action | Prompt cost | When to use |");
|
|
170
|
+
lines.push("|--------|-------------|-------------|");
|
|
171
|
+
lines.push("| **Keep as skill** | Full content every turn | Essential for this project, needed constantly |");
|
|
172
|
+
lines.push("| **Convert to rulebook rule** | Name + description only | Relevant but only needed on-demand (load via `rule://`) |");
|
|
173
|
+
lines.push("| **Convert to TTSR rule** | Zero | Behavioral enforcement — triggered by regex pattern in output stream |");
|
|
174
|
+
lines.push("| **Convert to slash command** | Zero | Interactive workflow the user invokes explicitly |");
|
|
175
|
+
lines.push("| **Disable** | Zero | Irrelevant to this project's tech stack |");
|
|
176
|
+
lines.push("");
|
|
177
|
+
lines.push("For each skill, consider:");
|
|
178
|
+
lines.push("1. Is it relevant to the detected tech stack? If not → **disable**.");
|
|
179
|
+
lines.push("2. Does it enforce a behavior pattern (debugging, TDD, verification)? → **TTSR** with a condition regex.");
|
|
180
|
+
lines.push("3. Is it reference material loaded for occasional lookups? → **rulebook** with a short description.");
|
|
181
|
+
lines.push("4. Is it an interactive workflow the user triggers explicitly? → **slash command**.");
|
|
182
|
+
lines.push("5. Is it essential context needed on every turn for this project? → **keep**.");
|
|
183
|
+
lines.push("");
|
|
184
|
+
lines.push("## Implementation");
|
|
185
|
+
lines.push("");
|
|
186
|
+
lines.push("After classifying, implement the changes:");
|
|
187
|
+
lines.push("");
|
|
188
|
+
lines.push("- **Rulebook**: Create `.omp/rules/<skill-name>.md` with YAML frontmatter `description: \"...\"` and condensed key content");
|
|
189
|
+
lines.push("- **TTSR**: Create `.omp/rules/<skill-name>.md` with YAML frontmatter `condition: \"regex_pattern\"`");
|
|
190
|
+
lines.push("- **Disable**: Note which skills to remove from the session configuration");
|
|
191
|
+
lines.push("- **Command**: Note which skills could become slash commands (but don't create them now)");
|
|
192
|
+
lines.push("");
|
|
193
|
+
lines.push("## Warnings");
|
|
194
|
+
lines.push("");
|
|
195
|
+
lines.push("- Do **NOT** delete files from `~/.omp/skills/` — only create project-local `.omp/rules/` files");
|
|
196
|
+
lines.push("- Rulebook and TTSR files go in `.omp/rules/` at the project root");
|
|
197
|
+
lines.push("- Preserve the original skill content's intent when condensing for rulebook rules");
|
|
198
|
+
lines.push("");
|
|
199
|
+
lines.push("Present your classification table and implementation plan first, then ask before executing.");
|
|
200
|
+
|
|
201
|
+
return lines.join("\n");
|
|
202
|
+
}
|
package/src/commands/plan.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { buildPlanningPrompt, buildQuickPlanPrompt } from "../planning/prompt-bu
|
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
15
|
-
import { resolveModelForAction, createModelBridge } from "../config/model-resolver.js";
|
|
15
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
16
16
|
import { loadModelConfig } from "../config/model-config.js";
|
|
17
17
|
import { startPlanTracking } from "../planning/approval-flow.js";
|
|
18
18
|
|
|
@@ -38,6 +38,12 @@ export function registerPlanCommand(platform: Platform): void {
|
|
|
38
38
|
platform.registerCommand("supi:plan", {
|
|
39
39
|
description: "Start collaborative planning for a feature or task",
|
|
40
40
|
async handler(args: string | undefined, ctx: any) {
|
|
41
|
+
// Resolve and apply model override early — before any logic that might fail
|
|
42
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
43
|
+
const bridge = createModelBridge(platform);
|
|
44
|
+
const resolved = resolveModelForAction("plan", modelRegistry, modelCfg, bridge);
|
|
45
|
+
await applyModelOverride(platform, ctx, "plan", resolved);
|
|
46
|
+
|
|
41
47
|
const skillPath = findSkillPath("planning");
|
|
42
48
|
let skillContent = "";
|
|
43
49
|
if (skillPath) {
|
|
@@ -133,13 +139,6 @@ export function registerPlanCommand(platform: Platform): void {
|
|
|
133
139
|
prompt += "\n\n" + buildVisualInstructions(visualUrl, visualSessionDir);
|
|
134
140
|
}
|
|
135
141
|
|
|
136
|
-
// Resolve model for this action
|
|
137
|
-
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
138
|
-
const bridge = createModelBridge(platform);
|
|
139
|
-
const resolved = resolveModelForAction("plan", modelRegistry, modelConfig, bridge);
|
|
140
|
-
if (resolved.source !== "main" && platform.setModel && resolved.model) {
|
|
141
|
-
platform.setModel(resolved.model);
|
|
142
|
-
}
|
|
143
142
|
|
|
144
143
|
platform.sendMessage(
|
|
145
144
|
{
|
|
@@ -151,7 +150,7 @@ export function registerPlanCommand(platform: Platform): void {
|
|
|
151
150
|
);
|
|
152
151
|
|
|
153
152
|
// Track planning state for the approval flow (agent_end hook)
|
|
154
|
-
startPlanTracking(ctx.cwd, platform.paths, ctx.newSession?.bind(ctx));
|
|
153
|
+
startPlanTracking(ctx.cwd, platform.paths, ctx.newSession?.bind(ctx), resolved);
|
|
155
154
|
|
|
156
155
|
notifyInfo(ctx, "Planning started", args ? `Topic: ${args}` : "Describe what you want to build");
|
|
157
156
|
},
|
package/src/commands/qa.ts
CHANGED
|
@@ -8,6 +8,16 @@ import { createNewE2eSession } from "../qa/session.js";
|
|
|
8
8
|
import { buildE2eOrchestratorPrompt } from "../qa/prompt-builder.js";
|
|
9
9
|
import { findActiveSession, getSessionDir } from "../storage/qa-sessions.js";
|
|
10
10
|
import type { E2eQaConfig, AppType, E2eRegression } from "../qa/types.js";
|
|
11
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
12
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
13
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
14
|
+
|
|
15
|
+
modelRegistry.register({
|
|
16
|
+
id: "qa",
|
|
17
|
+
category: "command",
|
|
18
|
+
label: "QA",
|
|
19
|
+
harnessRoleHint: "slow",
|
|
20
|
+
});
|
|
11
21
|
|
|
12
22
|
function getScriptsDir(): string {
|
|
13
23
|
return path.join(path.dirname(new URL(import.meta.url).pathname), "..", "qa", "scripts");
|
|
@@ -104,6 +114,11 @@ export function registerQaCommand(platform: Platform): void {
|
|
|
104
114
|
platform.registerCommand("supi:qa", {
|
|
105
115
|
description: "Run autonomous E2E product testing pipeline with playwright",
|
|
106
116
|
async handler(args: string | undefined, ctx: any) {
|
|
117
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
118
|
+
const bridge = createModelBridge(platform);
|
|
119
|
+
const resolved = resolveModelForAction("qa", modelRegistry, modelCfg, bridge);
|
|
120
|
+
await applyModelOverride(platform, ctx, "qa", resolved);
|
|
121
|
+
|
|
107
122
|
const scriptsDir = getScriptsDir();
|
|
108
123
|
|
|
109
124
|
// ── Step 1: Detect app type ─────────────────────────────────────
|
package/src/commands/release.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Platform } from "../platform/types.js";
|
|
2
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
3
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
4
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
2
5
|
import type { ReleaseChannel, BumpType } from "../types.js";
|
|
3
6
|
import { loadConfig, updateConfig } from "../config/loader.js";
|
|
4
7
|
import { detectChannels } from "../release/detector.js";
|
|
@@ -10,6 +13,13 @@ import { notifyInfo, notifySuccess, notifyError } from "../notifications/rendere
|
|
|
10
13
|
import { analyzeAndCommit } from "../git/commit.js";
|
|
11
14
|
import { getWorkingTreeStatus } from "../git/status.js";
|
|
12
15
|
|
|
16
|
+
modelRegistry.register({
|
|
17
|
+
id: "release",
|
|
18
|
+
category: "command",
|
|
19
|
+
label: "Release",
|
|
20
|
+
harnessRoleHint: "slow",
|
|
21
|
+
});
|
|
22
|
+
|
|
13
23
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
14
24
|
const STATUS_KEY = "supi-release";
|
|
15
25
|
const WIDGET_KEY = "supi-release";
|
|
@@ -185,7 +195,12 @@ export function registerReleaseCommand(platform: Platform): void {
|
|
|
185
195
|
* TUI-only handler — called from the input event dispatcher in bootstrap.ts.
|
|
186
196
|
* Runs the full release flow without triggering the outer LLM session.
|
|
187
197
|
*/
|
|
188
|
-
export function handleRelease(platform: Platform, ctx: any, args?: string): void {
|
|
198
|
+
export async function handleRelease(platform: Platform, ctx: any, args?: string): Promise<void> {
|
|
199
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
200
|
+
const bridge = createModelBridge(platform);
|
|
201
|
+
const resolved = resolveModelForAction("release", modelRegistry, modelCfg, bridge);
|
|
202
|
+
await applyModelOverride(platform, ctx, "release", resolved);
|
|
203
|
+
|
|
189
204
|
if (!ctx.hasUI) {
|
|
190
205
|
ctx.ui.notify("Release requires interactive mode", "warning");
|
|
191
206
|
return;
|
package/src/commands/review.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { buildReviewPrompt } from "../quality/gate-runner.js";
|
|
|
5
5
|
import { isLspAvailable } from "../lsp/detector.js";
|
|
6
6
|
import { notifyInfo, notifyWarning } from "../notifications/renderer.js";
|
|
7
7
|
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
8
|
-
import { resolveModelForAction, createModelBridge } from "../config/model-resolver.js";
|
|
8
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
9
9
|
import { loadModelConfig } from "../config/model-config.js";
|
|
10
10
|
|
|
11
11
|
modelRegistry.register({
|
|
@@ -19,6 +19,12 @@ export function registerReviewCommand(platform: Platform): void {
|
|
|
19
19
|
platform.registerCommand("supi:review", {
|
|
20
20
|
description: "Run quality gates at chosen depth (quick/thorough/full-regression)",
|
|
21
21
|
async handler(args: string | undefined, ctx: any) {
|
|
22
|
+
// Resolve and apply model override early — before any logic that might fail
|
|
23
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
24
|
+
const bridge = createModelBridge(platform);
|
|
25
|
+
const resolved = resolveModelForAction("review", modelRegistry, modelCfg, bridge);
|
|
26
|
+
await applyModelOverride(platform, ctx, "review", resolved);
|
|
27
|
+
|
|
22
28
|
const config = loadConfig(platform.paths, ctx.cwd);
|
|
23
29
|
|
|
24
30
|
let profileOverride: string | undefined;
|
|
@@ -96,13 +102,6 @@ export function registerReviewCommand(platform: Platform): void {
|
|
|
96
102
|
|
|
97
103
|
notifyInfo(ctx, `Review started`, `profile: ${profile.name}`);
|
|
98
104
|
|
|
99
|
-
// Resolve model for this action
|
|
100
|
-
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
101
|
-
const bridge = createModelBridge(platform);
|
|
102
|
-
const resolved = resolveModelForAction("review", modelRegistry, modelConfig, bridge);
|
|
103
|
-
if (resolved.source !== "main" && platform.setModel && resolved.model) {
|
|
104
|
-
platform.setModel(resolved.model);
|
|
105
|
-
}
|
|
106
105
|
|
|
107
106
|
platform.sendMessage(
|
|
108
107
|
{
|
package/src/commands/supi.ts
CHANGED
|
@@ -20,6 +20,7 @@ export function handleSupi(platform: Platform, ctx: PlatformContext): void {
|
|
|
20
20
|
"/supi:doctor — Run health checks",
|
|
21
21
|
"/supi:update — Update to latest version",
|
|
22
22
|
"/supi:context — Show context breakdown",
|
|
23
|
+
"/supi:optimize-context — Optimize context to save tokens",
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
const status = [
|