@oh-my-pi/pi-coding-agent 14.5.7 → 14.5.9

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 (40) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/package.json +7 -7
  3. package/src/config/model-registry.ts +23 -1
  4. package/src/config/settings-schema.ts +23 -0
  5. package/src/edit/modes/atom.lark +7 -5
  6. package/src/edit/modes/atom.ts +462 -56
  7. package/src/edit/modes/hashline.ts +21 -1
  8. package/src/lsp/index.ts +2 -4
  9. package/src/lsp/render.ts +0 -3
  10. package/src/lsp/types.ts +1 -4
  11. package/src/lsp/utils.ts +18 -14
  12. package/src/modes/components/settings-defs.ts +10 -0
  13. package/src/modes/controllers/command-controller.ts +17 -0
  14. package/src/modes/controllers/event-controller.ts +14 -9
  15. package/src/modes/controllers/input-controller.ts +13 -1
  16. package/src/modes/interactive-mode.ts +44 -23
  17. package/src/modes/types.ts +5 -2
  18. package/src/modes/utils/context-usage.ts +294 -0
  19. package/src/prompts/tools/atom.md +99 -44
  20. package/src/prompts/tools/exit-plan-mode.md +5 -39
  21. package/src/prompts/tools/lsp.md +2 -3
  22. package/src/prompts/tools/recipe.md +16 -0
  23. package/src/prompts/tools/task.md +34 -147
  24. package/src/prompts/tools/todo-write.md +22 -64
  25. package/src/session/compaction/compaction.ts +35 -22
  26. package/src/session/session-dump-format.ts +1 -0
  27. package/src/slash-commands/builtin-registry.ts +12 -5
  28. package/src/tools/bash.ts +149 -115
  29. package/src/tools/debug.ts +57 -70
  30. package/src/tools/index.ts +11 -0
  31. package/src/tools/recipe/index.ts +80 -0
  32. package/src/tools/recipe/render.ts +19 -0
  33. package/src/tools/recipe/runner.ts +219 -0
  34. package/src/tools/recipe/runners/cargo.ts +131 -0
  35. package/src/tools/recipe/runners/index.ts +8 -0
  36. package/src/tools/recipe/runners/just.ts +73 -0
  37. package/src/tools/recipe/runners/make.ts +101 -0
  38. package/src/tools/recipe/runners/pkg.ts +165 -0
  39. package/src/tools/recipe/runners/task.ts +72 -0
  40. package/src/tools/renderers.ts +2 -0
@@ -1,167 +1,54 @@
1
1
  Launches subagents to parallelize workflows.
2
2
 
3
3
  {{#if asyncEnabled}}
4
- - Use `read jobs://` to inspect state; `read jobs://<job_id>` for detail.
5
- - Use the `job` tool (with `poll`) to wait until completion. You **MUST NOT** poll `read jobs://` in a loop.
4
+ - `read jobs://` for state, `read jobs://<id>` for detail.
5
+ - Use `job` (with `poll`) to wait. **MUST NOT** poll `read jobs://` in a loop.
6
6
  {{/if}}
7
7
 
8
- {{#if defaultMode}}
9
- Current input mode: `default`. Shared `context` and custom task-call `schema` are available.
10
- {{/if}}
11
- {{#if schemaFreeMode}}
12
- Current input mode: `schema-free`. Shared `context` is available; custom task-call `schema` is disabled. For structured output, rely on the agent definition or inherited session schema.
13
- {{/if}}
14
- {{#if independentMode}}
15
- Current input mode: `independent`. Shared `context` and custom `schema` are both disabled. Every assignment must stand on its own.
16
- {{/if}}
17
-
18
- {{#if contextEnabled}}
19
- Subagents lack your conversation history. Every decision, file content, and user requirement they need **MUST** be explicit in `context` or `assignment`.
20
- {{else}}
21
- Subagents lack your conversation history. Every decision, file content, and user requirement they need **MUST** be explicit in each task `assignment`.
22
- {{/if}}
8
+ Subagents have no access to your conversation history. Every fact, file path, and decision they need **MUST** be explicit in {{#if contextEnabled}}`context` or `assignment`{{else}}each `assignment`{{/if}}.
23
9
 
24
10
  <parameters>
25
- - `agent`: Agent type for all tasks.
26
- - `.id`: CamelCase, max 32 chars
27
- - `.description`: UI display only — subagent never sees it
28
- - `.assignment`: Complete self-contained instructions. One-liners PROHIBITED; missing acceptance criteria = too vague.
29
- {{#if contextEnabled}}
30
- - `context`: Shared background prepended to every assignment. Session-specific info only.
31
- {{/if}}
32
- {{#if customSchemaEnabled}}
33
- - `schema`: JSON-encoded JTD schema for expected output. Format lives here — **MUST NOT** be duplicated in assignments.
34
- {{/if}}
35
- - `tasks`: Tasks to execute in parallel.
36
- {{#if isolationEnabled}}
37
- - `isolated`: Run in isolated environment; returns patches. Use when tasks edit overlapping files.
38
- {{/if}}
11
+ - `agent`: agent type for all tasks
12
+ - `tasks`: tasks to execute in parallel
13
+ - `.id`: CamelCase, ≤32 chars
14
+ - `.description`: UI label only subagent never sees it
15
+ - `.assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED
16
+ {{#if contextEnabled}}- `context`: shared background prepended to every assignment; session-specific only{{/if}}
17
+ {{#if customSchemaEnabled}}- `schema`: JTD schema for expected structured output (do not put format rules in assignments){{/if}}
18
+ {{#if isolationEnabled}}- `isolated`: run in isolated env; use when tasks edit overlapping files{{/if}}
39
19
  </parameters>
40
20
 
41
- <critical>
42
- {{#if contextEnabled}}
43
- - **MUST NOT** duplicate shared constraints across assignments put them in `context` once.
44
- {{else}}
45
- - Every `assignment` must repeat any constraints, reference paths, and acceptance criteria it needs — there is no shared `context` field.
46
- {{/if}}
47
- - **MUST NOT** tell tasks to run project-wide build/test/lint. Parallel agents share the working tree; each task edits, stops. Caller verifies after all complete.
48
- - For large payloads (traces, JSON blobs), write to `local://<path>` and pass the path in {{#if contextEnabled}}`context`{{else}}the relevant `assignment`{{/if}}.
49
- - Prefer `task` agents that investigate **and** edit in one pass. Launch a dedicated read-only discovery step only when affected files are genuinely unknown.
50
- </critical>
51
-
52
- <scope>
53
- Each task: **at most 3–5 files**. Globs, "update all", or package-wide scope = too broad. Enumerate files explicitly and fan out to a cluster of agents.
54
- </scope>
21
+ <rules>
22
+ - **MUST NOT** assign tasks to run project-wide build/test/lint. Caller verifies after the batch.
23
+ - Each task: ≤3–5 explicit files. No globs, no "update all", no package-wide scope. Fan out to a cluster instead.
24
+ - Pass large payloads via `local://<path>` URIs, not inline.
25
+ {{#if contextEnabled}}- Put shared constraints in `context` once; do not duplicate across assignments.{{/if}}
26
+ - Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.
27
+ </rules>
55
28
 
56
29
  <parallelization>
57
- **Test:** Can task B produce correct output without seeing A's result? Yes parallel. Nosequential.
58
-
59
- |Sequential first|Then|Reason|
60
- |---|---|---|
61
- |Types/interfaces|Consumers|Need contract|
62
- |API exports|Callers|Need signatures|
63
- |Core module|Dependents|Import dependency|
64
- |Schema/migration|App logic|Schema dependency|
65
-
66
- **Safe to parallelize:** independent modules, isolated file-scoped refactors, tests for existing code.
30
+ Test: can task B run correctly without seeing A's output? If no, sequence AB.
31
+ Sequential when one task produces a contract (types, API, schema, core module) the other consumes.
32
+ Parallel when tasks touch disjoint files or are independent refactors/tests.
67
33
  </parallelization>
68
34
 
69
- <templates>
70
- {{#if contextEnabled}}
71
- **context:**
72
- ```
73
- ## Goal ← one sentence: what the batch accomplishes
74
- ## Non-goals ← what tasks must not touch
75
- ## Constraints ← MUST/MUST NOT rules and session decisions
76
- ## API Contract ← exact types/signatures if tasks share an interface (omit if N/A)
77
- ## Acceptance ← definition of done; build/lint runs AFTER all tasks complete
78
- ```
79
- {{else}}
80
- No shared `context` field exists in this mode. Fold goal, non-goals, constraints, and acceptance criteria into each `assignment`.
81
- {{/if}}
82
- **assignment:**
83
- ```
84
- ## Target ← exact file paths; named symbols; explicit non-goals
85
- ## Change ← step-by-step what to add/remove/rename; patterns/APIs to use
86
- ## Edge Cases ← tricky inputs; existing behavior that must survive
87
- ## Acceptance ← observable result proving the task is done; no project-wide commands
88
- ```
89
- </templates>
90
-
91
- <checklist>
92
- Before invoking:
93
35
  {{#if contextEnabled}}
94
- - `context` contains only session-specific info
95
- {{else}}
96
- - Every `assignment` includes its own goal, constraints, and acceptance criteria (no shared context)
36
+ <context-fmt>
37
+ # Goal ← one sentence: what the batch accomplishes
38
+ # Constraints ← **MUST**/**MUST NOT** rules and session decisions
39
+ # Contract ← exact types/signatures if tasks share an interface
40
+ </context-fmt>
97
41
  {{/if}}
98
- - Every `assignment` follows the template; no one-liners; edge cases covered
99
- - Tasks are truly parallel — you can articulate why none depends on another's output
100
- - File paths are explicit; no globs
101
- {{#if customSchemaEnabled}}
102
- - `schema` is set if you expect structured output
103
- {{else}}
104
- - Do not pass a custom task-call `schema` in this mode
105
- {{/if}}
106
- </checklist>
107
-
108
- {{#if contextEnabled}}
109
- <example label="Rename exported symbol + update all call sites">
110
- Two tasks with non-overlapping file sets — demonstrates scope partitioning.
111
42
 
112
- <context>
113
- ## Goal
114
- Rename `parseConfig` `loadConfig` in `src/config/parser.ts` and all callers.
115
- ## Non-goals
116
- No behavior or signature changes; rename only.
117
- ## Acceptance (global)
118
- Caller runs `bun check:ts` after both tasks complete. Tasks must NOT run it.
119
- </context>
120
- <tasks>
121
- <task name="RenameExport">
122
- <assignment>
123
- ## Target
124
- - `src/config/parser.ts`: function `parseConfig`
125
- - If `src/config/index.ts` re-exports it, update the re-export
126
- - Non-goals: do not touch callers or tests
127
-
128
- ## Change
129
- - Rename `parseConfig` → `loadConfig` (declaration + any JSDoc references)
130
-
131
- ## Edge Cases
132
- - Rename all overload signatures if overloaded
133
- - Internal helpers like `_parseConfigValue` are different symbols — leave untouched
134
- - Do not add a backwards-compat alias
135
-
136
- ## Acceptance
137
- - `parseConfig` no longer appears as a top-level export in `parser.ts`
138
- </assignment>
139
- </task>
140
- <task name="UpdateCallers">
141
- <assignment>
142
- ## Target
143
- - `src/cli/init.ts`, `src/server/bootstrap.ts`, `src/worker/index.ts`
144
- - Non-goals: do not touch `src/config/parser.ts` or `src/config/index.ts`
145
-
146
- ## Change
147
- - Replace `import { parseConfig }` → `import { loadConfig }`
148
- - Replace every call site `parseConfig(` → `loadConfig(`
149
- - For `import * as cfg` users, update `cfg.parseConfig` property access
150
-
151
- ## Edge Cases
152
- - String literals containing "parseConfig" (logs, comments) are documentation — leave them
153
- - If a file re-exports to an external package boundary, keep the old name via `export { loadConfig as parseConfig }` with a `// TODO: remove after next major` comment
154
-
155
- ## Acceptance
156
- - No bare `parseConfig` identifier remains in the three target files
157
- </assignment>
158
- </task>
159
- </tasks>
160
- </example>
161
- {{/if}}
43
+ <assignment-fmt>
44
+ # Target ← exact files and symbols; explicit non-goals
45
+ # Change ← step-by-step add/remove/rename; APIs and patterns
46
+ # Acceptance ← observable result; no project-wide commands
47
+ </assignment-fmt>
162
48
 
49
+ <agents>
163
50
  {{#list agents join="\n"}}
164
- ### Agent: {{name}}
165
- **Tools:** {{default (join tools ", ") "All"}}
51
+ # {{name}}
166
52
  {{description}}
167
53
  {{/list}}
54
+ </agents>
@@ -1,69 +1,33 @@
1
- Manages a phased task list through an `ops` array of flat operations.
2
- The next pending task is auto-promoted to `in_progress` after completing the current one.
1
+ Manages a phased task list. Pass `ops`: a flat array of operations.
2
+ The next pending task is auto-promoted to `in_progress` after each completion.
3
3
 
4
- <protocol>
5
- ## Shape
4
+ ## Operations
6
5
 
7
- Pass an object with an `ops` array:
8
-
9
- ```ts
10
- {
11
- ops: [
12
- { op: "replace", phases: [...] },
13
- { op: "start", task: "task-3" },
14
- { op: "done", phase: "Implementation" },
15
- { op: "rm" },
16
- { op: "drop", task: "task-9" },
17
- { op: "append", phase: "Implementation", items: [{ id: "task-10", label: "Run tests" }] },
18
- ],
19
- }
20
- ```
21
-
22
- ## Operation fields
23
-
24
- |Field|Type|When to use|
6
+ |`op`|Required fields|Effect|
25
7
  |---|---|---|
26
- |`op`|string|Required. One of `replace`, `start`, `done`, `rm`, `drop`, `append`, `note`|
27
- |`task`|string|Task id for `start`, or a task target for `done` / `rm` / `drop`|
28
- |`phase`|string|Phase target for `done` / `rm` / `drop`, or append destination for `append`|
29
- |`items`|{id, label}[]|Required for `append`. If the phase does not exist, it is created at the end|
30
- |`phases`|Phase[]|Only for `replace`. Keeps initial phased setup available for harness bootstrap and full restructures|
31
- |`text`|string|Required for `note`. The note text appended to `task.notes` (which is a list, joined with newlines on render)|
32
-
33
- ## Semantics
34
- - `start`: requires `task`; sets that task to `in_progress`
35
- - `done`: marks one task, one phase, or all tasks completed
36
- - `rm`: removes one task, one phase's tasks, or all tasks
37
- - `drop`: marks one task, one phase, or all tasks abandoned
38
- - `append`: appends `items` to `phase`; creates the phase if missing
39
- - `replace`: replaces the full todo list
40
- - `note`: append `text` as a new note attached to `task`. Notes are append-only context the user added; they only render to you when the task is `in_progress`. Other tasks display only a `+N` marker. Use this when you want to leave a follow-up reminder for yourself when you reach a later task.
41
-
42
- If `done`, `rm`, or `drop` omits both `task` and `phase`, it applies to all tasks.
43
-
44
- ## Task Anatomy
45
- - `label`: Short label (5-10 words). What is being done, not how.
46
- - `replace` task `content` should stay short and specific.
47
-
48
- ## Phase Anatomy
49
- - `name`: Short, human-readable noun phrase (1-3 words). Capitalize naturally.
50
- - Always prefix with a roman-numeral ordinal (`I.`, `II.`, `III.`, `IV.`, …) to convey ordering — e.g. `I. Foundation`, `II. Auth`, `III. Routing`. Single-phase plans use `I.` too.
51
- - You **MUST NOT** use snake_case, `Phase1_*`, arabic numerals (`1.`), or letter prefixes (`A.`) — they render as ugly identifiers.
8
+ |`replace`|`phases`|Replace the full list (initial setup, full restructure)|
9
+ |`start`|`task`|Set task to `in_progress`|
10
+ |`done`|`task` or `phase` (or neither = all)|Mark completed|
11
+ |`drop`|`task` or `phase` (or neither = all)|Mark abandoned|
12
+ |`rm`|`task` or `phase` (or neither = all)|Remove|
13
+ |`append`|`phase`, `items: {id, label}[]`|Append tasks; creates phase if missing|
14
+ |`note`|`task`, `text`|Append a note to `task.notes`. Only use to leave reminders for future-you.|
15
+
16
+ ## Anatomy
17
+ - **Task `label`**: 5–10 words, what is being done, not how.
18
+ - **Phase `name`**: short noun phrase prefixed with a roman numeral — `I. Foundation`, `II. Auth`, `III. Verification`. Single-phase plans still use `I.`. Never use snake_case, arabic numerals, or letter prefixes.
52
19
 
53
20
  ## Rules
54
21
  - Mark tasks done immediately after finishing — never defer.
55
- - Complete phases in order — do not skip ahead while earlier ones are pending.
56
- - On blockers, append a new task to the active phase.
22
+ - Complete phases in order.
23
+ - On blockers, `append` a new task to the active phase.
57
24
  - Keep ids stable once introduced.
58
- </protocol>
59
25
 
60
- <conditions>
61
- Create a todo list when:
62
- 1. Task requires 3+ distinct steps
63
- 2. User explicitly requests one
64
- 3. User provides a set of tasks to complete
65
- 4. New instructions arrive mid-task — capture before proceeding
66
- </conditions>
26
+ ## When to create a list
27
+ - Task requires 3+ distinct steps
28
+ - User explicitly requests one
29
+ - User provides a set of tasks to complete
30
+ - New instructions arrive mid-task capture before proceeding
67
31
 
68
32
  <examples>
69
33
  # Initial setup (multi-phase)
@@ -81,9 +45,3 @@ Create a todo list when:
81
45
  # Append tasks to a phase
82
46
  `{"ops":[{"op":"append","phase":"II. Auth","items":[{"id":"task-8","label":"Handle retries"},{"id":"task-9","label":"Run tests"}]}]}`
83
47
  </examples>
84
-
85
- <avoid>
86
- - Single-step tasks — act directly
87
- - Conversational or informational requests
88
- - Tasks completable in under 3 trivial steps
89
- </avoid>
@@ -26,6 +26,7 @@ import {
26
26
  getOpenAIResponsesHistoryPayload,
27
27
  normalizeResponsesToolCallId,
28
28
  } from "@oh-my-pi/pi-ai/utils";
29
+ import { countTokens } from "@oh-my-pi/pi-natives";
29
30
  import { logger, prompt } from "@oh-my-pi/pi-utils";
30
31
  import compactionShortSummaryPrompt from "../../prompts/compaction/compaction-short-summary.md" with { type: "text" };
31
32
  import compactionSummaryPrompt from "../../prompts/compaction/compaction-summary.md" with { type: "text" };
@@ -218,7 +219,7 @@ export function shouldCompact(contextTokens: number, contextWindow: number, sett
218
219
  return contextTokens > thresholdTokens;
219
220
  }
220
221
 
221
- function resolveThresholdTokens(contextWindow: number, settings: CompactionSettings): number {
222
+ export function resolveThresholdTokens(contextWindow: number, settings: CompactionSettings): number {
222
223
  // Fixed token limit takes priority over percentage
223
224
  const thresholdTokens = settings.thresholdTokens;
224
225
  if (typeof thresholdTokens === "number" && Number.isFinite(thresholdTokens) && thresholdTokens > 0) {
@@ -240,67 +241,79 @@ function resolveThresholdTokens(contextWindow: number, settings: CompactionSetti
240
241
  // ============================================================================
241
242
 
242
243
  /**
243
- * Estimate token count for a message using chars/4 heuristic.
244
- * This is conservative (overestimates tokens).
244
+ * Image content has no tokenizer representation; charge a fixed estimate
245
+ * matching what providers typically bill for inline images.
246
+ */
247
+ const IMAGE_TOKEN_ESTIMATE = 1200;
248
+
249
+ /**
250
+ * Estimate token count for a message using cl100k_base via the native
251
+ * tokenizer. This is not Claude's first-party tokenizer (Anthropic doesn't
252
+ * publish one) but is within ~5–10% across English/code text.
245
253
  */
246
254
  export function estimateTokens(message: AgentMessage): number {
247
- let chars = 0;
255
+ const fragments: string[] = [];
256
+ let extra = 0;
248
257
 
249
258
  switch (message.role) {
250
259
  case "user": {
251
260
  const content = (message as { content: string | Array<{ type: string; text?: string }> }).content;
252
261
  if (typeof content === "string") {
253
- chars = content.length;
262
+ fragments.push(content);
254
263
  } else if (Array.isArray(content)) {
255
264
  for (const block of content) {
256
265
  if (block.type === "text" && block.text) {
257
- chars += block.text.length;
266
+ fragments.push(block.text);
258
267
  }
259
268
  }
260
269
  }
261
- return Math.ceil(chars / 4);
270
+ break;
262
271
  }
263
272
  case "assistant": {
264
273
  const assistant = message as AssistantMessage;
265
274
  for (const block of assistant.content) {
266
275
  if (block.type === "text") {
267
- chars += block.text.length;
276
+ fragments.push(block.text);
268
277
  } else if (block.type === "thinking") {
269
- chars += block.thinking.length;
278
+ fragments.push(block.thinking);
270
279
  } else if (block.type === "toolCall") {
271
- chars += block.name.length + JSON.stringify(block.arguments).length;
280
+ fragments.push(block.name);
281
+ fragments.push(JSON.stringify(block.arguments));
272
282
  }
273
283
  }
274
- return Math.ceil(chars / 4);
284
+ break;
275
285
  }
276
286
  case "hookMessage":
277
287
  case "toolResult": {
278
288
  if (typeof message.content === "string") {
279
- chars = message.content.length;
289
+ fragments.push(message.content);
280
290
  } else {
281
291
  for (const block of message.content) {
282
292
  if (block.type === "text" && block.text) {
283
- chars += block.text.length;
284
- }
285
- if (block.type === "image") {
286
- chars += 4800; // Estimate images as 4000 chars, or 1200 tokens
293
+ fragments.push(block.text);
294
+ } else if (block.type === "image") {
295
+ extra += IMAGE_TOKEN_ESTIMATE;
287
296
  }
288
297
  }
289
298
  }
290
- return Math.ceil(chars / 4);
299
+ break;
291
300
  }
292
301
  case "bashExecution": {
293
- chars = message.command.length + message.output.length;
294
- return Math.ceil(chars / 4);
302
+ fragments.push(message.command);
303
+ fragments.push(message.output);
304
+ break;
295
305
  }
296
306
  case "branchSummary":
297
307
  case "compactionSummary": {
298
- chars = message.summary.length;
299
- return Math.ceil(chars / 4);
308
+ fragments.push(message.summary);
309
+ break;
300
310
  }
311
+ default:
312
+ return 0;
301
313
  }
302
314
 
303
- return 0;
315
+ if (fragments.length === 0) return extra;
316
+ return extra + countTokens(fragments);
304
317
  }
305
318
 
306
319
  function estimateEntriesTokens(entries: SessionEntry[], startIndex: number, endIndex: number): number {
@@ -114,6 +114,7 @@ export function formatSessionDumpText(options: FormatSessionDumpTextOptions): st
114
114
  if (c.type === "text") {
115
115
  lines.push(c.text);
116
116
  } else if (c.type === "thinking") {
117
+ if (c.thinking.trim().length === 0) continue;
117
118
  lines.push("<thinking>");
118
119
  lines.push(c.thinking);
119
120
  lines.push("</thinking>\n");
@@ -123,11 +123,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
123
123
  },
124
124
  {
125
125
  name: "loop",
126
- description: "Loop the agent: re-submit the same prompt every time it yields (Esc to stop)",
127
- inlineHint: "<prompt>",
128
- allowArgs: true,
129
- handle: async (command, runtime) => {
130
- await runtime.ctx.handleLoopCommand(command.args || undefined);
126
+ description:
127
+ "Toggle loop mode. While enabled, the next prompt you send re-submits after every yield. Esc cancels the current iteration; /loop again to disable.",
128
+ handle: async (_command, runtime) => {
129
+ await runtime.ctx.handleLoopCommand();
131
130
  runtime.ctx.editor.setText("");
132
131
  },
133
132
  },
@@ -356,6 +355,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
356
355
  runtime.ctx.editor.setText("");
357
356
  },
358
357
  },
358
+ {
359
+ name: "context",
360
+ description: "Show estimated context usage breakdown",
361
+ handle: (_command, runtime) => {
362
+ runtime.ctx.handleContextCommand();
363
+ runtime.ctx.editor.setText("");
364
+ },
365
+ },
359
366
  {
360
367
  name: "extensions",
361
368
  aliases: ["status"],