supipowers 2.0.1 → 2.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/README.md +10 -6
- package/package.json +4 -2
- package/skills/harness/SKILL.md +1 -0
- package/src/bootstrap.ts +5 -133
- package/src/commands/clear.ts +6 -6
- package/src/commands/release.ts +3 -1
- package/src/commands/update.ts +1 -1
- package/src/config/defaults.ts +5 -5
- package/src/config/loader.ts +1 -0
- package/src/config/schema.ts +2 -6
- package/src/context/analyzer.ts +104 -35
- package/src/context-mode/knowledge/store.ts +381 -43
- package/src/context-mode/tools.ts +41 -3
- package/src/deps/registry.ts +1 -12
- package/src/fix-pr/assessment.ts +1 -0
- package/src/fix-pr/prompt-builder.ts +1 -0
- package/src/git/commit.ts +76 -18
- package/src/harness/command.ts +103 -6
- package/src/harness/default-agents/docs.md +39 -0
- package/src/harness/docs/config.ts +29 -0
- package/src/harness/docs/glob-match.ts +27 -0
- package/src/harness/docs/index-renderer.ts +82 -0
- package/src/harness/docs/provenance.ts +125 -0
- package/src/harness/docs/regen-decision.ts +167 -0
- package/src/harness/docs/representative-files.ts +175 -0
- package/src/harness/docs/source-hash.ts +106 -0
- package/src/harness/docs/validator.ts +233 -0
- package/src/harness/hooks/layer-context-inject.ts +35 -1
- package/src/harness/hooks/register.ts +24 -3
- package/src/harness/pipeline.ts +20 -5
- package/src/harness/pr-comment/baseline.ts +105 -0
- package/src/harness/pr-comment/ci-env.ts +120 -0
- package/src/harness/pr-comment/gh-poster.ts +227 -0
- package/src/harness/pr-comment/handler.ts +198 -0
- package/src/harness/pr-comment/render.ts +297 -0
- package/src/harness/pr-comment/status.ts +95 -0
- package/src/harness/pr-comment/types.ts +73 -0
- package/src/harness/pr-comment/workflow-summary.ts +47 -0
- package/src/harness/project-paths.ts +95 -0
- package/src/harness/stages/design.ts +1 -0
- package/src/harness/stages/discover.ts +1 -13
- package/src/harness/stages/docs.ts +708 -0
- package/src/harness/stages/implement-apply.ts +877 -0
- package/src/harness/stages/implement.ts +64 -51
- package/src/harness/stages/plan.ts +25 -16
- package/src/harness/stages/validate.ts +370 -0
- package/src/harness/storage.ts +142 -0
- package/src/harness/tools.ts +130 -0
- package/src/mempalace/bridge.ts +207 -41
- package/src/mempalace/config.ts +10 -4
- package/src/mempalace/format.ts +122 -6
- package/src/mempalace/hooks.ts +204 -56
- package/src/mempalace/installer-helper.ts +18 -4
- package/src/mempalace/python/mempalace_bridge.py +128 -3
- package/src/mempalace/runtime.ts +55 -18
- package/src/mempalace/schema.ts +151 -30
- package/src/mempalace/session-summary.ts +5 -0
- package/src/mempalace/tool.ts +17 -4
- package/src/mempalace/upstream-limits.ts +69 -0
- package/src/planning/approval-flow.ts +25 -2
- package/src/planning/planning-ask-tool.ts +34 -4
- package/src/planning/system-prompt.ts +1 -1
- package/src/tool-catalog/active-tool-controller.ts +0 -22
- package/src/tool-catalog/active-tool-planner.ts +0 -26
- package/src/tool-catalog/tool-groups.ts +1 -9
- package/src/types.ts +87 -8
- package/src/ui-design/session.ts +114 -10
- package/src/utils/executable.ts +10 -1
- package/src/workspace/state-paths.ts +1 -1
- package/src/commands/mcp.ts +0 -814
- package/src/mcp/activation.ts +0 -77
- package/src/mcp/config.ts +0 -223
- package/src/mcp/docs.ts +0 -154
- package/src/mcp/gateway.ts +0 -103
- package/src/mcp/lifecycle.ts +0 -79
- package/src/mcp/manager-tool.ts +0 -104
- package/src/mcp/mcpc.ts +0 -113
- package/src/mcp/registry.ts +0 -98
- package/src/mcp/triggers.ts +0 -62
- package/src/mcp/types.ts +0 -95
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Run the interactive installer:
|
|
|
23
23
|
bunx supipowers
|
|
24
24
|
```
|
|
25
25
|
|
|
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
|
|
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 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.
|
|
@@ -42,7 +42,6 @@ The installer scans for these and offers to install missing tooling where it can
|
|
|
42
42
|
|
|
43
43
|
| Dependency | What it enables |
|
|
44
44
|
| ------------------------------------- | --------------------------------------------------------------------- |
|
|
45
|
-
| [mcpc](https://github.com/apify/mcpc) | MCP server management via `/supi:mcp` |
|
|
46
45
|
| `typescript-language-server` | TypeScript/JS diagnostics and references in review gates |
|
|
47
46
|
| `pyright` | Python type checking |
|
|
48
47
|
| `rust-analyzer` | Rust language server |
|
|
@@ -70,19 +69,22 @@ The installer scans for these and offers to install missing tooling where it can
|
|
|
70
69
|
| `/supi:model` | Configure model assignments per action (plan, review, qa…) |
|
|
71
70
|
| `/supi:context` | Show current context window usage and system prompt breakdown |
|
|
72
71
|
| `/supi:optimize-context` | Analyze loaded prompt/context usage and suggest reductions |
|
|
73
|
-
| `/supi:mcp` | Manage MCP servers (connect, disconnect, migrate) |
|
|
74
72
|
| `/supi:config` | Interactive settings TUI |
|
|
75
73
|
| `/supi:status` | Show project plans and configuration summary |
|
|
76
74
|
| `/supi:doctor` | Diagnose extension health and missing dependencies |
|
|
77
75
|
| `/supi:generate` | Documentation drift detection |
|
|
78
76
|
| `/supi:update` | Update supipowers to the latest version |
|
|
79
77
|
| `/supi:agents` | Manage review agents |
|
|
78
|
+
| `/supi:ultraplan` | Multi-stage authoring pipeline (intake → scout → discover → research → synthesize → review → approve) |
|
|
79
|
+
| `/supi:harness` | Harness engineering pipeline and anti-slop guardrails |
|
|
80
|
+
| `/supi:memory` | Manage native MemPalace memory integration (`status`, `setup`) |
|
|
81
|
+
| `/supi:clear` | Clear metrics, cache, session knowledge, and memory |
|
|
80
82
|
|
|
81
|
-
Most commands steer the AI session. These are TUI-only — they open native dialogs without triggering the AI: `/supi`, `/supi:config`, `/supi:status`, `/supi:review`, `/supi:update`, `/supi:doctor`, `/supi:
|
|
83
|
+
Most commands steer the AI session. These are TUI-only — they open native dialogs without triggering the AI: `/supi`, `/supi:config`, `/supi:status`, `/supi:review`, `/supi:update`, `/supi:doctor`, `/supi:model`, `/supi:context`, `/supi:optimize-context`, `/supi:commit`, `/supi:release`, `/supi:checks`, `/supi:agents`, `/supi:ultraplan`, `/supi:harness`, `/supi:memory`, `/supi:clear`.
|
|
82
84
|
|
|
83
85
|
## How it works
|
|
84
86
|
|
|
85
|
-
**Planning.** `/supi:plan` steers the AI through planning phases (
|
|
87
|
+
**Planning.** `/supi:plan` steers the AI through planning phases (Explore → Clarify → Brainstorm → Design & Save → Review Loop → User Gate → Plan), saves the result to `.omp/supipowers/plans/`, and presents an approval UI. On approval, tasks execute in the same session.
|
|
86
88
|
|
|
87
89
|
**Quality gates.** `/supi:checks` runs deterministic quality gates. Six gates are available: `lsp-diagnostics`, `lint`, `typecheck`, `format`, `test-suite`, and `build`. Each gate can be enabled independently via `/supi:config` or the shared repository config at `.omp/supipowers/config.json`. In monorepos, `/supi:checks` defaults to `All`, which runs the root target plus every workspace target sequentially; use `--target <package>` to narrow the run or `--target all` to request the batch mode explicitly. Gates report issues with severity levels.
|
|
88
90
|
|
|
@@ -123,7 +125,6 @@ Use `/supi:agents` to inspect the merged set that will actually run.
|
|
|
123
125
|
| Release automation | ✅ | ❌ |
|
|
124
126
|
| Commit workflow | ✅ | ❌ |
|
|
125
127
|
| Context-window optimizations | ✅ | ❌ |
|
|
126
|
-
| MCP server management through mcpc | ✅ | ❌ |
|
|
127
128
|
| Git worktree workflow | ❌ | ✅ |
|
|
128
129
|
|
|
129
130
|
## Quality gates
|
|
@@ -157,6 +158,8 @@ Configuration uses built-in defaults plus two user-managed override layers:
|
|
|
157
158
|
|
|
158
159
|
`/supi:config` exposes only `Global` and `Repository`. In monorepos, the repository config is shared across every workspace; there are no per-workspace Supipowers config files for general settings.
|
|
159
160
|
|
|
161
|
+
MemPalace hook timeouts are configured under `mempalace.timeouts`. Keep `hookMs` at or above `6000` when `mempalace.hooks.autoSearchOnPrompt` is enabled; MemPalace search can now pause before retrying a transient index lookup. The built-in default is `10000`.
|
|
162
|
+
|
|
160
163
|
## Release channels
|
|
161
164
|
|
|
162
165
|
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`.
|
|
@@ -213,6 +216,7 @@ Supipowers ships runtime-loaded prompt skills that are also available to the age
|
|
|
213
216
|
| `release` | `/supi:release` |
|
|
214
217
|
| `context-mode` | Context window guidance |
|
|
215
218
|
| `creating-supi-agents` | Agent creation guidance |
|
|
219
|
+
| `harness` | `/supi:harness` |
|
|
216
220
|
|
|
217
221
|
## Development
|
|
218
222
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supipowers",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Workflow extension for OMP coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
"test:watch": "bun test --timeout 20000 --watch tests/",
|
|
10
10
|
"test:evals": "bun test --timeout 20000 tests/evals/",
|
|
11
11
|
"build": "tsc -p tsconfig.build.json",
|
|
12
|
+
"ci": "bun run typecheck && bun run test",
|
|
12
13
|
"install:visual-server": "npm --prefix src/visual/scripts ci --ignore-scripts --no-audit --no-fund",
|
|
13
14
|
"postinstall": "bun run install:visual-server",
|
|
14
|
-
"prepare": "git config core.hooksPath hooks || true"
|
|
15
|
+
"prepare": "git config core.hooksPath hooks || true",
|
|
16
|
+
"dev-install": "bun run bin/dev-install.ts"
|
|
15
17
|
},
|
|
16
18
|
"engines": {
|
|
17
19
|
"bun": ">=1.3.10"
|
package/skills/harness/SKILL.md
CHANGED
|
@@ -78,6 +78,7 @@ Scorecard has lenient + strict scores (0–100). Strict counts `wontfix` items a
|
|
|
78
78
|
- `/supi:harness resolve <id>` — mark an entry resolved.
|
|
79
79
|
- `/supi:harness backlog` — list every open entry.
|
|
80
80
|
- `/supi:harness score` — recompute and display the score.
|
|
81
|
+
- `/supi:harness pr-comment [--dry-run] [--pr=N] [--repo=owner/repo] [--session=<id>] [--mode=every-push|on-status-change]` — render (or post) the sticky PR comment for the latest validate report. See `docs/supipowers/harness/pr-comment.md`.
|
|
81
82
|
|
|
82
83
|
## Conventions you MUST follow
|
|
83
84
|
|
package/src/bootstrap.ts
CHANGED
|
@@ -13,9 +13,7 @@ import { registerQaCommand } from "./commands/qa.js";
|
|
|
13
13
|
import { registerReleaseCommand, handleRelease } from "./commands/release.js";
|
|
14
14
|
import { registerUpdateCommand, handleUpdate } from "./commands/update.js";
|
|
15
15
|
import { registerDoctorCommand, handleDoctor } from "./commands/doctor.js";
|
|
16
|
-
import { registerMcpCommand, handleMcp, handleMcpCli, parseCliArgs } from "./commands/mcp.js";
|
|
17
16
|
import { registerModelCommand, handleModel } from "./commands/model.js";
|
|
18
|
-
import { executeManagerAction } from "./mcp/manager-tool.js";
|
|
19
17
|
import { registerFixPrCommand } from "./commands/fix-pr.js";
|
|
20
18
|
import { registerContextCommand, handleContext } from "./commands/context.js";
|
|
21
19
|
import { registerOptimizeContextCommand, handleOptimizeContext } from "./commands/optimize-context.js";
|
|
@@ -30,10 +28,6 @@ import { registerHarnessPipelineTools } from "./harness/tools.js";
|
|
|
30
28
|
import { registerHarnessHooks } from "./harness/hooks/register.js";
|
|
31
29
|
import { loadConfig } from "./config/loader.js";
|
|
32
30
|
import { registerContextModeHooks } from "./context-mode/hooks.js";
|
|
33
|
-
import { loadMcpRegistry } from "./mcp/config.js";
|
|
34
|
-
import { McpcClient } from "./mcp/mcpc.js";
|
|
35
|
-
import { parseTags } from "./mcp/activation.js";
|
|
36
|
-
import { initializeMcpServers, shutdownMcpServers } from "./mcp/lifecycle.js";
|
|
37
31
|
import { registerPlanApprovalHook } from "./planning/approval-flow.js";
|
|
38
32
|
import { registerPlanningSystemPromptHook } from "./planning/system-prompt.js";
|
|
39
33
|
import { registerPlanningAskTool, registerPlanningAskToolGuard } from "./planning/planning-ask-tool.js";
|
|
@@ -60,7 +54,6 @@ const TUI_COMMANDS: Record<string, (platform: Platform, ctx: any, args?: string)
|
|
|
60
54
|
"supi:review": (platform, ctx, args) => handleAiReview(platform, ctx, args),
|
|
61
55
|
"supi:update": (platform, ctx) => handleUpdate(platform, ctx),
|
|
62
56
|
"supi:doctor": (platform, ctx) => handleDoctor(platform, ctx),
|
|
63
|
-
"supi:mcp": (platform, ctx) => handleMcp(platform, ctx),
|
|
64
57
|
"supi:model": (platform, ctx) => handleModel(platform, ctx),
|
|
65
58
|
"supi:context": (platform, ctx) => handleContext(platform, ctx),
|
|
66
59
|
"supi:optimize-context": (platform, ctx, args) => handleOptimizeContext(platform, ctx, args),
|
|
@@ -74,8 +67,6 @@ const TUI_COMMANDS: Record<string, (platform: Platform, ctx: any, args?: string)
|
|
|
74
67
|
"supi:memory": (platform, ctx, args) => handleMemory(platform, ctx, args),
|
|
75
68
|
};
|
|
76
69
|
|
|
77
|
-
let pendingTags: string[] = [];
|
|
78
|
-
|
|
79
70
|
function getInstalledVersion(platform: Platform): string | null {
|
|
80
71
|
const pkgPath = platform.paths.agent("extensions", "supipowers", "package.json");
|
|
81
72
|
if (!existsSync(pkgPath)) return null;
|
|
@@ -99,7 +90,6 @@ export function bootstrap(platform: Platform): void {
|
|
|
99
90
|
registerUpdateCommand(platform);
|
|
100
91
|
registerFixPrCommand(platform);
|
|
101
92
|
registerDoctorCommand(platform);
|
|
102
|
-
registerMcpCommand(platform);
|
|
103
93
|
registerModelCommand(platform);
|
|
104
94
|
registerContextCommand(platform);
|
|
105
95
|
registerOptimizeContextCommand(platform);
|
|
@@ -131,17 +121,6 @@ export function bootstrap(platform: Platform): void {
|
|
|
131
121
|
// message submission, so no chat message appears and no "Working..." indicator
|
|
132
122
|
platform.on("input", (event, ctx) => {
|
|
133
123
|
const text = event.text.trim();
|
|
134
|
-
|
|
135
|
-
// Scan for $tags
|
|
136
|
-
const registry = loadMcpRegistry(platform.paths, ctx.cwd);
|
|
137
|
-
const registeredNames = new Set(Object.keys(registry.servers));
|
|
138
|
-
if (registeredNames.size > 0) {
|
|
139
|
-
const tags = parseTags(event.text, registeredNames);
|
|
140
|
-
if (tags.length > 0) {
|
|
141
|
-
pendingTags = tags;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
124
|
if (!text.startsWith("/")) return;
|
|
146
125
|
|
|
147
126
|
const spaceIndex = text.indexOf(" ");
|
|
@@ -157,14 +136,7 @@ export function bootstrap(platform: Platform): void {
|
|
|
157
136
|
|
|
158
137
|
// Context-mode integration
|
|
159
138
|
const config = loadConfig(platform.paths, process.cwd());
|
|
160
|
-
registerActiveToolController(platform, config
|
|
161
|
-
loadMcpRegistryForCwd: (cwd: string) => loadMcpRegistry(platform.paths, cwd),
|
|
162
|
-
consumePendingTags: () => {
|
|
163
|
-
const tags = pendingTags;
|
|
164
|
-
pendingTags = [];
|
|
165
|
-
return tags;
|
|
166
|
-
},
|
|
167
|
-
});
|
|
139
|
+
registerActiveToolController(platform, config);
|
|
168
140
|
registerContextModeHooks(platform, config);
|
|
169
141
|
registerMempalaceTool(platform, config);
|
|
170
142
|
registerMempalaceHooks(platform, config);
|
|
@@ -174,9 +146,9 @@ export function bootstrap(platform: Platform): void {
|
|
|
174
146
|
registerPlanningSystemPromptHook(platform);
|
|
175
147
|
registerUiDesignSystemPromptHook(platform);
|
|
176
148
|
|
|
177
|
-
// Register harness anti-slop hooks
|
|
178
|
-
//
|
|
179
|
-
//
|
|
149
|
+
// Register harness anti-slop hooks only for repos with a harness marker at extension boot.
|
|
150
|
+
// Registered handlers also check the marker per event, so removing the marker disables
|
|
151
|
+
// an already-started process without affecting other repos.
|
|
180
152
|
registerHarnessHooks(platform);
|
|
181
153
|
|
|
182
154
|
|
|
@@ -196,23 +168,6 @@ export function bootstrap(platform: Platform): void {
|
|
|
196
168
|
// OMP's StatusLine never clears hook statuses on /new, so extensions must do it.
|
|
197
169
|
ctx.ui?.setStatus?.("supi-model", undefined);
|
|
198
170
|
|
|
199
|
-
// MCP: always register mcpc_manager tool (agent needs it even with zero servers)
|
|
200
|
-
if (platform.registerTool) {
|
|
201
|
-
registerMcpcManagerTool(platform, ctx);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// MCP server initialization (only if servers configured)
|
|
205
|
-
const mcpRegistry = loadMcpRegistry(platform.paths, ctx.cwd);
|
|
206
|
-
if (Object.keys(mcpRegistry.servers).length > 0) {
|
|
207
|
-
const mcpClient = new McpcClient((cmd, args, opts) => platform.exec(cmd, args, opts));
|
|
208
|
-
const installed = await mcpClient.checkInstalled();
|
|
209
|
-
if (!installed.installed) {
|
|
210
|
-
ctx.ui.notify("mcpc not installed — MCP servers won't connect. Run /supi:upgrade to install.", "warning");
|
|
211
|
-
} else {
|
|
212
|
-
await initializeMcpServers(mcpRegistry, mcpClient);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
171
|
// Check for updates in the background
|
|
217
172
|
const currentVersion = getInstalledVersion(platform);
|
|
218
173
|
if (!currentVersion) return;
|
|
@@ -234,90 +189,7 @@ export function bootstrap(platform: Platform): void {
|
|
|
234
189
|
});
|
|
235
190
|
|
|
236
191
|
// Session shutdown
|
|
237
|
-
platform.on("session_shutdown", async (
|
|
192
|
+
platform.on("session_shutdown", async () => {
|
|
238
193
|
await stopActiveUiDesignSession();
|
|
239
|
-
|
|
240
|
-
const mcpConfig = loadConfig(platform.paths, ctx.cwd ?? process.cwd());
|
|
241
|
-
if (!mcpConfig.mcp?.closeSessionsOnExit) return;
|
|
242
|
-
|
|
243
|
-
const registry = loadMcpRegistry(platform.paths, ctx.cwd ?? process.cwd());
|
|
244
|
-
const mcpClient = new McpcClient((cmd, args, opts) => platform.exec(cmd, args, opts));
|
|
245
|
-
const names = Object.keys(registry.servers).filter((n) => registry.servers[n].enabled);
|
|
246
|
-
await shutdownMcpServers(names, mcpClient, true);
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function registerMcpcManagerTool(platform: Platform, ctx: any): void {
|
|
251
|
-
platform.registerTool!({
|
|
252
|
-
name: "mcpc_manager",
|
|
253
|
-
label: "MCP Server Manager",
|
|
254
|
-
description: "Add, remove, enable, disable, or refresh MCP servers managed by supipowers. Use this when the user asks to install, set up, or manage MCP servers.",
|
|
255
|
-
promptSnippet: "mcpc_manager — manage MCP servers (add, remove, enable, disable, refresh, login, logout, list, info)",
|
|
256
|
-
promptGuidelines: [
|
|
257
|
-
"Use when the user asks to install, add, or set up an MCP server",
|
|
258
|
-
"Use when the user asks to remove, disable, or manage an MCP server",
|
|
259
|
-
"Do NOT use for calling MCP tools — use the mcpc_<name> gateway tools instead",
|
|
260
|
-
],
|
|
261
|
-
parameters: {
|
|
262
|
-
type: "object",
|
|
263
|
-
properties: {
|
|
264
|
-
action: { type: "string", enum: ["add", "remove", "enable", "disable", "refresh", "login", "logout", "set-activation", "set-taggable", "list", "info"], description: "Action to perform" },
|
|
265
|
-
name: { type: "string", description: "Server name (required for all except list/refresh-all)" },
|
|
266
|
-
url: { type: "string", description: "Server URL (required for add)" },
|
|
267
|
-
transport: { type: "string", enum: ["http", "stdio"], description: "Transport type" },
|
|
268
|
-
docsUrl: { type: "string", description: "Documentation URL for richer README generation" },
|
|
269
|
-
activation: { type: "string", enum: ["always", "contextual", "disabled"], description: "Activation mode" },
|
|
270
|
-
taggable: { type: "boolean", description: "Whether $name tag activates this server" },
|
|
271
|
-
},
|
|
272
|
-
required: ["action"],
|
|
273
|
-
},
|
|
274
|
-
async execute(_toolCallId: string, params: any, _signal: any, _onUpdate: any, toolCtx: any) {
|
|
275
|
-
// First validate via routeManagerAction
|
|
276
|
-
const result = await executeManagerAction(params, {
|
|
277
|
-
hasUI: toolCtx.hasUI ?? true,
|
|
278
|
-
ui: toolCtx.ui ?? {},
|
|
279
|
-
cwd: toolCtx.cwd ?? ctx.cwd,
|
|
280
|
-
}, {
|
|
281
|
-
addServer: () => {},
|
|
282
|
-
removeServer: () => {},
|
|
283
|
-
updateServer: () => {},
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
if (result.error) {
|
|
287
|
-
throw new Error(result.content[0]?.text ?? "Manager action failed");
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// For actual operations, delegate to handleMcpCli
|
|
291
|
-
const cliArgs = buildCliArgsFromParams(params);
|
|
292
|
-
await handleMcpCli(platform, {
|
|
293
|
-
cwd: toolCtx.cwd ?? ctx.cwd,
|
|
294
|
-
hasUI: toolCtx.hasUI ?? true,
|
|
295
|
-
ui: {
|
|
296
|
-
notify: (msg: string) => {
|
|
297
|
-
// Collect notifications — they'll be in the tool result
|
|
298
|
-
},
|
|
299
|
-
...toolCtx.ui,
|
|
300
|
-
},
|
|
301
|
-
}, cliArgs);
|
|
302
|
-
|
|
303
|
-
return {
|
|
304
|
-
content: result.content,
|
|
305
|
-
details: { action: params.action, name: params.name },
|
|
306
|
-
};
|
|
307
|
-
},
|
|
308
194
|
});
|
|
309
195
|
}
|
|
310
|
-
|
|
311
|
-
function buildCliArgsFromParams(params: any): ReturnType<typeof parseCliArgs> {
|
|
312
|
-
return {
|
|
313
|
-
subcommand: params.action === "set-activation" ? "activation"
|
|
314
|
-
: params.action === "set-taggable" ? "tag"
|
|
315
|
-
: params.action,
|
|
316
|
-
name: params.name,
|
|
317
|
-
url: params.url,
|
|
318
|
-
transport: params.transport,
|
|
319
|
-
docsUrl: params.docsUrl,
|
|
320
|
-
activation: params.activation,
|
|
321
|
-
taggable: params.taggable,
|
|
322
|
-
};
|
|
323
|
-
}
|
package/src/commands/clear.ts
CHANGED
|
@@ -269,14 +269,14 @@ function parseArgs(args: string | undefined): ParsedArgs {
|
|
|
269
269
|
return { scope, dryRun };
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
export function handleClear(
|
|
272
|
+
export async function handleClear(
|
|
273
273
|
platform: Platform,
|
|
274
274
|
ctx: PlatformContext,
|
|
275
275
|
args?: string,
|
|
276
|
-
): void {
|
|
276
|
+
): Promise<void> {
|
|
277
277
|
if (!ctx.hasUI) return;
|
|
278
278
|
|
|
279
|
-
|
|
279
|
+
try {
|
|
280
280
|
const { scope, dryRun } = parseArgs(args);
|
|
281
281
|
const store = getMetricsStore();
|
|
282
282
|
const sessionId = getSessionId();
|
|
@@ -419,16 +419,16 @@ export function handleClear(
|
|
|
419
419
|
"error",
|
|
420
420
|
);
|
|
421
421
|
}
|
|
422
|
-
}
|
|
422
|
+
} catch (err) {
|
|
423
423
|
ctx.ui.notify(`Clear error: ${(err as Error).message}`, "error");
|
|
424
|
-
}
|
|
424
|
+
}
|
|
425
425
|
}
|
|
426
426
|
|
|
427
427
|
export function registerClearCommand(platform: Platform): void {
|
|
428
428
|
platform.registerCommand("supi:clear", {
|
|
429
429
|
description: "Clear metrics, cache, current-session knowledge, and memory for the active session (or `all` for the project)",
|
|
430
430
|
async handler(args: string | undefined, ctx: any) {
|
|
431
|
-
handleClear(platform, ctx, args);
|
|
431
|
+
await handleClear(platform, ctx, args);
|
|
432
432
|
},
|
|
433
433
|
});
|
|
434
434
|
}
|
package/src/commands/release.ts
CHANGED
|
@@ -51,6 +51,9 @@ import {
|
|
|
51
51
|
import {
|
|
52
52
|
checkDocDrift,
|
|
53
53
|
buildFixPrompt,
|
|
54
|
+
loadState as loadDriftState,
|
|
55
|
+
saveState as saveDriftState,
|
|
56
|
+
getHeadCommit,
|
|
54
57
|
} from "../docs/drift.js";
|
|
55
58
|
import { runQualityGates } from "../quality/runner.js";
|
|
56
59
|
import { REVIEW_GATE_REGISTRY } from "../quality/review-gates.js";
|
|
@@ -492,7 +495,6 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
492
495
|
progress.activate("doc-drift", "Fixing documentation");
|
|
493
496
|
notifyInfo(ctx, "Updating documentation", driftResult.summary);
|
|
494
497
|
const fixPrompt = buildFixPrompt(driftResult.findings);
|
|
495
|
-
const { loadState: loadDriftState, saveState: saveDriftState, getHeadCommit } = await import("../docs/drift.js");
|
|
496
498
|
const driftHead = await getHeadCommit(platform, repoRoot);
|
|
497
499
|
const driftState = loadDriftState(platform.paths, repoRoot);
|
|
498
500
|
saveDriftState(platform.paths, repoRoot, { ...driftState, lastCommit: driftHead, lastRunAt: new Date().toISOString() });
|
package/src/commands/update.ts
CHANGED
|
@@ -106,7 +106,7 @@ async function updateSupipowers(
|
|
|
106
106
|
cpSync(binSource, join(extDir, "bin"), { recursive: true });
|
|
107
107
|
}
|
|
108
108
|
// skills/ must live inside the extension dir — src/commands/agents.ts
|
|
109
|
-
// uses a static
|
|
109
|
+
// uses a static skills markdown import resolved relative to src/.
|
|
110
110
|
const skillsDirSource = join(downloadedRoot, "skills");
|
|
111
111
|
if (existsSync(skillsDirSource)) {
|
|
112
112
|
cpSync(skillsDirSource, join(extDir, "skills"), { recursive: true });
|
package/src/config/defaults.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/config/defaults.ts
|
|
2
2
|
import type { SupipowersConfig } from "../types.js";
|
|
3
|
+
import { MEMPALACE_PACKAGE_VERSION } from "../mempalace/upstream-limits.js";
|
|
3
4
|
|
|
4
5
|
export const DEFAULT_CONFIG: SupipowersConfig = {
|
|
5
6
|
version: "1.0.0",
|
|
@@ -39,7 +40,7 @@ export const DEFAULT_CONFIG: SupipowersConfig = {
|
|
|
39
40
|
lazyTools: {
|
|
40
41
|
enabled: true,
|
|
41
42
|
mode: "balanced",
|
|
42
|
-
alwaysKeep: ["ctx_execute", "ctx_search", "ctx_open_cached"
|
|
43
|
+
alwaysKeep: ["ctx_execute", "ctx_search", "ctx_open_cached"],
|
|
43
44
|
commandAllowlist: {},
|
|
44
45
|
keywordTools: {},
|
|
45
46
|
},
|
|
@@ -61,12 +62,9 @@ export const DEFAULT_CONFIG: SupipowersConfig = {
|
|
|
61
62
|
focusChainCadence: 6,
|
|
62
63
|
},
|
|
63
64
|
},
|
|
64
|
-
mcp: {
|
|
65
|
-
closeSessionsOnExit: false,
|
|
66
|
-
},
|
|
67
65
|
mempalace: {
|
|
68
66
|
enabled: true,
|
|
69
|
-
packageVersion:
|
|
67
|
+
packageVersion: MEMPALACE_PACKAGE_VERSION,
|
|
70
68
|
managedVenvPath: "~/.omp/supipowers/mempalace-venv",
|
|
71
69
|
palacePath: "~/.mempalace/palace",
|
|
72
70
|
defaultWingStrategy: "repo-name",
|
|
@@ -87,6 +85,8 @@ export const DEFAULT_CONFIG: SupipowersConfig = {
|
|
|
87
85
|
diaryChars: 8000,
|
|
88
86
|
autoSearchTokens: 150,
|
|
89
87
|
wakeUpInjectionEvery: 10,
|
|
88
|
+
autoSearchSimilarityFloor: 0.55,
|
|
89
|
+
autoSearchBm25Floor: 0.3,
|
|
90
90
|
},
|
|
91
91
|
timeouts: {
|
|
92
92
|
setupMs: 120000,
|
package/src/config/loader.ts
CHANGED
|
@@ -349,6 +349,7 @@ function migrateConfig(config: Record<string, unknown>): Record<string, unknown>
|
|
|
349
349
|
const legacyProfile = typeof migrated.defaultProfile === "string" ? migrated.defaultProfile : null;
|
|
350
350
|
delete migrated.defaultProfile;
|
|
351
351
|
delete migrated.orchestration;
|
|
352
|
+
delete migrated.mcp;
|
|
352
353
|
|
|
353
354
|
const qa = asRecord(migrated.qa);
|
|
354
355
|
const legacyTestCommand =
|
package/src/config/schema.ts
CHANGED
|
@@ -120,12 +120,6 @@ export const ConfigSchema = Type.Object(
|
|
|
120
120
|
},
|
|
121
121
|
{ additionalProperties: false },
|
|
122
122
|
),
|
|
123
|
-
mcp: Type.Object(
|
|
124
|
-
{
|
|
125
|
-
closeSessionsOnExit: Type.Boolean(),
|
|
126
|
-
},
|
|
127
|
-
{ additionalProperties: false },
|
|
128
|
-
),
|
|
129
123
|
mempalace: Type.Object(
|
|
130
124
|
{
|
|
131
125
|
enabled: Type.Boolean(),
|
|
@@ -158,6 +152,8 @@ export const ConfigSchema = Type.Object(
|
|
|
158
152
|
diaryChars: Type.Integer({ minimum: 1 }),
|
|
159
153
|
autoSearchTokens: Type.Integer({ minimum: 1 }),
|
|
160
154
|
wakeUpInjectionEvery: Type.Integer({ minimum: 1 }),
|
|
155
|
+
autoSearchSimilarityFloor: Type.Number({ minimum: 0, maximum: 1 }),
|
|
156
|
+
autoSearchBm25Floor: Type.Number({ minimum: 0 }),
|
|
161
157
|
},
|
|
162
158
|
{ additionalProperties: false },
|
|
163
159
|
),
|
package/src/context/analyzer.ts
CHANGED
|
@@ -59,41 +59,71 @@ export interface ParsedSkill {
|
|
|
59
59
|
export function parseIndividualSkills(systemPrompt: string): ParsedSkill[] {
|
|
60
60
|
if (!systemPrompt) return [];
|
|
61
61
|
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
62
|
+
// Locate `# Skills` and bound the section by the next h1 heading or end of
|
|
63
|
+
// text. The legacy bound used `^##\s`, which let the section bleed past
|
|
64
|
+
// sibling h2 headings (e.g. `## MCP Server Instructions`) into unrelated
|
|
65
|
+
// content and misidentify them as skills.
|
|
66
|
+
const headerMatch = systemPrompt.match(/^# Skills\b[^\n]*\n/m);
|
|
67
|
+
if (!headerMatch) return [];
|
|
68
|
+
const bodyStart = headerMatch.index! + headerMatch[0].length;
|
|
69
|
+
const after = systemPrompt.slice(bodyStart);
|
|
70
|
+
const nextH1 = after.search(/^# [^#]/m);
|
|
71
|
+
const skillsBody = nextH1 === -1 ? after : after.slice(0, nextH1);
|
|
72
|
+
|
|
73
|
+
// Modern OMP (≥14.7) renders skills as a bullet list: "- name: description"
|
|
74
|
+
// with descriptions that may wrap across multiple lines.
|
|
75
|
+
const bulletRegex = /^- ([a-zA-Z0-9._-]+):/gm;
|
|
76
|
+
const bullets: { name: string; index: number }[] = [];
|
|
77
|
+
let bm: RegExpExecArray | null;
|
|
78
|
+
while ((bm = bulletRegex.exec(skillsBody)) !== null) {
|
|
79
|
+
bullets.push({ name: bm[1], index: bm.index });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (bullets.length > 0) {
|
|
83
|
+
// Defensive upper bound for the last bullet: stop at any inline markdown
|
|
84
|
+
// heading inside the body. Real OMP prompts already terminate at an h1
|
|
85
|
+
// boundary, but synthetic / older prompts may not.
|
|
86
|
+
let bodyEnd = skillsBody.length;
|
|
87
|
+
const headingScan = /^#{1,6}\s/gm;
|
|
88
|
+
let hsm: RegExpExecArray | null;
|
|
89
|
+
while ((hsm = headingScan.exec(skillsBody)) !== null) {
|
|
90
|
+
if (hsm.index > bullets[bullets.length - 1].index) {
|
|
91
|
+
bodyEnd = hsm.index;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const bulletSkills: ParsedSkill[] = [];
|
|
97
|
+
for (let i = 0; i < bullets.length; i++) {
|
|
98
|
+
const start = bullets[i].index;
|
|
99
|
+
const end = i + 1 < bullets.length ? bullets[i + 1].index : bodyEnd;
|
|
100
|
+
const content = skillsBody.slice(start, end).trimEnd();
|
|
101
|
+
bulletSkills.push({
|
|
102
|
+
name: bullets[i].name,
|
|
103
|
+
bytes: byteLength(content),
|
|
104
|
+
tokens: estimateTokens(content),
|
|
105
|
+
content,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return bulletSkills;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Legacy / synthetic shape: "## name" h2 sub-headings under `# Skills`.
|
|
81
112
|
const headingRegex = /^## (.+)$/gm;
|
|
82
113
|
const headings: { name: string; index: number }[] = [];
|
|
83
|
-
let
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
headings.push({ name: match[1].trim(), index: match.index });
|
|
114
|
+
let hm: RegExpExecArray | null;
|
|
115
|
+
while ((hm = headingRegex.exec(skillsBody)) !== null) {
|
|
116
|
+
headings.push({ name: hm[1].trim(), index: hm.index });
|
|
87
117
|
}
|
|
88
118
|
|
|
119
|
+
const skills: ParsedSkill[] = [];
|
|
89
120
|
for (let i = 0; i < headings.length; i++) {
|
|
90
121
|
const start = headings[i].index;
|
|
91
122
|
const end = i + 1 < headings.length ? headings[i + 1].index : skillsBody.length;
|
|
92
123
|
const content = skillsBody.slice(start, end).trimEnd();
|
|
93
|
-
const bytes = byteLength(content);
|
|
94
124
|
skills.push({
|
|
95
125
|
name: headings[i].name,
|
|
96
|
-
bytes,
|
|
126
|
+
bytes: byteLength(content),
|
|
97
127
|
tokens: estimateTokens(content),
|
|
98
128
|
content,
|
|
99
129
|
});
|
|
@@ -218,26 +248,45 @@ function extractXmlSections(
|
|
|
218
248
|
sections: PromptSection[],
|
|
219
249
|
consumed: Set<number>,
|
|
220
250
|
): void {
|
|
221
|
-
// Project section FIRST (so nested <file> tags inside
|
|
222
|
-
|
|
223
|
-
|
|
251
|
+
// Project section FIRST (so nested <file> tags inside the wrapper are consumed).
|
|
252
|
+
// Modern OMP uses `<|START_PROJECT|>...<|END_PROJECT|>` pipe markers; older OMP
|
|
253
|
+
// and synthetic test inputs use the legacy `<project>...</project>` XML form.
|
|
254
|
+
const projectPatterns: RegExp[] = [
|
|
255
|
+
/<\|START_PROJECT\|>[\s\S]*?<\|END_PROJECT\|>/,
|
|
256
|
+
/<project>[\s\S]*?<\/project>/,
|
|
257
|
+
];
|
|
258
|
+
for (const pattern of projectPatterns) {
|
|
259
|
+
const projMatch = text.match(pattern);
|
|
260
|
+
if (!projMatch) continue;
|
|
224
261
|
sections.push({
|
|
225
262
|
label: "Project context",
|
|
226
263
|
bytes: byteLength(projMatch[0]),
|
|
227
264
|
content: projMatch[0],
|
|
228
265
|
});
|
|
229
266
|
markConsumed(consumed, projMatch.index!, projMatch.index! + projMatch[0].length);
|
|
267
|
+
break;
|
|
230
268
|
}
|
|
231
269
|
|
|
232
|
-
//
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
270
|
+
// Environment envelope (OMP ≥14.9.3) — workstation, tool catalog, LSP guidance.
|
|
271
|
+
const envMatch = text.match(/<\|START_ENV\|>[\s\S]*?<\|END_ENV\|>/);
|
|
272
|
+
if (envMatch) {
|
|
235
273
|
sections.push({
|
|
236
|
-
label: "
|
|
237
|
-
bytes: byteLength(
|
|
238
|
-
content:
|
|
274
|
+
label: "Environment",
|
|
275
|
+
bytes: byteLength(envMatch[0]),
|
|
276
|
+
content: envMatch[0],
|
|
239
277
|
});
|
|
240
|
-
markConsumed(consumed,
|
|
278
|
+
markConsumed(consumed, envMatch.index!, envMatch.index! + envMatch[0].length);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Contract envelope (OMP ≥14.9.3) — inviolable rules, yielding criteria.
|
|
282
|
+
const contractMatch = text.match(/<\|START_CONTRACT\|>[\s\S]*?<\|END_CONTRACT\|>/);
|
|
283
|
+
if (contractMatch) {
|
|
284
|
+
sections.push({
|
|
285
|
+
label: "Contract",
|
|
286
|
+
bytes: byteLength(contractMatch[0]),
|
|
287
|
+
content: contractMatch[0],
|
|
288
|
+
});
|
|
289
|
+
markConsumed(consumed, contractMatch.index!, contractMatch.index! + contractMatch[0].length);
|
|
241
290
|
}
|
|
242
291
|
|
|
243
292
|
// File sections — skip if already consumed (e.g., nested inside <project>)
|
|
@@ -318,6 +367,26 @@ function extractHeadingSections(
|
|
|
318
367
|
}
|
|
319
368
|
}
|
|
320
369
|
|
|
370
|
+
// Skills aggregate (OMP ≥14.7): markdown bullet list under `# Skills`.
|
|
371
|
+
// The legacy `<skills>` XML form is handled in extractXmlSections; this picks
|
|
372
|
+
// up the modern markdown shape rendered by OMP runtime. Bounded by the next
|
|
373
|
+
// h1/h2 heading so we don't swallow MCP instructions / Tools blocks.
|
|
374
|
+
const skillsHeading = text.match(/^# Skills\b[^\n]*\n/m);
|
|
375
|
+
if (skillsHeading && !consumed.has(skillsHeading.index!)) {
|
|
376
|
+
const start = skillsHeading.index!;
|
|
377
|
+
const afterHeading = text.slice(start + skillsHeading[0].length);
|
|
378
|
+
const nextHeading = afterHeading.search(/^#{1,2}\s/m);
|
|
379
|
+
const end = nextHeading === -1
|
|
380
|
+
? text.length
|
|
381
|
+
: start + skillsHeading[0].length + nextHeading;
|
|
382
|
+
const content = text.slice(start, end);
|
|
383
|
+
const bulletCount = (content.match(/^- [a-zA-Z0-9._-]+:/gm) || []).length;
|
|
384
|
+
if (bulletCount > 0) {
|
|
385
|
+
sections.push({ label: `Skills (${bulletCount})`, bytes: byteLength(content), content });
|
|
386
|
+
markConsumed(consumed, start, end);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
321
390
|
// Also recognize bare memory:// blocks without a heading
|
|
322
391
|
if (!sections.some((s) => s.label === "Memory")) {
|
|
323
392
|
const memoryMatch = text.match(/memory:\/\/\S+/);
|