@oh-my-pi/pi-coding-agent 12.7.6 → 12.8.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 (56) hide show
  1. package/CHANGELOG.md +37 -37
  2. package/README.md +9 -1052
  3. package/package.json +7 -7
  4. package/src/cli/args.ts +1 -0
  5. package/src/cli/update-cli.ts +49 -35
  6. package/src/cli/web-search-cli.ts +3 -2
  7. package/src/commands/web-search.ts +1 -0
  8. package/src/config/model-registry.ts +6 -0
  9. package/src/config/settings-schema.ts +25 -3
  10. package/src/config/settings.ts +1 -0
  11. package/src/extensibility/extensions/wrapper.ts +20 -13
  12. package/src/extensibility/slash-commands.ts +12 -91
  13. package/src/lsp/client.ts +24 -27
  14. package/src/lsp/index.ts +92 -42
  15. package/src/mcp/config-writer.ts +33 -0
  16. package/src/mcp/config.ts +6 -1
  17. package/src/mcp/types.ts +1 -0
  18. package/src/modes/components/custom-editor.ts +8 -5
  19. package/src/modes/components/settings-defs.ts +2 -1
  20. package/src/modes/controllers/command-controller.ts +12 -6
  21. package/src/modes/controllers/input-controller.ts +21 -186
  22. package/src/modes/controllers/mcp-command-controller.ts +60 -3
  23. package/src/modes/interactive-mode.ts +2 -2
  24. package/src/modes/types.ts +1 -1
  25. package/src/sdk.ts +23 -1
  26. package/src/secrets/index.ts +116 -0
  27. package/src/secrets/obfuscator.ts +269 -0
  28. package/src/secrets/regex.ts +21 -0
  29. package/src/session/agent-session.ts +143 -21
  30. package/src/session/compaction/branch-summarization.ts +2 -2
  31. package/src/session/compaction/compaction.ts +10 -3
  32. package/src/session/compaction/utils.ts +25 -1
  33. package/src/slash-commands/builtin-registry.ts +419 -0
  34. package/src/web/scrapers/github.ts +50 -12
  35. package/src/web/search/index.ts +5 -5
  36. package/src/web/search/provider.ts +13 -2
  37. package/src/web/search/providers/brave.ts +165 -0
  38. package/src/web/search/types.ts +1 -1
  39. package/docs/compaction.md +0 -436
  40. package/docs/config-usage.md +0 -176
  41. package/docs/custom-tools.md +0 -585
  42. package/docs/environment-variables.md +0 -257
  43. package/docs/extension-loading.md +0 -106
  44. package/docs/extensions.md +0 -1342
  45. package/docs/fs-scan-cache-architecture.md +0 -50
  46. package/docs/hooks.md +0 -906
  47. package/docs/models.md +0 -234
  48. package/docs/python-repl.md +0 -110
  49. package/docs/rpc.md +0 -1173
  50. package/docs/sdk.md +0 -1039
  51. package/docs/session-tree-plan.md +0 -84
  52. package/docs/session.md +0 -368
  53. package/docs/skills.md +0 -254
  54. package/docs/theme.md +0 -696
  55. package/docs/tree.md +0 -206
  56. package/docs/tui.md +0 -487
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "12.7.6",
3
+ "version": "12.8.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "bin": {
@@ -84,12 +84,12 @@
84
84
  },
85
85
  "dependencies": {
86
86
  "@mozilla/readability": "0.6.0",
87
- "@oh-my-pi/omp-stats": "12.7.6",
88
- "@oh-my-pi/pi-agent-core": "12.7.6",
89
- "@oh-my-pi/pi-ai": "12.7.6",
90
- "@oh-my-pi/pi-natives": "12.7.6",
91
- "@oh-my-pi/pi-tui": "12.7.6",
92
- "@oh-my-pi/pi-utils": "12.7.6",
87
+ "@oh-my-pi/omp-stats": "12.8.0",
88
+ "@oh-my-pi/pi-agent-core": "12.8.0",
89
+ "@oh-my-pi/pi-ai": "12.8.0",
90
+ "@oh-my-pi/pi-natives": "12.8.0",
91
+ "@oh-my-pi/pi-tui": "12.8.0",
92
+ "@oh-my-pi/pi-utils": "12.8.0",
93
93
  "@sinclair/typebox": "^0.34.48",
94
94
  "@xterm/headless": "^6.0.0",
95
95
  "ajv": "^8.18.0",
package/src/cli/args.ts CHANGED
@@ -213,6 +213,7 @@ export function getExtraHelpText(): string {
213
213
 
214
214
  ${chalk.dim("# Search & Tools")}
215
215
  EXA_API_KEY - Exa web search
216
+ BRAVE_API_KEY - Brave web search
216
217
  PERPLEXITY_API_KEY - Perplexity web search
217
218
  ANTHROPIC_SEARCH_API_KEY - Anthropic search provider
218
219
 
@@ -80,7 +80,7 @@ async function getLatestRelease(): Promise<ReleaseInfo> {
80
80
  return {
81
81
  tag,
82
82
  version,
83
- assets: [makeAsset(getBinaryName()), makeAsset(getNativeAddonName())],
83
+ assets: [makeAsset(getBinaryName()), ...getNativeAddonNames().map(makeAsset)],
84
84
  };
85
85
  }
86
86
 
@@ -143,13 +143,11 @@ function getBinaryName(): string {
143
143
  }
144
144
 
145
145
  /**
146
- * Get the appropriate native addon name for this platform.
147
- * Uses process.platform directly (linux, darwin, win32).
146
+ * Get native addon names for this platform, ordered by preference.
148
147
  */
149
- function getNativeAddonName(): string {
148
+ function getNativeAddonNames(): string[] {
150
149
  const platform = process.platform;
151
150
  const arch = process.arch;
152
-
153
151
  if (!["linux", "darwin", "win32"].includes(platform)) {
154
152
  throw new Error(`Unsupported platform: ${platform}`);
155
153
  }
@@ -157,7 +155,12 @@ function getNativeAddonName(): string {
157
155
  throw new Error(`Unsupported architecture: ${arch}`);
158
156
  }
159
157
 
160
- return `pi_natives.${platform}-${arch}.node`;
158
+ const baseName = `pi_natives.${platform}-${arch}.node`;
159
+ if (arch !== "x64") {
160
+ return [baseName];
161
+ }
162
+
163
+ return [`pi_natives.${platform}-${arch}-modern.node`, `pi_natives.${platform}-${arch}-baseline.node`];
161
164
  }
162
165
 
163
166
  /**
@@ -197,50 +200,58 @@ async function updateViaBun(expectedVersion: string): Promise<void> {
197
200
  */
198
201
  async function updateViaBinary(release: ReleaseInfo): Promise<void> {
199
202
  const binaryName = getBinaryName();
200
- const nativeAddonName = getNativeAddonName();
201
-
203
+ const nativeAddonNames = getNativeAddonNames();
202
204
  const asset = release.assets.find(a => a.name === binaryName);
203
- const nativeAsset = release.assets.find(a => a.name === nativeAddonName);
204
-
205
205
  if (!asset) {
206
206
  throw new Error(`No binary found for ${binaryName}`);
207
207
  }
208
- if (!nativeAsset) {
209
- throw new Error(`No native addon found for ${nativeAddonName}`);
210
- }
211
-
212
208
  const execPath = process.execPath;
213
209
  const execDir = path.dirname(execPath);
214
210
  const tempPath = `${execPath}.new`;
215
211
  const backupPath = `${execPath}.bak`;
216
- const nativePath = path.join(execDir, nativeAddonName);
217
- const nativeTempPath = `${nativePath}.new`;
212
+ const nativeDownloads: Array<{ name: string; tempPath: string; finalPath: string }> = [];
213
+
214
+ const downloadNativeAsset = async (name: string, required: boolean): Promise<boolean> => {
215
+ const nativeAsset = release.assets.find(assetEntry => assetEntry.name === name);
216
+ if (!nativeAsset) {
217
+ if (required) throw new Error(`No native addon found for ${name}`);
218
+ return false;
219
+ }
220
+
221
+ console.log(chalk.dim(`Downloading ${name}…`));
222
+ try {
223
+ const nativeResponse = await fetch(nativeAsset.url, { redirect: "follow" });
224
+ if (!nativeResponse.ok || !nativeResponse.body) {
225
+ if (required) throw new Error(`Native addon download failed for ${name}: ${nativeResponse.statusText}`);
226
+ return false;
227
+ }
228
+
229
+ const nativeFinalPath = path.join(execDir, name);
230
+ const nativeTempPath = `${nativeFinalPath}.new`;
231
+ const nativeFileStream = fs.createWriteStream(nativeTempPath, { mode: 0o755 });
232
+ await pipeline(nativeResponse.body, nativeFileStream);
233
+ nativeDownloads.push({ name, tempPath: nativeTempPath, finalPath: nativeFinalPath });
234
+ return true;
235
+ } catch (err) {
236
+ if (required) throw err;
237
+ return false;
238
+ }
239
+ };
218
240
 
219
241
  console.log(chalk.dim(`Downloading ${binaryName}…`));
220
242
 
221
- // Download to temp file
243
+ // Download binary to temp file
222
244
  const response = await fetch(asset.url, { redirect: "follow" });
223
245
  if (!response.ok || !response.body) {
224
246
  throw new Error(`Download failed: ${response.statusText}`);
225
247
  }
226
-
227
248
  const fileStream = fs.createWriteStream(tempPath, { mode: 0o755 });
228
249
  await pipeline(response.body, fileStream);
229
-
230
- // Download native addon
231
- console.log(chalk.dim(`Downloading ${nativeAddonName}…`));
232
-
233
- const nativeResponse = await fetch(nativeAsset.url, { redirect: "follow" });
234
- if (!nativeResponse.ok || !nativeResponse.body) {
235
- throw new Error(`Native addon download failed: ${nativeResponse.statusText}`);
250
+ for (const nativeAddonName of nativeAddonNames) {
251
+ await downloadNativeAsset(nativeAddonName, true);
236
252
  }
237
-
238
- const nativeFileStream = fs.createWriteStream(nativeTempPath, { mode: 0o755 });
239
- await pipeline(nativeResponse.body, nativeFileStream);
240
-
241
253
  // Replace current binary
242
254
  console.log(chalk.dim("Installing update..."));
243
-
244
255
  try {
245
256
  try {
246
257
  await fs.promises.unlink(backupPath);
@@ -251,10 +262,11 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
251
262
  await fs.promises.rename(tempPath, execPath);
252
263
  await fs.promises.unlink(backupPath);
253
264
 
254
- // Replace native addon (no backup needed, just overwrite)
255
- await fs.promises.rename(nativeTempPath, nativePath);
256
-
265
+ for (const nativeDownload of nativeDownloads) {
266
+ await fs.promises.rename(nativeDownload.tempPath, nativeDownload.finalPath);
267
+ }
257
268
  console.log(chalk.green(`\n${theme.status.success} Updated to ${release.version}`));
269
+ console.log(chalk.dim(`Installed ${nativeDownloads.length} native addon file(s)`));
258
270
  console.log(chalk.dim(`Restart ${APP_NAME} to use the new version`));
259
271
  } catch (err) {
260
272
  if (fs.existsSync(backupPath) && !fs.existsSync(execPath)) {
@@ -263,8 +275,10 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
263
275
  if (fs.existsSync(tempPath)) {
264
276
  await fs.promises.unlink(tempPath);
265
277
  }
266
- if (fs.existsSync(nativeTempPath)) {
267
- await fs.promises.unlink(nativeTempPath);
278
+ for (const nativeDownload of nativeDownloads) {
279
+ if (fs.existsSync(nativeDownload.tempPath)) {
280
+ await fs.promises.unlink(nativeDownload.tempPath);
281
+ }
268
282
  }
269
283
  throw err;
270
284
  }
@@ -24,6 +24,7 @@ const PROVIDERS: Array<SearchProviderId | "auto"> = [
24
24
  "anthropic",
25
25
  "perplexity",
26
26
  "exa",
27
+ "brave",
27
28
  "jina",
28
29
  "zai",
29
30
  "gemini",
@@ -131,14 +132,14 @@ ${chalk.bold("Arguments:")}
131
132
 
132
133
  ${chalk.bold("Options:")}
133
134
  --provider <name> Provider: ${PROVIDERS.join(", ")}
134
- --recency <value> Recency filter (Perplexity only): ${RECENCY_OPTIONS.join(", ")}
135
+ --recency <value> Recency filter (Brave/Perplexity): ${RECENCY_OPTIONS.join(", ")}
135
136
  -l, --limit <n> Max results to return
136
137
  --compact Render condensed output
137
138
  -h, --help Show this help
138
139
 
139
140
  ${chalk.bold("Examples:")}
140
141
  ${APP_NAME} q --provider=exa "what's the color of the sky"
141
- ${APP_NAME} q --provider=perplexity --recency=week "latest TypeScript 5.7 changes"
142
+ ${APP_NAME} q --provider=brave --recency=week "latest TypeScript 5.7 changes"
142
143
  `);
143
144
  }
144
145
 
@@ -10,6 +10,7 @@ const PROVIDERS: Array<SearchProviderId | "auto"> = [
10
10
  "anthropic",
11
11
  "perplexity",
12
12
  "exa",
13
+ "brave",
13
14
  "jina",
14
15
  "zai",
15
16
  "gemini",
@@ -93,6 +93,7 @@ const ModelDefinitionSchema = Type.Object({
93
93
  maxTokens: Type.Optional(Type.Number()),
94
94
  headers: Type.Optional(Type.Record(Type.String(), Type.String())),
95
95
  compat: Type.Optional(OpenAICompatSchema),
96
+ contextPromotionTarget: Type.Optional(Type.String({ minLength: 1 })),
96
97
  });
97
98
 
98
99
  // Schema for per-model overrides (all fields optional, merged with built-in model)
@@ -112,6 +113,7 @@ const ModelOverrideSchema = Type.Object({
112
113
  maxTokens: Type.Optional(Type.Number()),
113
114
  headers: Type.Optional(Type.Record(Type.String(), Type.String())),
114
115
  compat: Type.Optional(OpenAICompatSchema),
116
+ contextPromotionTarget: Type.Optional(Type.String({ minLength: 1 })),
115
117
  });
116
118
 
117
119
  type ModelOverride = Static<typeof ModelOverrideSchema>;
@@ -274,6 +276,7 @@ function applyModelOverride(model: Model<Api>, override: ModelOverride): Model<A
274
276
  if (override.input !== undefined) result.input = override.input as ("text" | "image")[];
275
277
  if (override.contextWindow !== undefined) result.contextWindow = override.contextWindow;
276
278
  if (override.maxTokens !== undefined) result.maxTokens = override.maxTokens;
279
+ if (override.contextPromotionTarget !== undefined) result.contextPromotionTarget = override.contextPromotionTarget;
277
280
  if (override.cost) {
278
281
  result.cost = {
279
282
  input: override.cost.input ?? model.cost.input,
@@ -652,6 +655,7 @@ export class ModelRegistry {
652
655
  maxTokens: modelDef.maxTokens ?? 16384,
653
656
  headers,
654
657
  compat: modelDef.compat,
658
+ contextPromotionTarget: modelDef.contextPromotionTarget,
655
659
  } as Model<Api>);
656
660
  }
657
661
  }
@@ -816,6 +820,7 @@ export class ModelRegistry {
816
820
  maxTokens: modelDef.maxTokens,
817
821
  headers,
818
822
  compat: modelDef.compat,
823
+ contextPromotionTarget: modelDef.contextPromotionTarget,
819
824
  } as Model<Api>);
820
825
  }
821
826
 
@@ -872,5 +877,6 @@ export interface ProviderConfigInput {
872
877
  maxTokens: number;
873
878
  headers?: Record<string, string>;
874
879
  compat?: Model<Api>["compat"];
880
+ contextPromotionTarget?: string;
875
881
  }>;
876
882
  }
@@ -299,6 +299,24 @@ export const SETTINGS_SCHEMA = {
299
299
  disabledProviders: { type: "array", default: [] as string[] },
300
300
  disabledExtensions: { type: "array", default: [] as string[] },
301
301
  modelRoles: { type: "record", default: {} as Record<string, string> },
302
+ "contextPromotion.enabled": {
303
+ type: "boolean",
304
+ default: true,
305
+ ui: {
306
+ tab: "agent",
307
+ label: "Auto-promote context",
308
+ description: "Promote to a larger-context model on context overflow instead of compacting",
309
+ },
310
+ },
311
+
312
+ // ─────────────────────────────────────────────────────────────────────────
313
+ // Secrets settings
314
+ // ─────────────────────────────────────────────────────────────────────────
315
+ "secrets.enabled": {
316
+ type: "boolean",
317
+ default: false,
318
+ ui: { tab: "config", label: "Hide secrets", description: "Obfuscate secrets before sending to AI providers" },
319
+ },
302
320
 
303
321
  // ─────────────────────────────────────────────────────────────────────────
304
322
  // Compaction settings
@@ -409,7 +427,7 @@ export const SETTINGS_SCHEMA = {
409
427
  },
410
428
  "grep.contextBefore": {
411
429
  type: "number",
412
- default: 1,
430
+ default: 0,
413
431
  ui: {
414
432
  tab: "tools",
415
433
  label: "Grep context before",
@@ -419,7 +437,7 @@ export const SETTINGS_SCHEMA = {
419
437
  },
420
438
  "grep.contextAfter": {
421
439
  type: "number",
422
- default: 3,
440
+ default: 0,
423
441
  ui: {
424
442
  tab: "tools",
425
443
  label: "Grep context after",
@@ -617,7 +635,7 @@ export const SETTINGS_SCHEMA = {
617
635
  // ─────────────────────────────────────────────────────────────────────────
618
636
  "providers.webSearch": {
619
637
  type: "enum",
620
- values: ["auto", "exa", "jina", "zai", "perplexity", "anthropic"] as const,
638
+ values: ["auto", "exa", "brave", "jina", "zai", "perplexity", "anthropic"] as const,
621
639
  default: "auto",
622
640
  ui: { tab: "services", label: "Web search provider", description: "Provider for web search tool", submenu: true },
623
641
  },
@@ -1027,6 +1045,9 @@ export interface CompactionSettings {
1027
1045
  remoteEndpoint: string | undefined;
1028
1046
  }
1029
1047
 
1048
+ export interface ContextPromotionSettings {
1049
+ enabled: boolean;
1050
+ }
1030
1051
  export interface RetrySettings {
1031
1052
  enabled: boolean;
1032
1053
  maxRetries: number;
@@ -1134,6 +1155,7 @@ export interface BashInterceptorRule {
1134
1155
  /** Map group prefix -> typed settings interface */
1135
1156
  export interface GroupTypeMap {
1136
1157
  compaction: CompactionSettings;
1158
+ contextPromotion: ContextPromotionSettings;
1137
1159
  retry: RetrySettings;
1138
1160
  memories: MemoriesSettings;
1139
1161
  branchSummary: BranchSummarySettings;
@@ -39,6 +39,7 @@ export type {
39
39
  BranchSummarySettings,
40
40
  CommitSettings,
41
41
  CompactionSettings,
42
+ ContextPromotionSettings,
42
43
  ExaSettings,
43
44
  GroupPrefix,
44
45
  GroupTypeMap,
@@ -18,11 +18,31 @@ export class RegisteredToolAdapter implements AgentTool<any, any, any> {
18
18
  declare parameters: any;
19
19
  declare label: string;
20
20
 
21
+ renderCall?: (args: any, theme: any) => any;
22
+ renderResult?: (result: any, options: any, theme: any, args?: any) => any;
23
+
21
24
  constructor(
22
25
  private registeredTool: RegisteredTool,
23
26
  private runner: ExtensionRunner,
24
27
  ) {
25
28
  applyToolProxy(registeredTool.definition, this);
29
+
30
+ // Only define render methods when the underlying definition provides them.
31
+ // If these exist unconditionally on the prototype, ToolExecutionComponent
32
+ // enters the custom-renderer path, gets undefined back, and silently
33
+ // discards tool result text (extensions without renderers show blank).
34
+ if (registeredTool.definition.renderCall) {
35
+ this.renderCall = (args: any, theme: any) => registeredTool.definition.renderCall!(args, theme as Theme);
36
+ }
37
+ if (registeredTool.definition.renderResult) {
38
+ this.renderResult = (result: any, options: any, theme: any, args?: any) =>
39
+ registeredTool.definition.renderResult!(
40
+ result,
41
+ { expanded: options.expanded, isPartial: options.isPartial, spinnerFrame: options.spinnerFrame },
42
+ theme as Theme,
43
+ args,
44
+ );
45
+ }
26
46
  }
27
47
 
28
48
  async execute(
@@ -34,19 +54,6 @@ export class RegisteredToolAdapter implements AgentTool<any, any, any> {
34
54
  ) {
35
55
  return this.registeredTool.definition.execute(toolCallId, params, signal, onUpdate, this.runner.createContext());
36
56
  }
37
-
38
- renderCall?(args: any, theme: any) {
39
- return this.registeredTool.definition.renderCall?.(args, theme as Theme);
40
- }
41
-
42
- renderResult?(result: any, options: any, theme: any, args?: any) {
43
- return this.registeredTool.definition.renderResult?.(
44
- result,
45
- { expanded: options.expanded, isPartial: options.isPartial, spinnerFrame: options.spinnerFrame },
46
- theme as Theme,
47
- args,
48
- );
49
- }
50
57
  }
51
58
 
52
59
  /**
@@ -1,4 +1,15 @@
1
1
  import type { AutocompleteItem } from "@oh-my-pi/pi-tui";
2
+ import { slashCommandCapability } from "../capability/slash-command";
3
+ import { renderPromptTemplate } from "../config/prompt-templates";
4
+ import type { SlashCommand } from "../discovery";
5
+ import { loadCapability } from "../discovery";
6
+ import {
7
+ BUILTIN_SLASH_COMMAND_DEFS,
8
+ type BuiltinSlashCommand,
9
+ type SubcommandDef,
10
+ } from "../slash-commands/builtin-registry";
11
+ import { EMBEDDED_COMMAND_TEMPLATES } from "../task/commands";
12
+ import { parseFrontmatter } from "../utils/frontmatter";
2
13
 
3
14
  export type SlashCommandSource = "extension" | "prompt" | "skill";
4
15
 
@@ -12,22 +23,7 @@ export interface SlashCommandInfo {
12
23
  path?: string;
13
24
  }
14
25
 
15
- /** Declarative subcommand definition for commands like /mcp */
16
- export interface SubcommandDef {
17
- name: string;
18
- description: string;
19
- /** Usage hint shown as dim ghost text, e.g. "<name> [--scope project|user]" */
20
- usage?: string;
21
- }
22
-
23
- export interface BuiltinSlashCommand {
24
- name: string;
25
- description: string;
26
- /** Subcommands for dropdown completion (e.g. /mcp add, /mcp list) */
27
- subcommands?: SubcommandDef[];
28
- /** Static inline hint when command takes a simple argument (no subcommands) */
29
- inlineHint?: string;
30
- }
26
+ export type { BuiltinSlashCommand, SubcommandDef } from "../slash-commands/builtin-registry";
31
27
 
32
28
  /**
33
29
  * Build getArgumentCompletions from declarative subcommand definitions.
@@ -93,74 +89,6 @@ function buildStaticInlineHint(hint: string): (argumentText: string) => string |
93
89
  return (argumentText: string) => (argumentText.trim().length === 0 ? hint : null);
94
90
  }
95
91
 
96
- const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = [
97
- { name: "settings", description: "Open settings menu" },
98
- { name: "plan", description: "Toggle plan mode (agent plans before executing)" },
99
- { name: "model", description: "Select model (opens selector UI)" },
100
- { name: "export", description: "Export session to HTML file", inlineHint: "[path]" },
101
- { name: "dump", description: "Copy session transcript to clipboard" },
102
- { name: "share", description: "Share session as a secret GitHub gist" },
103
- {
104
- name: "browser",
105
- description: "Toggle browser headless vs visible mode",
106
- subcommands: [
107
- { name: "headless", description: "Switch to headless mode" },
108
- { name: "visible", description: "Switch to visible mode" },
109
- ],
110
- },
111
- { name: "copy", description: "Copy last agent message to clipboard" },
112
- { name: "session", description: "Show session info and stats" },
113
- { name: "usage", description: "Show provider usage and limits" },
114
- { name: "changelog", description: "Show changelog entries" },
115
- { name: "hotkeys", description: "Show all keyboard shortcuts" },
116
- { name: "extensions", description: "Open Extension Control Center dashboard" },
117
- { name: "branch", description: "Create a new branch from a previous message" },
118
- { name: "fork", description: "Create a new fork from a previous message" },
119
- { name: "tree", description: "Navigate session tree (switch branches)" },
120
- { name: "login", description: "Login with OAuth provider" },
121
- { name: "logout", description: "Logout from OAuth provider" },
122
- {
123
- name: "mcp",
124
- description: "Manage MCP servers (add, list, remove, test)",
125
- subcommands: [
126
- {
127
- name: "add",
128
- description: "Add a new MCP server",
129
- usage: "<name> [--scope project|user] [--url <url>] [-- <command...>]",
130
- },
131
- { name: "list", description: "List all configured MCP servers" },
132
- { name: "remove", description: "Remove an MCP server", usage: "<name> [--scope project|user]" },
133
- { name: "test", description: "Test connection to a server", usage: "<name>" },
134
- { name: "reauth", description: "Reauthorize OAuth for a server", usage: "<name>" },
135
- { name: "unauth", description: "Remove OAuth auth from a server", usage: "<name>" },
136
- { name: "enable", description: "Enable an MCP server", usage: "<name>" },
137
- { name: "disable", description: "Disable an MCP server", usage: "<name>" },
138
- { name: "reload", description: "Force reload MCP runtime tools" },
139
- { name: "help", description: "Show help message" },
140
- ],
141
- },
142
- { name: "new", description: "Start a new session" },
143
- { name: "compact", description: "Manually compact the session context", inlineHint: "[focus instructions]" },
144
- { name: "handoff", description: "Hand off session context to a new session", inlineHint: "[focus instructions]" },
145
- { name: "resume", description: "Resume a different session" },
146
- { name: "background", description: "Detach UI and continue running in background" },
147
- { name: "debug", description: "Write debug log (TUI state and messages)" },
148
- {
149
- name: "memory",
150
- description: "Inspect and operate memory maintenance",
151
- subcommands: [
152
- { name: "view", description: "Show current memory injection payload" },
153
- { name: "clear", description: "Clear persisted memory data and artifacts" },
154
- { name: "reset", description: "Alias for clear" },
155
- { name: "enqueue", description: "Enqueue memory consolidation maintenance" },
156
- { name: "rebuild", description: "Alias for enqueue" },
157
- ],
158
- },
159
- { name: "move", description: "Move session to a different working directory", inlineHint: "<path>" },
160
- { name: "exit", description: "Exit the application" },
161
- { name: "quit", description: "Quit the application" },
162
- ];
163
-
164
92
  /**
165
93
  * Materialized builtin slash commands with completion functions derived from
166
94
  * declarative subcommand/hint definitions.
@@ -187,13 +115,6 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<
187
115
  return cmd;
188
116
  });
189
117
 
190
- import { slashCommandCapability } from "../capability/slash-command";
191
- import { renderPromptTemplate } from "../config/prompt-templates";
192
- import type { SlashCommand } from "../discovery";
193
- import { loadCapability } from "../discovery";
194
- import { EMBEDDED_COMMAND_TEMPLATES } from "../task/commands";
195
- import { parseFrontmatter } from "../utils/frontmatter";
196
-
197
118
  /**
198
119
  * Represents a custom slash command loaded from a file
199
120
  */