omegon 0.6.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/.gitattributes +3 -0
- package/AGENTS.md +16 -0
- package/LICENSE +15 -0
- package/README.md +289 -0
- package/bin/pi.mjs +30 -0
- package/extensions/00-secrets/index.ts +1126 -0
- package/extensions/01-auth/auth.ts +401 -0
- package/extensions/01-auth/index.ts +289 -0
- package/extensions/auto-compact.ts +42 -0
- package/extensions/bootstrap/deps.ts +291 -0
- package/extensions/bootstrap/index.ts +811 -0
- package/extensions/chronos/chronos.sh +487 -0
- package/extensions/chronos/index.ts +148 -0
- package/extensions/cleave/assessment.ts +754 -0
- package/extensions/cleave/bridge.ts +31 -0
- package/extensions/cleave/conflicts.ts +250 -0
- package/extensions/cleave/dispatcher.ts +808 -0
- package/extensions/cleave/guardrails.ts +426 -0
- package/extensions/cleave/index.ts +3121 -0
- package/extensions/cleave/lifecycle-emitter.ts +20 -0
- package/extensions/cleave/openspec.ts +811 -0
- package/extensions/cleave/planner.ts +260 -0
- package/extensions/cleave/review.ts +579 -0
- package/extensions/cleave/skills.ts +355 -0
- package/extensions/cleave/types.ts +261 -0
- package/extensions/cleave/workspace.ts +861 -0
- package/extensions/cleave/worktree.ts +243 -0
- package/extensions/core-renderers.ts +253 -0
- package/extensions/dashboard/context-gauge.ts +58 -0
- package/extensions/dashboard/file-watch.ts +14 -0
- package/extensions/dashboard/footer.ts +1145 -0
- package/extensions/dashboard/git.ts +185 -0
- package/extensions/dashboard/index.ts +478 -0
- package/extensions/dashboard/memory-audit.ts +34 -0
- package/extensions/dashboard/overlay-data.ts +705 -0
- package/extensions/dashboard/overlay.ts +365 -0
- package/extensions/dashboard/render-utils.ts +54 -0
- package/extensions/dashboard/types.ts +191 -0
- package/extensions/dashboard/uri-helper.ts +45 -0
- package/extensions/debug.ts +69 -0
- package/extensions/defaults.ts +282 -0
- package/extensions/design-tree/dashboard-state.ts +161 -0
- package/extensions/design-tree/design-card.ts +362 -0
- package/extensions/design-tree/index.ts +2130 -0
- package/extensions/design-tree/lifecycle-emitter.ts +41 -0
- package/extensions/design-tree/tree.ts +1607 -0
- package/extensions/design-tree/types.ts +163 -0
- package/extensions/distill.ts +127 -0
- package/extensions/effort/index.ts +395 -0
- package/extensions/effort/tiers.ts +146 -0
- package/extensions/effort/types.ts +105 -0
- package/extensions/lib/git-state.ts +227 -0
- package/extensions/lib/local-models.ts +157 -0
- package/extensions/lib/model-preferences.ts +51 -0
- package/extensions/lib/model-routing.ts +720 -0
- package/extensions/lib/operator-fallback.ts +205 -0
- package/extensions/lib/operator-profile.ts +360 -0
- package/extensions/lib/slash-command-bridge.ts +253 -0
- package/extensions/lib/typebox-helpers.ts +16 -0
- package/extensions/local-inference/index.ts +727 -0
- package/extensions/mcp-bridge/README.md +220 -0
- package/extensions/mcp-bridge/index.ts +951 -0
- package/extensions/mcp-bridge/lib.ts +365 -0
- package/extensions/mcp-bridge/mcp.json +3 -0
- package/extensions/mcp-bridge/package.json +11 -0
- package/extensions/model-budget.ts +752 -0
- package/extensions/offline-driver.ts +403 -0
- package/extensions/openspec/archive-gate.ts +164 -0
- package/extensions/openspec/branch-cleanup.ts +64 -0
- package/extensions/openspec/dashboard-state.ts +50 -0
- package/extensions/openspec/index.ts +1917 -0
- package/extensions/openspec/lifecycle-emitter.ts +65 -0
- package/extensions/openspec/lifecycle-files.ts +70 -0
- package/extensions/openspec/lifecycle.ts +50 -0
- package/extensions/openspec/reconcile.ts +187 -0
- package/extensions/openspec/spec.ts +1385 -0
- package/extensions/openspec/types.ts +98 -0
- package/extensions/project-memory/DESIGN-global-mind.md +198 -0
- package/extensions/project-memory/README.md +202 -0
- package/extensions/project-memory/api-types.ts +382 -0
- package/extensions/project-memory/compaction-policy.ts +29 -0
- package/extensions/project-memory/core.ts +164 -0
- package/extensions/project-memory/embeddings.ts +230 -0
- package/extensions/project-memory/extraction-v2.ts +861 -0
- package/extensions/project-memory/factstore.ts +2177 -0
- package/extensions/project-memory/index.ts +3459 -0
- package/extensions/project-memory/injection-metrics.ts +91 -0
- package/extensions/project-memory/jsonl-io.ts +12 -0
- package/extensions/project-memory/lifecycle.ts +331 -0
- package/extensions/project-memory/migration.ts +293 -0
- package/extensions/project-memory/package.json +9 -0
- package/extensions/project-memory/sci-renderers.ts +7 -0
- package/extensions/project-memory/template.ts +103 -0
- package/extensions/project-memory/triggers.ts +52 -0
- package/extensions/project-memory/types.ts +102 -0
- package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
- package/extensions/render/composition/package-lock.json +534 -0
- package/extensions/render/composition/package.json +22 -0
- package/extensions/render/composition/render.mjs +246 -0
- package/extensions/render/composition/test-comp.tsx +87 -0
- package/extensions/render/composition/types.ts +24 -0
- package/extensions/render/excalidraw/UPSTREAM.md +81 -0
- package/extensions/render/excalidraw/elements.ts +764 -0
- package/extensions/render/excalidraw/index.ts +66 -0
- package/extensions/render/excalidraw/types.ts +223 -0
- package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
- package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
- package/extensions/render/excalidraw-renderer/render_template.html +59 -0
- package/extensions/render/index.ts +830 -0
- package/extensions/render/native-diagrams/index.ts +57 -0
- package/extensions/render/native-diagrams/motifs.ts +542 -0
- package/extensions/render/native-diagrams/raster.ts +8 -0
- package/extensions/render/native-diagrams/scene.ts +75 -0
- package/extensions/render/native-diagrams/spec.ts +204 -0
- package/extensions/render/native-diagrams/svg.ts +116 -0
- package/extensions/sci-ui.ts +304 -0
- package/extensions/session-log.ts +174 -0
- package/extensions/shared-state.ts +146 -0
- package/extensions/spinner-verbs.ts +91 -0
- package/extensions/style.ts +281 -0
- package/extensions/terminal-title.ts +191 -0
- package/extensions/tool-profile/index.ts +291 -0
- package/extensions/tool-profile/profiles.ts +290 -0
- package/extensions/types.d.ts +9 -0
- package/extensions/vault/index.ts +185 -0
- package/extensions/version-check.ts +90 -0
- package/extensions/view/index.ts +859 -0
- package/extensions/view/uri-resolver.ts +148 -0
- package/extensions/web-search/index.ts +182 -0
- package/extensions/web-search/providers.ts +121 -0
- package/extensions/web-ui/index.ts +110 -0
- package/extensions/web-ui/server.ts +265 -0
- package/extensions/web-ui/state.ts +462 -0
- package/extensions/web-ui/static/index.html +145 -0
- package/extensions/web-ui/types.ts +284 -0
- package/package.json +76 -0
- package/prompts/init.md +75 -0
- package/prompts/new-repo.md +54 -0
- package/prompts/oci-login.md +56 -0
- package/prompts/status.md +50 -0
- package/settings.json +4 -0
- package/skills/cleave/SKILL.md +218 -0
- package/skills/git/SKILL.md +209 -0
- package/skills/git/_reference/ci-validation.md +204 -0
- package/skills/oci/SKILL.md +338 -0
- package/skills/openspec/SKILL.md +346 -0
- package/skills/pi-extensions/SKILL.md +191 -0
- package/skills/pi-tui/SKILL.md +517 -0
- package/skills/python/SKILL.md +189 -0
- package/skills/rust/SKILL.md +268 -0
- package/skills/security/SKILL.md +206 -0
- package/skills/style/SKILL.md +264 -0
- package/skills/typescript/SKILL.md +225 -0
- package/skills/vault/SKILL.md +102 -0
- package/themes/alpharius-legacy.json +85 -0
- package/themes/alpharius.conf +59 -0
- package/themes/alpharius.json +88 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@cwilson613/pi-coding-agent";
|
|
2
|
+
import { basename } from "path";
|
|
3
|
+
import { DASHBOARD_UPDATE_EVENT, sharedState } from "./shared-state.ts";
|
|
4
|
+
import type { CleaveState } from "./dashboard/types.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Dynamic terminal tab title — rich status for multi-tab workflows.
|
|
8
|
+
*
|
|
9
|
+
* Shows agent state, current activity, tool execution, cleave dispatch,
|
|
10
|
+
* and dashboard mode in the terminal tab/window title bar.
|
|
11
|
+
*
|
|
12
|
+
* Format: π <project> [<status>] <activity> <flags>
|
|
13
|
+
*
|
|
14
|
+
* Examples:
|
|
15
|
+
* π omegon ✦ — idle, awaiting input
|
|
16
|
+
* π omegon ◆ fixing auth bug — thinking about user's request
|
|
17
|
+
* π omegon ⚙ Bash — executing a tool
|
|
18
|
+
* π omegon ⚙ Read → Edit — tool chain (last 2)
|
|
19
|
+
* π omegon ◆ fixing auth bug ✦ — done, awaiting next input
|
|
20
|
+
* π omegon ⚡ cleave 3/5 — cleave dispatch in progress
|
|
21
|
+
* π omegon ⚡ cleave ✓ — cleave complete
|
|
22
|
+
* π omegon T4 ◆ refactoring types — turn 4, thinking
|
|
23
|
+
*/
|
|
24
|
+
export default function (pi: ExtensionAPI) {
|
|
25
|
+
const project = basename(process.cwd());
|
|
26
|
+
|
|
27
|
+
// ── State ──────────────────────────────────────────────────
|
|
28
|
+
let ctx: ExtensionContext | null = null;
|
|
29
|
+
let promptSnippet = "";
|
|
30
|
+
let idle = true;
|
|
31
|
+
let turnIndex = 0;
|
|
32
|
+
|
|
33
|
+
// Tool chain — show last 2 tools for pipeline visibility
|
|
34
|
+
let toolChain: string[] = [];
|
|
35
|
+
let toolActive = false;
|
|
36
|
+
|
|
37
|
+
// Cleave state from shared dashboard state
|
|
38
|
+
let cleaveStatus: CleaveState["status"] = "idle";
|
|
39
|
+
let cleaveDone = 0;
|
|
40
|
+
let cleaveTotal = 0;
|
|
41
|
+
|
|
42
|
+
// ── Helpers ────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
function truncate(text: string, max: number): string {
|
|
45
|
+
const clean = text.split("\n")[0]!.trim().replace(/\s+/g, " ");
|
|
46
|
+
if (clean.length <= max) return clean;
|
|
47
|
+
return clean.slice(0, max).trimEnd() + "…";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Read cleave state from shared dashboard state */
|
|
51
|
+
function syncCleaveState(): void {
|
|
52
|
+
const cleave = sharedState.cleave;
|
|
53
|
+
if (cleave) {
|
|
54
|
+
cleaveStatus = cleave.status;
|
|
55
|
+
const children = cleave.children ?? [];
|
|
56
|
+
cleaveTotal = children.length;
|
|
57
|
+
cleaveDone = children.filter(c => c.status === "done").length;
|
|
58
|
+
} else {
|
|
59
|
+
cleaveStatus = "idle";
|
|
60
|
+
cleaveDone = 0;
|
|
61
|
+
cleaveTotal = 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function render() {
|
|
66
|
+
if (!ctx?.ui?.setTitle) return;
|
|
67
|
+
|
|
68
|
+
const parts: string[] = [`π ${project}`];
|
|
69
|
+
|
|
70
|
+
// Cleave dispatch — takes priority when active
|
|
71
|
+
const cleaveActive = cleaveStatus !== "idle" && cleaveStatus !== "done" && cleaveStatus !== "failed";
|
|
72
|
+
if (cleaveActive) {
|
|
73
|
+
if (cleaveStatus === "dispatching" || cleaveStatus === "merging") {
|
|
74
|
+
parts.push(`⚡ cleave ${cleaveDone}/${cleaveTotal}`);
|
|
75
|
+
} else {
|
|
76
|
+
parts.push(`⚡ ${cleaveStatus}`);
|
|
77
|
+
}
|
|
78
|
+
} else if (cleaveStatus === "done") {
|
|
79
|
+
parts.push("⚡ cleave ✓");
|
|
80
|
+
} else if (cleaveStatus === "failed") {
|
|
81
|
+
parts.push("⚡ cleave ✗");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Tool execution
|
|
85
|
+
if (toolActive && toolChain.length > 0) {
|
|
86
|
+
const display = toolChain.slice(-2).join(" → ");
|
|
87
|
+
parts.push(`⚙ ${display}`);
|
|
88
|
+
}
|
|
89
|
+
// Agent thinking (no active tool)
|
|
90
|
+
else if (!idle && promptSnippet && !cleaveActive) {
|
|
91
|
+
parts.push(`◆ ${promptSnippet}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Turn counter when actively working (T2+)
|
|
95
|
+
if (!idle && turnIndex >= 2) {
|
|
96
|
+
parts.push(`T${turnIndex}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Idle indicator
|
|
100
|
+
if (idle) {
|
|
101
|
+
parts.push("✦");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
ctx.ui.setTitle(parts.join(" "));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── Session lifecycle ──────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
function resetState(c: ExtensionContext) {
|
|
110
|
+
ctx = c;
|
|
111
|
+
promptSnippet = "";
|
|
112
|
+
toolChain = [];
|
|
113
|
+
toolActive = false;
|
|
114
|
+
idle = true;
|
|
115
|
+
turnIndex = 0;
|
|
116
|
+
cleaveStatus = "idle";
|
|
117
|
+
cleaveDone = 0;
|
|
118
|
+
cleaveTotal = 0;
|
|
119
|
+
setTimeout(render, 50);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
pi.on("session_start", (_e, c) => resetState(c));
|
|
123
|
+
pi.on("session_switch", (_e, c) => resetState(c));
|
|
124
|
+
pi.on("session_fork", (_e, c) => resetState(c));
|
|
125
|
+
|
|
126
|
+
// ── Agent lifecycle ────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
pi.on("before_agent_start", (event) => {
|
|
129
|
+
if (event.prompt) {
|
|
130
|
+
promptSnippet = truncate(event.prompt, 30);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
pi.on("agent_start", (_e, c) => {
|
|
135
|
+
ctx = c;
|
|
136
|
+
idle = false;
|
|
137
|
+
toolChain = [];
|
|
138
|
+
toolActive = false;
|
|
139
|
+
render();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
pi.on("turn_start", (event) => {
|
|
143
|
+
turnIndex = event.turnIndex;
|
|
144
|
+
render();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
pi.on("tool_execution_start", (event) => {
|
|
148
|
+
// Deduplicate consecutive same-tool calls
|
|
149
|
+
if (toolChain[toolChain.length - 1] !== event.toolName) {
|
|
150
|
+
toolChain.push(event.toolName);
|
|
151
|
+
}
|
|
152
|
+
toolActive = true;
|
|
153
|
+
render();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
pi.on("tool_execution_end", () => {
|
|
157
|
+
toolActive = false;
|
|
158
|
+
render();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
pi.on("agent_end", (_e, c) => {
|
|
162
|
+
ctx = c;
|
|
163
|
+
idle = true;
|
|
164
|
+
toolChain = [];
|
|
165
|
+
toolActive = false;
|
|
166
|
+
render();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// ── Session compaction ─────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
pi.on("session_compact", () => {
|
|
172
|
+
// Brief flash during compaction
|
|
173
|
+
const prev = promptSnippet;
|
|
174
|
+
promptSnippet = "compacting…";
|
|
175
|
+
render();
|
|
176
|
+
setTimeout(() => {
|
|
177
|
+
promptSnippet = prev;
|
|
178
|
+
render();
|
|
179
|
+
}, 2000);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ── Dashboard + cleave state updates ───────────────────────
|
|
183
|
+
|
|
184
|
+
pi.events.on(DASHBOARD_UPDATE_EVENT, (data) => {
|
|
185
|
+
const source = (data as Record<string, unknown>)?.source;
|
|
186
|
+
if (source === "cleave") {
|
|
187
|
+
syncCleaveState();
|
|
188
|
+
}
|
|
189
|
+
render();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tool-profile — Smart tool activation based on project context.
|
|
3
|
+
*
|
|
4
|
+
* On session_start, scans the cwd for project signals (package.json, .git,
|
|
5
|
+
* Cargo.toml, etc.) and activates only the relevant tool profiles. Saves
|
|
6
|
+
* ~12K tokens of context window by disabling irrelevant tools.
|
|
7
|
+
*
|
|
8
|
+
* Commands:
|
|
9
|
+
* /profile — Show active profiles and tool counts
|
|
10
|
+
* /profile <name> — Toggle a profile on/off
|
|
11
|
+
* /profile reset — Re-detect from project signals
|
|
12
|
+
*
|
|
13
|
+
* Tool (LLM-callable):
|
|
14
|
+
* manage_tools — List, enable, disable tools or switch profiles
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { ExtensionAPI } from "@cwilson613/pi-coding-agent";
|
|
18
|
+
import { Type } from "@sinclair/typebox";
|
|
19
|
+
import { StringEnum } from "../lib/typebox-helpers.ts";
|
|
20
|
+
import {
|
|
21
|
+
PROFILES,
|
|
22
|
+
detectProfiles,
|
|
23
|
+
loadProfileConfig,
|
|
24
|
+
resolveActiveTools,
|
|
25
|
+
formatProfileSummary,
|
|
26
|
+
type ProfileConfig,
|
|
27
|
+
} from "./profiles.ts";
|
|
28
|
+
|
|
29
|
+
export default function (pi: ExtensionAPI) {
|
|
30
|
+
let currentDetected: string[] = [];
|
|
31
|
+
let currentConfig: ProfileConfig = {};
|
|
32
|
+
let allToolNames: string[] = [];
|
|
33
|
+
|
|
34
|
+
function applyProfile(ctx: { ui: { notify: (msg: string, type?: "info" | "warning" | "error") => void } } | null): void {
|
|
35
|
+
const activeTools = resolveActiveTools(allToolNames, currentDetected, currentConfig);
|
|
36
|
+
pi.setActiveTools(activeTools);
|
|
37
|
+
if (ctx) {
|
|
38
|
+
const inactive = allToolNames.length - activeTools.length;
|
|
39
|
+
if (inactive > 0) {
|
|
40
|
+
ctx.ui.notify(`Profile: ${activeTools.length} tools active (${inactive} disabled)`, "info");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Session Start: Auto-detect ──────────────────────────────
|
|
46
|
+
|
|
47
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
48
|
+
allToolNames = pi.getAllTools().map((t) => t.name);
|
|
49
|
+
currentDetected = detectProfiles(ctx.cwd);
|
|
50
|
+
currentConfig = loadProfileConfig(ctx.cwd);
|
|
51
|
+
|
|
52
|
+
// Only apply if we'd actually disable something.
|
|
53
|
+
// If pi-dev profile detected, everything stays on anyway.
|
|
54
|
+
if (currentDetected.includes("pi-dev")) return;
|
|
55
|
+
|
|
56
|
+
applyProfile(ctx);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ── /profile Command ────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
pi.registerCommand("profile", {
|
|
62
|
+
description: "Show or toggle tool profiles (/profile [name|reset])",
|
|
63
|
+
handler: async (args, ctx) => {
|
|
64
|
+
const arg = args.trim().toLowerCase();
|
|
65
|
+
|
|
66
|
+
if (arg === "reset") {
|
|
67
|
+
allToolNames = pi.getAllTools().map((t) => t.name);
|
|
68
|
+
// Re-read getAllTools before detection since we may have reset
|
|
69
|
+
// Actually, getAllTools returns ALL registered (not just active), so this is fine
|
|
70
|
+
currentDetected = detectProfiles(ctx.cwd);
|
|
71
|
+
currentConfig = loadProfileConfig(ctx.cwd);
|
|
72
|
+
applyProfile(ctx);
|
|
73
|
+
ctx.ui.notify("Profiles re-detected from project context", "info");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (arg && arg !== "status") {
|
|
78
|
+
// Toggle a specific profile
|
|
79
|
+
const profile = PROFILES.find((p) => p.id === arg);
|
|
80
|
+
if (!profile) {
|
|
81
|
+
ctx.ui.notify(`Unknown profile: ${arg}. Available: ${PROFILES.map((p) => p.id).join(", ")}`, "warning");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const isActive = currentDetected.includes(arg) ||
|
|
86
|
+
currentConfig.include?.includes(arg);
|
|
87
|
+
const isExcluded = currentConfig.exclude?.includes(arg);
|
|
88
|
+
|
|
89
|
+
if (isActive && !isExcluded) {
|
|
90
|
+
// Disable it
|
|
91
|
+
if (!currentConfig.exclude) currentConfig.exclude = [];
|
|
92
|
+
currentConfig.exclude.push(arg);
|
|
93
|
+
// Remove from include if manually added
|
|
94
|
+
if (currentConfig.include) {
|
|
95
|
+
currentConfig.include = currentConfig.include.filter((id) => id !== arg);
|
|
96
|
+
}
|
|
97
|
+
applyProfile(ctx);
|
|
98
|
+
ctx.ui.notify(`Profile '${profile.label}' disabled`, "info");
|
|
99
|
+
} else {
|
|
100
|
+
// Enable it
|
|
101
|
+
if (!currentConfig.include) currentConfig.include = [];
|
|
102
|
+
currentConfig.include.push(arg);
|
|
103
|
+
// Remove from exclude
|
|
104
|
+
if (currentConfig.exclude) {
|
|
105
|
+
currentConfig.exclude = currentConfig.exclude.filter((id) => id !== arg);
|
|
106
|
+
}
|
|
107
|
+
applyProfile(ctx);
|
|
108
|
+
ctx.ui.notify(`Profile '${profile.label}' enabled`, "info");
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Show status
|
|
114
|
+
// Refresh allToolNames to show full catalog
|
|
115
|
+
const fullToolNames = pi.getAllTools().map((t) => t.name);
|
|
116
|
+
const summary = formatProfileSummary(currentDetected, currentConfig, fullToolNames);
|
|
117
|
+
ctx.ui.notify(summary, "info");
|
|
118
|
+
},
|
|
119
|
+
getArgumentCompletions: (prefix) => {
|
|
120
|
+
const options = [...PROFILES.map((p) => p.id), "reset", "status"];
|
|
121
|
+
return options
|
|
122
|
+
.filter((o) => o.startsWith(prefix))
|
|
123
|
+
.map((o) => ({ value: o, label: o, description: PROFILES.find((p) => p.id === o)?.description ?? o }));
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ── manage_tools Tool (LLM-callable) ────────────────────────
|
|
128
|
+
|
|
129
|
+
pi.registerTool({
|
|
130
|
+
name: "manage_tools",
|
|
131
|
+
label: "Manage Tools",
|
|
132
|
+
description: "List, enable, or disable tools and tool profiles. Use to activate tools the user requests or disable irrelevant ones to save context window space.",
|
|
133
|
+
promptSnippet: "manage_tools: list/enable/disable tools and profiles",
|
|
134
|
+
promptGuidelines: [
|
|
135
|
+
"Use manage_tools to enable tools when the user asks for a capability that's currently disabled",
|
|
136
|
+
"Use manage_tools with action 'list' to see what's available before trying to use a tool that might be disabled",
|
|
137
|
+
],
|
|
138
|
+
parameters: Type.Object({
|
|
139
|
+
action: StringEnum(["list", "enable", "disable", "profiles", "apply_profile"], {
|
|
140
|
+
description: "Action: list (show tools), enable/disable (toggle tools), profiles (show profiles), apply_profile (switch profile)",
|
|
141
|
+
}),
|
|
142
|
+
tools: Type.Optional(Type.Array(Type.String(), {
|
|
143
|
+
description: "Tool names to enable/disable (for enable/disable actions)",
|
|
144
|
+
})),
|
|
145
|
+
profile: Type.Optional(Type.String({
|
|
146
|
+
description: "Profile id to apply (for apply_profile action)",
|
|
147
|
+
})),
|
|
148
|
+
}),
|
|
149
|
+
execute: async (_toolCallId, params, _signal, _onUpdate, ctx) => {
|
|
150
|
+
// Refresh tool catalog
|
|
151
|
+
const allTools = pi.getAllTools();
|
|
152
|
+
const activeToolSet = new Set(pi.getActiveTools());
|
|
153
|
+
|
|
154
|
+
switch (params.action) {
|
|
155
|
+
case "list": {
|
|
156
|
+
const lines: string[] = [];
|
|
157
|
+
const active = allTools.filter((t) => activeToolSet.has(t.name));
|
|
158
|
+
const inactive = allTools.filter((t) => !activeToolSet.has(t.name));
|
|
159
|
+
|
|
160
|
+
lines.push(`Active tools (${active.length}):`);
|
|
161
|
+
for (const t of active) {
|
|
162
|
+
lines.push(` ✓ ${t.name}`);
|
|
163
|
+
}
|
|
164
|
+
if (inactive.length > 0) {
|
|
165
|
+
lines.push(`\nInactive tools (${inactive.length}):`);
|
|
166
|
+
for (const t of inactive) {
|
|
167
|
+
lines.push(` ○ ${t.name} — ${t.description?.slice(0, 80) ?? ""}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { content: [{ type: "text", text: lines.join("\n") }], details: undefined };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
case "enable": {
|
|
174
|
+
if (!params.tools?.length) {
|
|
175
|
+
return { content: [{ type: "text", text: "Error: provide tool names to enable" }], details: undefined } as any;
|
|
176
|
+
}
|
|
177
|
+
const currentActive = new Set(pi.getActiveTools());
|
|
178
|
+
const allNames = new Set(allTools.map((t) => t.name));
|
|
179
|
+
const added: string[] = [];
|
|
180
|
+
const notFound: string[] = [];
|
|
181
|
+
|
|
182
|
+
for (const name of params.tools) {
|
|
183
|
+
if (!allNames.has(name)) {
|
|
184
|
+
notFound.push(name);
|
|
185
|
+
} else if (!currentActive.has(name)) {
|
|
186
|
+
currentActive.add(name);
|
|
187
|
+
added.push(name);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
pi.setActiveTools([...currentActive]);
|
|
192
|
+
|
|
193
|
+
// Also track in config overrides so re-detect doesn't undo it
|
|
194
|
+
if (!currentConfig.tools) currentConfig.tools = {};
|
|
195
|
+
if (!currentConfig.tools.enable) currentConfig.tools.enable = [];
|
|
196
|
+
for (const name of added) {
|
|
197
|
+
if (!currentConfig.tools.enable.includes(name)) {
|
|
198
|
+
currentConfig.tools.enable.push(name);
|
|
199
|
+
}
|
|
200
|
+
// Remove from disable if present
|
|
201
|
+
if (currentConfig.tools.disable) {
|
|
202
|
+
currentConfig.tools.disable = currentConfig.tools.disable.filter((n) => n !== name);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const parts: string[] = [];
|
|
207
|
+
if (added.length) parts.push(`Enabled: ${added.join(", ")}`);
|
|
208
|
+
if (notFound.length) parts.push(`Not found: ${notFound.join(", ")}`);
|
|
209
|
+
return { content: [{ type: "text", text: parts.join(". ") || "No changes" }], details: undefined };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
case "disable": {
|
|
213
|
+
if (!params.tools?.length) {
|
|
214
|
+
return { content: [{ type: "text", text: "Error: provide tool names to disable" }], details: undefined } as any;
|
|
215
|
+
}
|
|
216
|
+
const currentActive = new Set(pi.getActiveTools());
|
|
217
|
+
const removed: string[] = [];
|
|
218
|
+
|
|
219
|
+
for (const name of params.tools) {
|
|
220
|
+
if (currentActive.has(name)) {
|
|
221
|
+
currentActive.delete(name);
|
|
222
|
+
removed.push(name);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
pi.setActiveTools([...currentActive]);
|
|
227
|
+
|
|
228
|
+
// Track in config
|
|
229
|
+
if (!currentConfig.tools) currentConfig.tools = {};
|
|
230
|
+
if (!currentConfig.tools.disable) currentConfig.tools.disable = [];
|
|
231
|
+
for (const name of removed) {
|
|
232
|
+
if (!currentConfig.tools.disable.includes(name)) {
|
|
233
|
+
currentConfig.tools.disable.push(name);
|
|
234
|
+
}
|
|
235
|
+
if (currentConfig.tools.enable) {
|
|
236
|
+
currentConfig.tools.enable = currentConfig.tools.enable.filter((n) => n !== name);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { content: [{ type: "text", text: removed.length ? `Disabled: ${removed.join(", ")}` : "No changes" }], details: undefined };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
case "profiles": {
|
|
244
|
+
const fullNames = allTools.map((t) => t.name);
|
|
245
|
+
const summary = formatProfileSummary(currentDetected, currentConfig, fullNames);
|
|
246
|
+
return { content: [{ type: "text", text: summary }], details: undefined };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case "apply_profile": {
|
|
250
|
+
if (!params.profile) {
|
|
251
|
+
return { content: [{ type: "text", text: "Error: provide a profile id" }], details: undefined } as any;
|
|
252
|
+
}
|
|
253
|
+
const profile = PROFILES.find((p) => p.id === params.profile);
|
|
254
|
+
if (!profile) {
|
|
255
|
+
const available = PROFILES.map((p) => p.id).join(", ");
|
|
256
|
+
return { content: [{ type: "text", text: `Unknown profile: ${params.profile}. Available: ${available}` }], details: undefined } as any;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Toggle the profile
|
|
260
|
+
const isExcluded = currentConfig.exclude?.includes(params.profile);
|
|
261
|
+
const isIncluded = currentDetected.includes(params.profile) || currentConfig.include?.includes(params.profile);
|
|
262
|
+
|
|
263
|
+
if (isIncluded && !isExcluded) {
|
|
264
|
+
// Already active — disable it
|
|
265
|
+
if (!currentConfig.exclude) currentConfig.exclude = [];
|
|
266
|
+
currentConfig.exclude.push(params.profile);
|
|
267
|
+
if (currentConfig.include) {
|
|
268
|
+
currentConfig.include = currentConfig.include.filter((id) => id !== params.profile);
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
// Activate it
|
|
272
|
+
if (!currentConfig.include) currentConfig.include = [];
|
|
273
|
+
currentConfig.include.push(params.profile);
|
|
274
|
+
if (currentConfig.exclude) {
|
|
275
|
+
currentConfig.exclude = currentConfig.exclude.filter((id) => id !== params.profile);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
allToolNames = allTools.map((t) => t.name);
|
|
280
|
+
applyProfile(null);
|
|
281
|
+
const activeCount = pi.getActiveTools().length;
|
|
282
|
+
const action = (isIncluded && !isExcluded) ? "disabled" : "enabled";
|
|
283
|
+
return { content: [{ type: "text", text: `Profile '${profile.label}' ${action}. ${activeCount}/${allToolNames.length} tools now active.` }], details: undefined };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
default:
|
|
287
|
+
return { content: [{ type: "text", text: `Unknown action: ${params.action}` }], details: undefined } as any;
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|