supipowers 1.2.6 → 1.5.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.
Files changed (137) hide show
  1. package/README.md +118 -56
  2. package/bin/install.ts +48 -128
  3. package/package.json +11 -3
  4. package/skills/code-review/SKILL.md +137 -40
  5. package/skills/context-mode/SKILL.md +67 -56
  6. package/skills/creating-supi-agents/SKILL.md +204 -0
  7. package/skills/debugging/SKILL.md +86 -40
  8. package/skills/fix-pr/SKILL.md +96 -65
  9. package/skills/planning/SKILL.md +103 -46
  10. package/skills/qa-strategy/SKILL.md +68 -46
  11. package/skills/receiving-code-review/SKILL.md +60 -53
  12. package/skills/release/SKILL.md +111 -39
  13. package/skills/tdd/SKILL.md +118 -67
  14. package/skills/verification/SKILL.md +71 -37
  15. package/src/bootstrap.ts +27 -5
  16. package/src/commands/agents.ts +249 -0
  17. package/src/commands/ai-review.ts +1113 -0
  18. package/src/commands/config.ts +224 -95
  19. package/src/commands/doctor.ts +19 -13
  20. package/src/commands/fix-pr.ts +8 -11
  21. package/src/commands/generate.ts +200 -0
  22. package/src/commands/model-picker.ts +5 -15
  23. package/src/commands/model.ts +4 -5
  24. package/src/commands/optimize-context.ts +202 -0
  25. package/src/commands/plan.ts +148 -92
  26. package/src/commands/qa.ts +14 -23
  27. package/src/commands/release.ts +504 -275
  28. package/src/commands/review.ts +643 -86
  29. package/src/commands/status.ts +44 -17
  30. package/src/commands/supi.ts +69 -41
  31. package/src/commands/update.ts +57 -2
  32. package/src/config/defaults.ts +6 -39
  33. package/src/config/loader.ts +388 -40
  34. package/src/config/model-resolver.ts +26 -22
  35. package/src/config/schema.ts +113 -48
  36. package/src/context/analyzer.ts +61 -2
  37. package/src/context/optimizer.ts +199 -0
  38. package/src/context-mode/compressor.ts +14 -11
  39. package/src/context-mode/detector.ts +16 -54
  40. package/src/context-mode/event-extractor.ts +45 -16
  41. package/src/context-mode/event-store.ts +225 -16
  42. package/src/context-mode/hooks.ts +195 -22
  43. package/src/context-mode/knowledge/chunker.ts +235 -0
  44. package/src/context-mode/knowledge/store.ts +187 -0
  45. package/src/context-mode/routing.ts +12 -23
  46. package/src/context-mode/sandbox/executor.ts +183 -0
  47. package/src/context-mode/sandbox/runners.ts +40 -0
  48. package/src/context-mode/snapshot-builder.ts +243 -7
  49. package/src/context-mode/tools.ts +440 -0
  50. package/src/context-mode/web/fetcher.ts +117 -0
  51. package/src/context-mode/web/html-to-md.ts +293 -0
  52. package/src/debug/logger.ts +107 -0
  53. package/src/deps/registry.ts +0 -20
  54. package/src/docs/drift.ts +454 -0
  55. package/src/fix-pr/fetch-comments.ts +66 -0
  56. package/src/git/commit-msg.ts +2 -1
  57. package/src/git/commit.ts +123 -141
  58. package/src/git/conventions.ts +2 -2
  59. package/src/git/status.ts +4 -1
  60. package/src/lsp/bridge.ts +138 -12
  61. package/src/planning/approval-flow.ts +125 -19
  62. package/src/planning/plan-writer-prompt.ts +4 -11
  63. package/src/planning/planning-ask-tool.ts +81 -0
  64. package/src/planning/prompt-builder.ts +9 -169
  65. package/src/planning/system-prompt.ts +290 -0
  66. package/src/platform/omp.ts +50 -4
  67. package/src/platform/progress.ts +182 -0
  68. package/src/platform/test-utils.ts +4 -1
  69. package/src/platform/tui-colors.ts +30 -0
  70. package/src/platform/types.ts +1 -0
  71. package/src/qa/detect-app-type.ts +102 -0
  72. package/src/qa/discover-routes.ts +353 -0
  73. package/src/quality/ai-session.ts +96 -0
  74. package/src/quality/ai-setup.ts +86 -0
  75. package/src/quality/gates/ai-review.ts +129 -0
  76. package/src/quality/gates/build.ts +8 -0
  77. package/src/quality/gates/command.ts +150 -0
  78. package/src/quality/gates/format.ts +28 -0
  79. package/src/quality/gates/lint.ts +22 -0
  80. package/src/quality/gates/lsp-diagnostics.ts +84 -0
  81. package/src/quality/gates/test-suite.ts +8 -0
  82. package/src/quality/gates/typecheck.ts +22 -0
  83. package/src/quality/registry.ts +25 -0
  84. package/src/quality/review-gates.ts +33 -0
  85. package/src/quality/runner.ts +268 -0
  86. package/src/quality/schemas.ts +48 -0
  87. package/src/quality/setup.ts +227 -0
  88. package/src/release/changelog.ts +7 -3
  89. package/src/release/channels/custom.ts +43 -0
  90. package/src/release/channels/gitea.ts +35 -0
  91. package/src/release/channels/github.ts +35 -0
  92. package/src/release/channels/gitlab.ts +35 -0
  93. package/src/release/channels/registry.ts +52 -0
  94. package/src/release/channels/types.ts +27 -0
  95. package/src/release/detector.ts +10 -63
  96. package/src/release/executor.ts +61 -51
  97. package/src/release/prompt.ts +38 -38
  98. package/src/release/version.ts +129 -10
  99. package/src/review/agent-loader.ts +331 -0
  100. package/src/review/consolidator.ts +180 -0
  101. package/src/review/default-agents/correctness.md +72 -0
  102. package/src/review/default-agents/maintainability.md +64 -0
  103. package/src/review/default-agents/security.md +67 -0
  104. package/src/review/fixer.ts +219 -0
  105. package/src/review/multi-agent-runner.ts +135 -0
  106. package/src/review/output.ts +147 -0
  107. package/src/review/prompts/agent-review-wrapper.md +36 -0
  108. package/src/review/prompts/fix-findings.md +32 -0
  109. package/src/review/prompts/fix-output-schema.md +18 -0
  110. package/src/review/prompts/invalid-output-retry.md +22 -0
  111. package/src/review/prompts/output-instructions.md +14 -0
  112. package/src/review/prompts/review-output-schema.md +38 -0
  113. package/src/review/prompts/single-review.md +53 -0
  114. package/src/review/prompts/validation-review.md +30 -0
  115. package/src/review/runner.ts +128 -0
  116. package/src/review/scope.ts +353 -0
  117. package/src/review/template.ts +15 -0
  118. package/src/review/types.ts +296 -0
  119. package/src/review/validator.ts +160 -0
  120. package/src/storage/plans.ts +5 -3
  121. package/src/storage/reports.ts +50 -7
  122. package/src/storage/review-sessions.ts +117 -0
  123. package/src/text.ts +19 -0
  124. package/src/types.ts +336 -26
  125. package/src/utils/paths.ts +39 -0
  126. package/src/visual/companion.ts +5 -3
  127. package/src/visual/start-server.ts +101 -0
  128. package/src/visual/stop-server.ts +39 -0
  129. package/bin/ctx-mode-wrapper.mjs +0 -66
  130. package/src/config/profiles.ts +0 -64
  131. package/src/context-mode/installer.ts +0 -38
  132. package/src/quality/ai-review-gate.ts +0 -43
  133. package/src/quality/gate-runner.ts +0 -67
  134. package/src/quality/lsp-gate.ts +0 -24
  135. package/src/quality/test-gate.ts +0 -39
  136. package/src/visual/scripts/start-server.sh +0 -98
  137. package/src/visual/scripts/stop-server.sh +0 -21
package/src/bootstrap.ts CHANGED
@@ -6,8 +6,9 @@ import { registerSupiCommand, handleSupi } from "./commands/supi.js";
6
6
  import { registerConfigCommand, handleConfig } from "./commands/config.js";
7
7
  import { registerStatusCommand, handleStatus } from "./commands/status.js";
8
8
  import { registerPlanCommand, getActiveVisualSessionDir, setActiveVisualSessionDir } from "./commands/plan.js";
9
- import { getScriptsDir } from "./visual/companion.js";
10
- import { registerReviewCommand } from "./commands/review.js";
9
+ import { stopVisualServer } from "./visual/stop-server.js";
10
+ import { registerChecksCommand, handleChecksCommand } from "./commands/review.js";
11
+ import { registerAiReviewCommand, handleAiReview } from "./commands/ai-review.js";
11
12
  import { registerQaCommand } from "./commands/qa.js";
12
13
  import { registerReleaseCommand, handleRelease } from "./commands/release.js";
13
14
  import { registerUpdateCommand, handleUpdate } from "./commands/update.js";
@@ -17,7 +18,10 @@ import { registerModelCommand, handleModel } from "./commands/model.js";
17
18
  import { executeManagerAction } from "./mcp/manager-tool.js";
18
19
  import { registerFixPrCommand } from "./commands/fix-pr.js";
19
20
  import { registerContextCommand, handleContext } from "./commands/context.js";
21
+ import { registerOptimizeContextCommand, handleOptimizeContext } from "./commands/optimize-context.js";
20
22
  import { registerCommitCommand, handleCommit } from "./commands/commit.js";
23
+ import { registerGenerateCommand } from "./commands/generate.js";
24
+ import { registerAgentsCommand, handleAgents } from "./commands/agents.js";
21
25
  import { loadConfig } from "./config/loader.js";
22
26
  import { registerContextModeHooks } from "./context-mode/hooks.js";
23
27
  import { loadMcpRegistry } from "./mcp/config.js";
@@ -25,6 +29,8 @@ import { McpcClient } from "./mcp/mcpc.js";
25
29
  import { parseTags, computeActiveServers } from "./mcp/activation.js";
26
30
  import { initializeMcpServers, shutdownMcpServers } from "./mcp/lifecycle.js";
27
31
  import { registerPlanApprovalHook } from "./planning/approval-flow.js";
32
+ import { registerPlanningSystemPromptHook } from "./planning/system-prompt.js";
33
+ import { registerPlanningAskTool } from "./planning/planning-ask-tool.js";
28
34
 
29
35
  // TUI-only commands — intercepted at the input level to prevent
30
36
  // message submission and "Working..." indicator
@@ -32,13 +38,17 @@ const TUI_COMMANDS: Record<string, (platform: Platform, ctx: any, args?: string)
32
38
  "supi": (platform, ctx) => handleSupi(platform, ctx),
33
39
  "supi:config": (platform, ctx) => handleConfig(platform, ctx),
34
40
  "supi:status": (platform, ctx) => handleStatus(platform, ctx),
41
+ "supi:review": (platform, ctx) => handleAiReview(platform, ctx),
35
42
  "supi:update": (platform, ctx) => handleUpdate(platform, ctx),
36
43
  "supi:doctor": (platform, ctx) => handleDoctor(platform, ctx),
37
44
  "supi:mcp": (platform, ctx) => handleMcp(platform, ctx),
38
45
  "supi:model": (platform, ctx) => handleModel(platform, ctx),
39
46
  "supi:context": (platform, ctx) => handleContext(platform, ctx),
47
+ "supi:optimize-context": (platform, ctx) => handleOptimizeContext(platform, ctx),
40
48
  "supi:commit": (platform, ctx, args) => handleCommit(platform, ctx, args),
41
49
  "supi:release": (platform, ctx, args) => handleRelease(platform, ctx, args),
50
+ "supi:checks": (platform, ctx, args) => handleChecksCommand(platform, ctx, args),
51
+ "supi:agents": (platform, ctx, args) => handleAgents(platform, ctx, args),
42
52
  };
43
53
 
44
54
  let pendingTags: string[] = [];
@@ -59,7 +69,8 @@ export function bootstrap(platform: Platform): void {
59
69
  registerConfigCommand(platform);
60
70
  registerStatusCommand(platform);
61
71
  registerPlanCommand(platform);
62
- registerReviewCommand(platform);
72
+ registerChecksCommand(platform);
73
+ registerAiReviewCommand(platform);
63
74
  registerQaCommand(platform);
64
75
  registerReleaseCommand(platform);
65
76
  registerUpdateCommand(platform);
@@ -68,11 +79,15 @@ export function bootstrap(platform: Platform): void {
68
79
  registerMcpCommand(platform);
69
80
  registerModelCommand(platform);
70
81
  registerContextCommand(platform);
82
+ registerOptimizeContextCommand(platform);
71
83
  registerCommitCommand(platform);
84
+ registerGenerateCommand(platform);
85
+ registerAgentsCommand(platform);
72
86
 
73
87
 
74
88
  // Register plan approval flow (agent_end hook for plan approval UI)
75
89
  registerPlanApprovalHook(platform);
90
+ registerPlanningAskTool(platform);
76
91
 
77
92
 
78
93
  // Intercept TUI-only commands at the input level — this runs BEFORE
@@ -108,6 +123,10 @@ export function bootstrap(platform: Platform): void {
108
123
  registerContextModeHooks(platform, config);
109
124
 
110
125
 
126
+ // Planning-mode prompt override — registered after context-mode so it wins
127
+ // when /supi:plan is active and otherwise stays dormant.
128
+ registerPlanningSystemPromptHook(platform);
129
+
111
130
  // MCP per-turn activation
112
131
  platform.on("before_agent_start", async (event, ctx) => {
113
132
  const registry = loadMcpRegistry(platform.paths, ctx.cwd);
@@ -136,11 +155,14 @@ export function bootstrap(platform: Platform): void {
136
155
  // Clean up any leftover visual companion from a previous session
137
156
  const previousVisualDir = getActiveVisualSessionDir();
138
157
  if (previousVisualDir) {
139
- const stopScript = join(getScriptsDir(), "stop-server.sh");
140
- platform.exec("bash", [stopScript, previousVisualDir], { cwd: getScriptsDir() }).catch(() => {});
158
+ stopVisualServer(previousVisualDir);
141
159
  setActiveVisualSessionDir(null);
142
160
  }
143
161
 
162
+ // Clear leftover model-override status from a previous session.
163
+ // OMP's StatusLine never clears hook statuses on /new, so extensions must do it.
164
+ ctx.ui?.setStatus?.("supi-model", undefined);
165
+
144
166
  // MCP: always register mcpc_manager tool (agent needs it even with zero servers)
145
167
  if (platform.registerTool) {
146
168
  registerMcpcManagerTool(platform, ctx);
@@ -0,0 +1,249 @@
1
+ import type { Platform } from "../platform/types.js";
2
+ import {
3
+ loadMergedReviewAgents,
4
+ writeAgentFile,
5
+ addAgentToConfig,
6
+ getReviewAgentsDir,
7
+ getReviewAgentsConfigPath,
8
+ getGlobalReviewAgentsDir,
9
+ getGlobalReviewAgentsConfigPath,
10
+ } from "../review/agent-loader.js";
11
+ import { selectModelFromList } from "./model.js";
12
+ import type { ConfiguredReviewAgent } from "../types.js";
13
+ import creatingAgentsSkill from "../../skills/creating-supi-agents/SKILL.md" with { type: "text" };
14
+
15
+ // ── List View ──────────────────────────────────────────────────
16
+
17
+ function buildAgentDashboard(agents: ConfiguredReviewAgent[]): string {
18
+ const nameCol = 18;
19
+ const modelCol = 24;
20
+ const scopeCol = 10;
21
+
22
+ const lines: string[] = [
23
+ "\n Review Agents\n",
24
+ ` ${"name".padEnd(nameCol)} ${"model".padEnd(modelCol)} ${"scope".padEnd(scopeCol)} focus`,
25
+ ];
26
+
27
+ for (const agent of agents) {
28
+ const name = agent.name.padEnd(nameCol);
29
+ const model = (agent.model ?? "—").padEnd(modelCol);
30
+ const scope = (agent.scope ?? "project").padEnd(scopeCol);
31
+ const focus = agent.focus
32
+ ? agent.focus.length > 40
33
+ ? agent.focus.slice(0, 37) + "..."
34
+ : agent.focus
35
+ : "—";
36
+ lines.push(` ${name} ${model} ${scope} ${focus}`);
37
+ }
38
+
39
+ const globalCount = agents.filter((a) => a.scope === "global").length;
40
+ const projectCount = agents.filter((a) => a.scope === "project" || !a.scope).length;
41
+ lines.push("");
42
+ lines.push(` ${agents.length} agent(s) (${projectCount} project, ${globalCount} global)`);
43
+ lines.push("");
44
+
45
+ return lines.join("\n");
46
+ }
47
+
48
+ // ── Create Flow ────────────────────────────────────────────────
49
+
50
+ const KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
51
+
52
+ export async function runAgentCreateFlow(platform: Platform, ctx: any): Promise<void> {
53
+ if (!ctx.hasUI) {
54
+ ctx.ui.notify("Agent creation requires interactive mode", "warning");
55
+ return;
56
+ }
57
+
58
+ // Step 1: Scope selection
59
+ const scopeChoice = await ctx.ui.select("Where should this agent live?", [
60
+ "This project",
61
+ "Global",
62
+ ], { helpText: "Select scope · Esc to cancel" });
63
+
64
+ if (!scopeChoice) return;
65
+ const scope: "global" | "project" = scopeChoice === "Global" ? "global" : "project";
66
+
67
+ // Step 2: Agent name
68
+ const nameInput = await ctx.ui.input("Agent name (kebab-case)", {
69
+ helpText: "e.g. performance, api-design, accessibility",
70
+ });
71
+
72
+ if (!nameInput) return;
73
+ const agentName = nameInput.trim().toLowerCase();
74
+
75
+ if (!KEBAB_CASE_RE.test(agentName)) {
76
+ ctx.ui.notify(`Invalid agent name "${agentName}" — must be kebab-case (e.g. "my-agent")`, "error");
77
+ return;
78
+ }
79
+
80
+ // Check for name collision in target scope
81
+ try {
82
+ const existing = await loadMergedReviewAgents(platform.paths, ctx.cwd);
83
+ const collision = existing.agents.find(
84
+ (a) => a.name === agentName && (a.scope ?? "project") === scope,
85
+ );
86
+ if (collision) {
87
+ ctx.ui.notify(`Agent "${agentName}" already exists in ${scope} scope`, "error");
88
+ return;
89
+ }
90
+ } catch {
91
+ // If loading fails (e.g., no project dir), we can continue
92
+ }
93
+
94
+ // Step 3: Model selection
95
+ const modelInput = await selectModelFromList(ctx);
96
+ // null means "inherit default" — that's fine
97
+
98
+ // Step 4: Prompt source
99
+ const promptChoice = await ctx.ui.select("Agent prompt", [
100
+ "Send a prompt",
101
+ "Create from zero",
102
+ ], { helpText: "Choose how to provide the agent prompt" });
103
+
104
+ if (!promptChoice) return;
105
+
106
+ if (promptChoice === "Create from zero") {
107
+ // Load the creating-supi-agents skill via steer
108
+ loadSkillAndSteer(platform, ctx, scope, agentName, modelInput);
109
+ return;
110
+ }
111
+
112
+ // "Send a prompt" path
113
+ const promptBody = await ctx.ui.input("Paste your agent prompt", {
114
+ helpText: "The instructions the agent follows when reviewing code",
115
+ });
116
+
117
+ if (!promptBody?.trim()) {
118
+ ctx.ui.notify("Agent creation cancelled — empty prompt", "warning");
119
+ return;
120
+ }
121
+
122
+ // Step 5: Save
123
+ const agentsDir = scope === "global"
124
+ ? getGlobalReviewAgentsDir(platform.paths)
125
+ : getReviewAgentsDir(platform.paths, ctx.cwd);
126
+ const configPath = scope === "global"
127
+ ? getGlobalReviewAgentsConfigPath(platform.paths)
128
+ : getReviewAgentsConfigPath(platform.paths, ctx.cwd);
129
+
130
+ const fileName = writeAgentFile(agentsDir, agentName, {
131
+ name: agentName,
132
+ description: `${agentName} review agent`,
133
+ focus: null,
134
+ }, promptBody.trim());
135
+
136
+ await addAgentToConfig(configPath, {
137
+ name: agentName,
138
+ enabled: true,
139
+ data: fileName,
140
+ model: modelInput,
141
+ });
142
+
143
+ ctx.ui.notify(`Agent "${agentName}" created (${scope})`, "info");
144
+ }
145
+
146
+ // ── Skill Integration (steer pattern) ──────────────────────────
147
+
148
+ function loadSkillAndSteer(
149
+ platform: Platform,
150
+ ctx: any,
151
+ scope: "global" | "project",
152
+ agentName: string,
153
+ model: string | null,
154
+ ): void {
155
+ const agentsDir = scope === "global"
156
+ ? getGlobalReviewAgentsDir(platform.paths)
157
+ : getReviewAgentsDir(platform.paths, ctx.cwd);
158
+ const configPath = scope === "global"
159
+ ? getGlobalReviewAgentsConfigPath(platform.paths)
160
+ : getReviewAgentsConfigPath(platform.paths, ctx.cwd);
161
+
162
+ const prompt = buildSkillSteerPrompt(agentName, scope, agentsDir, configPath, model);
163
+
164
+ platform.sendMessage(
165
+ {
166
+ customType: "supi-agents-create",
167
+ content: [{ type: "text", text: prompt }],
168
+ display: "none",
169
+ },
170
+ { deliverAs: "steer", triggerTurn: true },
171
+ );
172
+ }
173
+
174
+ function buildSkillSteerPrompt(
175
+ agentName: string,
176
+ scope: "global" | "project",
177
+ agentsDir: string,
178
+ configPath: string,
179
+ model: string | null,
180
+ ): string {
181
+ return `You are guiding the user through creating a new AI review agent for supipowers' multi-agent code review pipeline.
182
+
183
+ ## Agent Details (already collected)
184
+ - **Name**: ${agentName}
185
+ - **Scope**: ${scope}
186
+ - **Model**: ${model ?? "inherit default"}
187
+ - **Target directory**: ${agentsDir}
188
+ - **Config path**: ${configPath}
189
+
190
+ ## Skill Instructions
191
+
192
+ ${creatingAgentsSkill}
193
+
194
+ ## Save Instructions
195
+
196
+ Once the user approves the agent design, save it:
197
+ 1. Write the markdown file to: ${agentsDir}/${agentName}.md
198
+ - The file MUST have YAML frontmatter (name, description, focus) and a prompt body ending with {output_instructions}
199
+ 2. Update config at: ${configPath}
200
+ - Add entry: { name: "${agentName}", enabled: true, data: "${agentName}.md", model: ${model ? `"${model}"` : "null"} }
201
+
202
+ Use the \`writeAgentFile\` and \`addAgentToConfig\` functions from \`src/review/agent-loader.ts\` if you have tool access, or write the files directly.
203
+
204
+ Start by asking the user about the goal/focus of their "${agentName}" agent.`;
205
+ }
206
+
207
+ // ── Command Entry Point ────────────────────────────────────────
208
+
209
+ export function handleAgents(platform: Platform, ctx: any, args?: string): void {
210
+ if (args?.trim() === "create") {
211
+ runAgentCreateFlow(platform, ctx).catch((err: Error) => {
212
+ ctx.ui.notify(`Agent creation error: ${err.message}`, "error");
213
+ });
214
+ return;
215
+ }
216
+
217
+ // Solo invocation: show dashboard
218
+ showAgentsDashboard(platform, ctx).catch((err: Error) => {
219
+ ctx.ui.notify(`Error loading agents: ${err.message}`, "error");
220
+ });
221
+ }
222
+
223
+ async function showAgentsDashboard(platform: Platform, ctx: any): Promise<void> {
224
+ const result = await loadMergedReviewAgents(platform.paths, ctx.cwd);
225
+ const dashboard = buildAgentDashboard(result.agents);
226
+ ctx.ui.notify(dashboard, "info");
227
+ }
228
+
229
+ // ── Registration ───────────────────────────────────────────────
230
+
231
+ const SUBCOMMANDS = [
232
+ { name: "create", description: "Create a new review agent" },
233
+ ] as const;
234
+
235
+ export function registerAgentsCommand(platform: Platform): void {
236
+ platform.registerCommand("supi:agents", {
237
+ description: "List and manage review agents",
238
+ getArgumentCompletions(prefix: string) {
239
+ const lower = prefix.toLowerCase();
240
+ const matches = SUBCOMMANDS
241
+ .filter((s) => s.name.startsWith(lower))
242
+ .map((s) => ({ value: `${s.name} `, label: s.name, description: s.description }));
243
+ return matches.length > 0 ? matches : null;
244
+ },
245
+ async handler(args: string | undefined, ctx: any) {
246
+ handleAgents(platform, ctx, args);
247
+ },
248
+ });
249
+ }