@oh-my-pi/pi-coding-agent 14.5.13 → 14.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/package.json +7 -7
  3. package/src/autoresearch/command-resume.md +5 -8
  4. package/src/autoresearch/git.ts +41 -51
  5. package/src/autoresearch/helpers.ts +43 -359
  6. package/src/autoresearch/index.ts +281 -273
  7. package/src/autoresearch/prompt-setup.md +43 -0
  8. package/src/autoresearch/prompt.md +52 -193
  9. package/src/autoresearch/resume-message.md +2 -8
  10. package/src/autoresearch/state.ts +59 -166
  11. package/src/autoresearch/storage.ts +687 -0
  12. package/src/autoresearch/tools/init-experiment.ts +201 -290
  13. package/src/autoresearch/tools/log-experiment.ts +304 -517
  14. package/src/autoresearch/tools/run-experiment.ts +117 -296
  15. package/src/autoresearch/tools/update-notes.ts +116 -0
  16. package/src/autoresearch/types.ts +16 -66
  17. package/src/commit/pipeline.ts +4 -3
  18. package/src/config/settings-schema.ts +1 -1
  19. package/src/config/settings.ts +20 -1
  20. package/src/config.ts +9 -6
  21. package/src/cursor.ts +1 -1
  22. package/src/edit/index.ts +9 -31
  23. package/src/edit/line-hash.ts +70 -43
  24. package/src/edit/modes/hashline.lark +26 -0
  25. package/src/edit/modes/hashline.ts +898 -1099
  26. package/src/edit/modes/patch.ts +0 -7
  27. package/src/edit/modes/replace.ts +0 -4
  28. package/src/edit/renderer.ts +22 -20
  29. package/src/edit/streaming.ts +8 -28
  30. package/src/eval/eval.lark +24 -30
  31. package/src/eval/js/context-manager.ts +5 -162
  32. package/src/eval/js/prelude.txt +0 -12
  33. package/src/eval/parse.ts +129 -129
  34. package/src/eval/py/kernel.ts +4 -4
  35. package/src/eval/py/prelude.py +1 -219
  36. package/src/export/html/template.generated.ts +1 -1
  37. package/src/export/html/template.js +2 -2
  38. package/src/internal-urls/docs-index.generated.ts +1 -1
  39. package/src/main.ts +10 -0
  40. package/src/mcp/manager.ts +22 -0
  41. package/src/modes/components/session-observer-overlay.ts +5 -2
  42. package/src/modes/components/status-line/segments.ts +1 -1
  43. package/src/modes/components/status-line.ts +3 -5
  44. package/src/modes/components/tree-selector.ts +4 -5
  45. package/src/modes/components/welcome.ts +11 -1
  46. package/src/modes/controllers/command-controller.ts +2 -6
  47. package/src/modes/controllers/event-controller.ts +1 -2
  48. package/src/modes/controllers/extension-ui-controller.ts +3 -15
  49. package/src/modes/controllers/input-controller.ts +0 -1
  50. package/src/modes/controllers/selector-controller.ts +1 -1
  51. package/src/modes/interactive-mode.ts +5 -7
  52. package/src/modes/rpc/rpc-client.ts +9 -0
  53. package/src/modes/rpc/rpc-mode.ts +6 -0
  54. package/src/modes/rpc/rpc-types.ts +9 -0
  55. package/src/prompts/system/system-prompt.md +14 -38
  56. package/src/prompts/tools/ast-edit.md +8 -8
  57. package/src/prompts/tools/ast-grep.md +10 -10
  58. package/src/prompts/tools/eval.md +13 -31
  59. package/src/prompts/tools/find.md +2 -1
  60. package/src/prompts/tools/hashline.md +66 -57
  61. package/src/prompts/tools/search.md +2 -2
  62. package/src/sdk.ts +19 -4
  63. package/src/session/agent-session.ts +110 -4
  64. package/src/session/session-manager.ts +17 -13
  65. package/src/task/agents.ts +4 -5
  66. package/src/tools/archive-reader.ts +9 -3
  67. package/src/tools/ast-edit.ts +141 -44
  68. package/src/tools/ast-grep.ts +112 -36
  69. package/src/tools/browser/readable.ts +11 -6
  70. package/src/tools/browser/tab-supervisor.ts +2 -2
  71. package/src/tools/browser.ts +5 -3
  72. package/src/tools/eval.ts +2 -53
  73. package/src/tools/find.ts +16 -15
  74. package/src/tools/image-gen.ts +2 -2
  75. package/src/tools/path-utils.ts +36 -196
  76. package/src/tools/search.ts +56 -35
  77. package/src/tools/write.ts +8 -1
  78. package/src/utils/edit-mode.ts +2 -11
  79. package/src/utils/file-display-mode.ts +1 -1
  80. package/src/utils/git.ts +17 -0
  81. package/src/utils/session-color.ts +0 -12
  82. package/src/utils/title-generator.ts +22 -38
  83. package/src/web/scrapers/crossref.ts +3 -3
  84. package/src/web/scrapers/devto.ts +1 -1
  85. package/src/web/scrapers/discourse.ts +5 -5
  86. package/src/web/scrapers/firefox-addons.ts +1 -1
  87. package/src/web/scrapers/flathub.ts +2 -2
  88. package/src/web/scrapers/gitlab.ts +1 -1
  89. package/src/web/scrapers/go-pkg.ts +2 -2
  90. package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
  91. package/src/web/scrapers/mastodon.ts +9 -9
  92. package/src/web/scrapers/mdn.ts +11 -7
  93. package/src/web/scrapers/pub-dev.ts +1 -1
  94. package/src/web/scrapers/rawg.ts +3 -3
  95. package/src/web/scrapers/readthedocs.ts +1 -1
  96. package/src/web/scrapers/spdx.ts +1 -1
  97. package/src/web/scrapers/stackoverflow.ts +2 -2
  98. package/src/web/scrapers/types.ts +53 -39
  99. package/src/web/scrapers/w3c.ts +1 -1
  100. package/src/web/search/providers/gemini.ts +2 -2
  101. package/src/autoresearch/apply-contract-to-state.ts +0 -24
  102. package/src/autoresearch/contract.ts +0 -288
  103. package/src/edit/modes/atom.lark +0 -29
  104. package/src/edit/modes/atom.ts +0 -1773
  105. package/src/prompts/tools/atom.md +0 -150
@@ -51,7 +51,7 @@ import { normalizeLocalScheme } from "../tools/path-utils";
51
51
  import { formatPhaseDisplayName } from "../tools/todo-write";
52
52
  import type { EventBus } from "../utils/event-bus";
53
53
  import { getEditorCommand, openInEditor } from "../utils/external-editor";
54
- import { getSessionAccentAnsi, getSessionAccentHexForTitle } from "../utils/session-color";
54
+ import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
55
55
  import { popTerminalTitle, pushTerminalTitle, setSessionTerminalTitle } from "../utils/title-generator";
56
56
  import type { AssistantMessageComponent } from "./components/assistant-message";
57
57
  import type { BashExecutionComponent } from "./components/bash-execution";
@@ -275,6 +275,7 @@ export class InteractiveMode implements InteractiveModeContext {
275
275
  this.#syncEditorMaxHeight();
276
276
  this.#resizeHandler = () => {
277
277
  this.#syncEditorMaxHeight();
278
+ this.updateEditorTopBorder();
278
279
  };
279
280
  process.stdout.on("resize", this.#resizeHandler);
280
281
  try {
@@ -431,11 +432,7 @@ export class InteractiveMode implements InteractiveModeContext {
431
432
  // Start the UI
432
433
  this.ui.start();
433
434
  pushTerminalTitle();
434
- setSessionTerminalTitle(
435
- this.sessionManager.getSessionName(),
436
- this.sessionManager.getCwd(),
437
- this.sessionManager.titleSource,
438
- );
435
+ setSessionTerminalTitle(this.sessionManager.getSessionName(), this.sessionManager.getCwd());
439
436
  this.updateEditorBorderColor();
440
437
  this.#syncEditorMaxHeight();
441
438
  this.isInitialized = true;
@@ -647,7 +644,8 @@ export class InteractiveMode implements InteractiveModeContext {
647
644
  } else if (this.isPythonMode) {
648
645
  this.editor.borderColor = theme.getPythonModeBorderColor();
649
646
  } else {
650
- const hex = getSessionAccentHexForTitle(this.sessionManager.getSessionName(), this.sessionManager.titleSource);
647
+ const sessionName = this.sessionManager.getSessionName();
648
+ const hex = sessionName ? getSessionAccentHex(sessionName) : undefined;
651
649
  const ansi = getSessionAccentAnsi(hex);
652
650
  if (ansi) {
653
651
  this.editor.borderColor = (str: string) => `${ansi}${str}\x1b[39m`;
@@ -11,6 +11,7 @@ import type { SessionStats } from "../../session/agent-session";
11
11
  import type { CompactionResult } from "../../session/compaction";
12
12
  import type {
13
13
  RpcCommand,
14
+ RpcHandoffResult,
14
15
  RpcHostToolCallRequest,
15
16
  RpcHostToolCancelRequest,
16
17
  RpcHostToolDefinition,
@@ -457,6 +458,14 @@ export class RpcClient {
457
458
  return this.#getData(response);
458
459
  }
459
460
 
461
+ /**
462
+ * Hand off session context to a new session.
463
+ */
464
+ async handoff(customInstructions?: string): Promise<RpcHandoffResult | null> {
465
+ const response = await this.#send({ type: "handoff", customInstructions });
466
+ return this.#getData(response);
467
+ }
468
+
460
469
  /**
461
470
  * Export session to HTML.
462
471
  */
@@ -574,6 +574,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
574
574
  description: tool.description,
575
575
  parameters: tool.parameters,
576
576
  })),
577
+ contextUsage: session.getContextUsage(),
577
578
  };
578
579
  return success(id, "get_state", state);
579
580
  }
@@ -741,6 +742,11 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
741
742
  return success(id, "set_session_name");
742
743
  }
743
744
 
745
+ case "handoff": {
746
+ const result = await session.handoff(command.customInstructions);
747
+ return success(id, "handoff", result ? { savedPath: result.savedPath } : null);
748
+ }
749
+
744
750
  // =================================================================
745
751
  // Messages
746
752
  // =================================================================
@@ -7,6 +7,7 @@
7
7
  import type { AgentMessage, AgentToolResult, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
8
8
  import type { Effort, ImageContent, Model } from "@oh-my-pi/pi-ai";
9
9
  import type { BashResult } from "../../exec/bash-executor";
10
+ import type { ContextUsage } from "../../extensibility/extensions/types";
10
11
  import type { SessionStats } from "../../session/agent-session";
11
12
  import type { CompactionResult } from "../../session/compaction";
12
13
  import type { TodoPhase } from "../../tools/todo-write";
@@ -63,6 +64,7 @@ export type RpcCommand =
63
64
  | { id?: string; type: "get_branch_messages" }
64
65
  | { id?: string; type: "get_last_assistant_text" }
65
66
  | { id?: string; type: "set_session_name"; name: string }
67
+ | { id?: string; type: "handoff"; customInstructions?: string }
66
68
 
67
69
  // Messages
68
70
  | { id?: string; type: "get_messages" };
@@ -89,6 +91,12 @@ export interface RpcSessionState {
89
91
  /** For session dump / export (plain-text parity with /dump). */
90
92
  systemPrompt?: string;
91
93
  dumpTools?: Array<{ name: string; description: string; parameters: unknown }>;
94
+ /** Current context window usage. Null tokens/percent when unknown (e.g. right after compaction). */
95
+ contextUsage?: ContextUsage;
96
+ }
97
+
98
+ export interface RpcHandoffResult {
99
+ savedPath?: string;
92
100
  }
93
101
 
94
102
  // ============================================================================
@@ -180,6 +188,7 @@ export type RpcResponse =
180
188
  data: { text: string | null };
181
189
  }
182
190
  | { id?: string; type: "response"; command: "set_session_name"; success: true }
191
+ | { id?: string; type: "response"; command: "handoff"; success: true; data: RpcHandoffResult | null }
183
192
 
184
193
  // Messages
185
194
  | { id?: string; type: "response"; command: "get_messages"; success: true; data: { messages: AgentMessage[] } }
@@ -77,23 +77,22 @@ If any check fails, continue or mark [blocked]. Do **NOT** reframe partial work
77
77
  - Correctness first, brevity second, politeness third.
78
78
  - Prefer concise, information-dense writing.
79
79
  - Avoid repeating the user's request or narrating routine tool calls.
80
+ - Prefer tool output over prose explanation — tool results communicate directly; narration adds noise, not signal.
80
81
  - Do not give time estimates or predictions.
81
82
  - Do not emit closing summaries, recap paragraphs, or "what I did" wrap-ups. Final messages state the result and any blockers; the trace already shows the work.
82
83
  </communication>
83
84
 
84
85
  <output-contract>
85
- - Brief preambles are allowed when they improve orientation, but they **MUST** stay short and **MUST NOT** be treated as completion.
86
86
  - A phase boundary, todo flip, or completed sub-step is **NOT** a yield point. Continue directly to the next step in the same turn — do **NOT** stop to summarize, ask for acknowledgement, or wait for the user to say "go".
87
87
  - Yield only when (a) the whole deliverable is complete, (b) you are [blocked], or (c) the user asked a question that requires their input.
88
88
  - Claims about code, tools, tests, docs, or external sources **MUST** be grounded in what was actually observed.
89
- - If a statement is an inference, label it as such.
89
+ - Persist on hard problems; do **NOT** punt half-solved work back
90
90
  - Be brief in prose, not in evidence, verification, or blocking details.
91
91
  </output-contract>
92
92
 
93
93
  <default-follow-through>
94
94
  - If the user's intent is clear and the next step is low-risk, proceed without asking.
95
95
  - Ask only when the next step is irreversible, has external side effects, or requires a missing choice that materially changes the outcome.
96
- - If you proceed, state what you did, what you verified, and what remains optional.
97
96
  </default-follow-through>
98
97
 
99
98
  <behavior>
@@ -138,20 +137,6 @@ Edge cases you ignored: pages at 3am.
138
137
  - Tests you did not write are bugs shipped; edge cases you ignored are pages at 3am. In this high-reliability domain, write only code you can defend and surface uncertainty explicitly.
139
138
  </principles>
140
139
 
141
- <design-checklist>
142
- Before writing or refactoring, verify:
143
- - Caller expectations are explicit
144
- - Failure modes surface the truth rather than plausible lies
145
- - Interfaces preserve distinctions the domain already knows
146
- - Existing repository patterns were considered before introducing new ones
147
- - The simpler design has been considered
148
- - Compiling is not correctness: verify behavior under the conditions that actually occur, including the failure modes
149
- - Adversarial caller: what does a malicious caller do? what would a tired maintainer misunderstand?
150
- - Cost named: before choosing the easy path, name what it costs (duplicated pattern across N files, unbounded resource use, escape hatch through the type system)
151
- - Inhabit the call site: read your own change as someone who has never seen the implementation — does the interface reflect what happened? is any input silently discarded?
152
- - Persist on hard problems; do **NOT** punt half-solved work back
153
- </design-checklist>
154
-
155
140
  {{SECTION_SEPARATOR "Environment"}}
156
141
 
157
142
  You operate inside the Oh My Pi coding harness. Given a task, you **MUST** complete it using the tools available to you.
@@ -288,6 +273,7 @@ Don't open a file hoping. Hope is not a strategy.
288
273
  {{#has tools "find"}}- Use `{{toolRefs.find}}` to map structure.{{/has}}
289
274
  {{#has tools "read"}}- Use `{{toolRefs.read}}` with offset or limit rather than whole-file reads when practical.{{/has}}
290
275
  {{#has tools "task"}}- Use `{{toolRefs.task}}` for investigate+edit when available.{{/has}}
276
+ - Load into context only what is necessary. Do not read files you do not need; do not fetch sections beyond what the task requires.
291
277
  <tool-persistence>
292
278
  - Use tools whenever they materially improve correctness, completeness, or grounding.
293
279
  - Do not stop at the first plausible answer if another tool call would materially reduce uncertainty.
@@ -322,15 +308,6 @@ These are inviolable.
322
308
  - If something is blocked, label it [blocked], say exactly what is missing, and distinguish it from work that is complete.
323
309
  </completeness-contract>
324
310
 
325
- # Design Integrity
326
-
327
- Design integrity means the code tells the truth about what the system currently is — not what it used to be, not what was convenient to patch. Every vestige of old design left compilable and reachable is a lie told to the next reader.
328
- - **The unit of change is the design decision, not the feature.** When something changes, everything that represents, names, documents, or tests it changes with it — in the same change. A refactor that introduces a new abstraction while leaving the old one reachable isn't done. A feature that requires a compatibility wrapper to land isn't done. The work is complete when the design is coherent, not when the tests pass.
329
- - **One concept, one representation.** Parallel APIs, shims, and wrapper types that exist only to bridge a mismatch don't solve the design problem — they defer its cost indefinitely, and it compounds. Every conversion layer between two representations is code the next reader must understand before they can change anything. Pick one representation, migrate everything to it, delete the other.
330
- - **Abstractions must cover their domain completely.** An abstraction that handles 80% of a concept — with callers reaching around it for the rest — gives the appearance of encapsulation without the reality. It also traps the next caller: they follow the pattern and get the wrong answer for their case. If callers routinely work around an abstraction, its boundary is wrong. Fix the boundary.
331
- - **Types must preserve what the domain knows.** Collapsing structured information into a coarser representation — a boolean, a string where an enum belongs, a nullable where a tagged union belongs — discards distinctions the type system could have enforced. Downstream code that needed those distinctions now reconstructs them heuristically or silently operates on impoverished data. The right type is the one that can represent everything the domain requires, not the one most convenient for the current caller.
332
- - **Optimize for the next edit, not the current diff.** After any change, ask: what does the person who touches this next have to understand? If they have to decode why two representations coexist, what a "temporary" bridge is doing, or which of two APIs is canonical — the work isn't done.
333
-
334
311
  # Procedure
335
312
  ## 1. Scope
336
313
  {{#if skills.length}}- You **MUST** read skills that match the task domain before starting.{{/if}}
@@ -353,6 +330,7 @@ Design integrity means the code tells the truth about what the system currently
353
330
  > a. Semantic edits to files that don't import each other or share types being changed
354
331
  > b. Investigating multiple subsystems
355
332
  > c. Work that decomposes into independent pieces wired together at the end
333
+ - Multiple edits to different sections of the same file are independent — stable hash anchors make them safe to batch. Issue them in one response rather than sequentially.
356
334
  - When a plan feels too large for a single turn, parallelize aggressively — do **NOT** abandon phases, silently drop them, or narrate scope cuts. Scope pressure is a signal to delegate, not to shrink the work.
357
335
  {{/has}}
358
336
  - Justify sequential work; default parallel. If you cannot articulate why B depends on A, it doesn't.
@@ -362,18 +340,16 @@ Design integrity means the code tells the truth about what the system currently
362
340
  - Marking a todo done is a transition, not a stop: in the same turn, start the next pending todo. Acceptable inter-phase text is one short line ("phase 1 done, starting phase 2") — not a recap, not a question.
363
341
 
364
342
  ## 5. While working
365
- You are not making code that works. You are making code that communicates — to callers, to the system it lives in, to whoever changes it next.
366
- - **One job, one level of abstraction.** If you need "and" to describe what something does, it should be two things. Code that mixes levels — orchestrating a flow while also handling parsing, formatting, or low-level manipulation — has no coherent owner and no coherent test. Each piece operates at one level and delegates everything else.
367
- - **Fix where the invariant is violated, not where the violation is observed.** If a function returns the wrong thing, fix the function — not the caller's workaround. If a type is wrong, fix the type — not the cast. The right fix location is always where the contract is broken.
368
- - **New code makes old code obsolete. Remove it.** When you introduce an abstraction, find what it replaces: old helpers, compatibility branches, stale tests, documentation describing removed behavior. Remove them in the same change.
369
- - **No forwarding addresses.** Deleted or moved code leaves no trace — no `// moved to X` comments, no re-exports from the old location, no aliases kept "for now," no renaming unused parameters to `_var`, no `// removed` tombstones. If something is unused, delete it completely.
370
- - **Prefer editing over creating.** Do not create new files unless they are necessary to achieve the goal. Editing an existing file prevents file bloat and builds on existing work. A new file must earn its existence.
371
- - **After writing, inhabit the call site.** Read your own code as someone who has never seen the implementation. Does the interface honestly reflect what happened? Is any accepted input silently discarded? Does any pattern exist in more than one place? Fix it.
372
- - When a tool call fails, read the full error before doing anything else. If a file changed since you last read it, re-read before editing.
373
- {{#has tools "ask"}}- Ask before destructive commands like `git checkout/restore/reset`, overwriting changes, or deleting code you did not write.{{else}}- Do **NOT** run destructive git commands like `git checkout/restore/reset`, overwrite changes, or delete code you did not write.{{/has}}
374
- {{#has tools "web_search"}}- If stuck or uncertain, gather more information. Do **NOT** pivot approaches without cause.{{/has}}
375
- - If others may be editing concurrently, re-read changed files and adapt.
376
- - If blocked, exhaust tools and context first.
343
+ Focus on clarity and correctness. Make code easy to understand now and in the future.
344
+ - Fix problems at their source, not at their symptoms.
345
+ - Remove obsolete or unused code no leftover comments, aliases, or re-exports.
346
+ - Prefer updating existing files over creating new ones, unless a new file is necessary.
347
+ - After editing, review from a user's perspective. Make sure your changes are clear and the interface matches behavior.
348
+ - If a tool fails or a file changes, re-read before acting.
349
+ {{#has tools "ask"}}- Ask before running destructive commands or deleting code you did not write.{{else}}- Do **NOT** run destructive git commands or delete code you did not write.{{/has}}
350
+ {{#has tools "web_search"}}- If unsure, search for more information instead of guessing.{{/has}}
351
+ - Adapt to concurrent edits by re-reading changed files.
352
+ - Use all available tools and context before declaring a blocker.
377
353
 
378
354
  ## 6. Verification
379
355
  - Test rigorously. Prefer unit or end-to-end tests, you **MUST NOT** rely on mocks.
@@ -2,8 +2,8 @@ Performs structural AST-aware rewrites via native ast-grep.
2
2
 
3
3
  <instruction>
4
4
  - Use for codemods and structural rewrites where plain text replace is unsafe
5
- - `path` is required and accepts a file, directory, glob, comma-separated path list, or internal URL
6
- - Language is inferred from `path`; narrow `path` to one language for deterministic rewrites
5
+ - `paths` is required and accepts an array of files, directories, globs, or internal URLs
6
+ - Language is inferred from `paths`; narrow each call to one language for deterministic rewrites
7
7
  - Metavariables captured in `pat` (`$A`, `$$$ARGS`) are substituted into that entry's `out` template
8
8
  - **Patterns match AST structure, not text.** `$NAME` = one node (captured); `$_` = one without binding; `$$$NAME` = zero-or-more (lazy — stops at next matchable element); `$$$` = zero-or-more without binding. Use `$$$NAME`, **NOT** `$$NAME` — the two-dollar form is invalid. Metavariable names are UPPERCASE and **MUST** be the whole AST node — partial text like `prefix$VAR` or `"hello $NAME"` does NOT work
9
9
  - When the same metavariable appears twice, both occurrences **MUST** match identical code (`$A == $A` matches `x == x`, not `x == y`)
@@ -20,17 +20,17 @@ Performs structural AST-aware rewrites via native ast-grep.
20
20
 
21
21
  <examples>
22
22
  # Rename a call site across TypeScript files
23
- `{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"path":"src/**/*.ts"}`
23
+ `{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"paths":["src/**/*.ts"]}`
24
24
  # Delete matching calls
25
- `{"ops":[{"pat":"console.log($$$ARGS)","out":""}],"path":"src/**/*.ts"}`
25
+ `{"ops":[{"pat":"console.log($$$ARGS)","out":""}],"paths":["src/**/*.ts"]}`
26
26
  # Rewrite import source path
27
- `{"ops":[{"pat":"import { $$$IMPORTS } from \"old-package\"","out":"import { $$$IMPORTS } from \"new-package\""}],"path":"src/**/*.ts"}`
27
+ `{"ops":[{"pat":"import { $$$IMPORTS } from \"old-package\"","out":"import { $$$IMPORTS } from \"new-package\""}],"paths":["src/**/*.ts"]}`
28
28
  # Modernize to optional chaining (same metavariable enforces identity)
29
- `{"ops":[{"pat":"$A && $A()","out":"$A?.()"}],"path":"src/**/*.ts"}`
29
+ `{"ops":[{"pat":"$A && $A()","out":"$A?.()"}],"paths":["src/**/*.ts"]}`
30
30
  # Swap two arguments using captures
31
- `{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"path":"tests/**/*.ts"}`
31
+ `{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"paths":["tests/**/*.ts"]}`
32
32
  # Python — convert print calls to logging
33
- `{"ops":[{"pat":"print($$$ARGS)","out":"logger.info($$$ARGS)"}],"path":"src/**/*.py"}`
33
+ `{"ops":[{"pat":"print($$$ARGS)","out":"logger.info($$$ARGS)"}],"paths":["src/**/*.py"]}`
34
34
  </examples>
35
35
 
36
36
  <critical>
@@ -2,8 +2,8 @@ Performs structural code search using AST matching via native ast-grep.
2
2
 
3
3
  <instruction>
4
4
  - Use when syntax shape matters more than raw text (calls, declarations, specific language constructs)
5
- - `path` is required and accepts a file, directory, glob, comma-separated path list, or internal URL
6
- - Language is inferred from `path`; narrow `path` to one language when mixed-language trees could cause parse noise
5
+ - `paths` is required and accepts an array of files, directories, globs, or internal URLs
6
+ - Language is inferred from `paths`; narrow each call to one language when mixed-language trees could cause parse noise
7
7
  - `pat` is a single AST pattern. Run separate calls for distinct unrelated patterns
8
8
  - **Patterns match AST structure, not text** — whitespace/formatting is ignored
9
9
  - `$NAME` captures one node; `$_` matches one without binding; `$$$NAME` captures zero-or-more (lazy — stops at next matchable element); `$$$` matches zero-or-more without binding. Use `$$$NAME`, **NOT** `$$NAME` — the two-dollar form is invalid and produces a parse error
@@ -13,7 +13,7 @@ Performs structural code search using AST matching via native ast-grep.
13
13
  - C++ qualified calls used as expression statements need the statement semicolon in the pattern: use `ns::doThing($ARG);`, `$CALLEE($ARG);`, or wrap a statement snippet. Without `;`, tree-sitter-cpp may parse `ns::doThing($ARG)` as declaration-like syntax and return no matches
14
14
  - For TS declarations/methods, tolerate unknown annotations: `async function $NAME($$$ARGS): $_ { $$$BODY }` or `class $_ { method($ARG: $_): $_ { $$$BODY } }`
15
15
  - Declaration forms are structurally distinct — top-level `function foo`, class method `foo()`, and `const foo = () => {}` are different AST shapes; search the right form before concluding absence
16
- - Loosest existence check: `pat: "executeBash"` with a narrow `path`
16
+ - Loosest existence check: `pat: "executeBash"` with narrow `paths`
17
17
  </instruction>
18
18
 
19
19
  <output>
@@ -24,19 +24,19 @@ Performs structural code search using AST matching via native ast-grep.
24
24
 
25
25
  <examples>
26
26
  # Search TypeScript files under src
27
- `{"pat":"console.log($$$)","path":"src/**/*.ts"}`
27
+ `{"pat":"console.log($$$)","paths":["src/**/*.ts"]}`
28
28
  # Named imports from a specific package
29
- `{"pat":"import { $$$IMPORTS } from \"react\"","path":"src/**/*.ts"}`
29
+ `{"pat":"import { $$$IMPORTS } from \"react\"","paths":["src/**/*.ts"]}`
30
30
  # Arrow functions assigned to a const
31
- `{"pat":"const $NAME = ($$$ARGS) => $BODY","path":"src/utils/**/*.ts"}`
31
+ `{"pat":"const $NAME = ($$$ARGS) => $BODY","paths":["src/utils/**/*.ts"]}`
32
32
  # Method call on any object, ignoring method name with `$_`
33
- `{"pat":"logger.$_($$$ARGS)","path":"src/**/*.ts"}`
33
+ `{"pat":"logger.$_($$$ARGS)","paths":["src/**/*.ts"]}`
34
34
  # Loosest existence check for a symbol in one file
35
- `{"pat":"processItems","path":"src/worker.ts"}`
35
+ `{"pat":"processItems","paths":["src/worker.ts"]}`
36
36
  </examples>
37
37
 
38
38
  <critical>
39
- - Avoid repo-root scans — narrow `path` first
40
- - Parse issues are query failure, not evidence of absence: repair the pattern or tighten `path` before concluding "no matches"
39
+ - Avoid repo-root scans — narrow `paths` first
40
+ - Parse issues are query failure, not evidence of absence: repair the pattern or tighten `paths` before concluding "no matches"
41
41
  - For broad/open-ended exploration across subsystems, use Task tool with explore subagent first
42
42
  </critical>
@@ -1,17 +1,19 @@
1
1
  Run code in a persistent kernel, using a series of codeblocks acting as cells.
2
2
 
3
3
  <instruction>
4
- Each cell is a markdown fenced code block. The opening fence's info string carries metadata:
4
+ Each cell is introduced by a header line of the form:
5
5
 
6
6
  ```
7
- <lang>? <duration>? (title-fragment | key=value)*
7
+ ===== <info> =====
8
8
  ```
9
- - **Language**: {{#if py}}`py`/`python` for Python{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}`js`/`javascript`/`ts`/`typescript` for JavaScript{{/if}}.{{#ifAll py js}} Omitted → inherit the previous cell's language (the first cell defaults to Python, falling back to JavaScript when Python is unavailable).{{else}} Omitted → inherit the previous cell's language.{{/ifAll}}
10
- - **Positional duration**: `15s`, `500ms`, `2m`, or a bare integer (seconds). Default 30s.
9
+
10
+ where each side is at least 5 equal signs. Everything between one header and the next (or end of input) is the cell's code, verbatim. The info is space-separated tokens, all optional, in any order:
11
+ - **Language**: {{#if py}}`py` for Python{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}`js` / `ts` for JavaScript{{/if}}.{{#ifAll py js}} Omitted → inherit the previous cell's language (the first cell defaults to Python, falling back to JavaScript when Python is unavailable).{{else}} Omitted → inherit the previous cell's language.{{/ifAll}}
12
+ - **Title shorthand**: `py:"…"`, `js:"…"`, `ts:"…"` set the language and the cell title together.
11
13
  - **Attributes**:
12
- - `id="…"` — cell id (shown as the title in the transcript).
13
- - `t=<duration>` — overrides the positional duration.
14
- - `rst=true` — wipe **this cell's own language kernel** before running.{{#ifAll py js}} Other languages are untouched.{{/ifAll}}
14
+ - `id:"…"` — cell title (when language is unchanged or already set).
15
+ - `t:<duration>` — per-cell timeout. Duration is digits with optional `ms` / `s` / `m` units (e.g. `t:500ms`, `t:15s`, `t:2m`). Default 30s.
16
+ - `rst` — wipe **this cell's own language kernel** before running.{{#ifAll py js}} Other languages are untouched.{{/ifAll}}
15
17
 
16
18
  **Work incrementally:** one logical step per cell (imports, define, test, use). Pass multiple small cells in one call. Define small reusable functions you can debug individually. You **MUST** put workflow explanations in the assistant message or cell title — never inside cell code.
17
19
 
@@ -31,18 +33,6 @@ write(path, content) → str
31
33
  Write content to a file (creates parent directories). Returns the resolved path.
32
34
  append(path, content) → str
33
35
  Append content to a file. Returns the resolved path.
34
- stat(path) → {path, size, is_file, is_dir, mtime}
35
- File or directory metadata. mtime is an ISO-8601 string.
36
- find(pattern, path?=".", type?="file", limit?=1000, hidden?=False, sort_by_mtime?=False, maxdepth?=None, mindepth?=None) → list[path]
37
- Recursive glob find. Respects .gitignore.
38
- glob(pattern, path?=".", hidden?=False) → list[path]
39
- Non-recursive glob. Use find() for recursive walks. Respects .gitignore.
40
- grep(pattern, path, ignore_case?=False, literal?=False, context?=0) → list[{line, text}]
41
- Search a single file.
42
- rgrep(pattern, path?=".", glob_pattern?="*", ignore_case?=False, literal?=False, limit?=100, hidden?=False) → list[{file, line, text}]
43
- Search recursively across files. Respects .gitignore.
44
- sed(path, pattern, repl, flags?=0) → int
45
- Regex replace in a file (like sed -i). Returns replacement count.
46
36
  tree(path?=".", max_depth?=3, show_hidden?=False) → str
47
37
  Render a directory tree.
48
38
  diff(a, b) → str
@@ -63,30 +53,22 @@ Cells render like a Jupyter notebook. Pass any value to `display(value)`; non-pr
63
53
  </output>
64
54
 
65
55
  <caution>
66
- - In session mode, use `rst=true` on a cell to wipe its language's kernel before running.{{#ifAll py js}} Reset is per-language: a python cell's `rst=true` does not touch the JavaScript kernel and vice versa.{{/ifAll}}
56
+ - In session mode, use `rst` on a cell to wipe its language's kernel before running.{{#ifAll py js}} Reset is per-language: a python cell's `rst` does not touch the JavaScript kernel and vice versa.{{/ifAll}}
67
57
  {{#if js}}- **js**: the VM exposes a selective `process` subset, Web APIs, `Buffer`, `fs/promises`.
68
58
  {{/if}}</caution>
69
59
 
70
60
  <example>
71
- {{#if py}}```py id="imports" t="10s"
61
+ {{#if py}}===== py:"imports" t:10s =====
72
62
  import json
73
63
  from pathlib import Path
74
- ```
75
64
 
76
- ```py id="load config"
65
+ ===== py:"load config" =====
77
66
  data = json.loads(read('package.json'))
78
67
  display(data)
79
- ```
80
68
  {{/if}}{{#ifAll py js}}
81
-
82
- {{/ifAll}}{{#if js}}```js id="js summary" rst=true
69
+ {{/ifAll}}{{#if js}}===== js:"js summary" rst =====
83
70
  const data = JSON.parse(await read('package.json'));
84
71
  display(data);
85
72
  return data.name;
86
- ```
87
-
88
- ```
89
- return 'still JavaScript';
90
- ```
91
73
  {{/if}}
92
74
  </example>
@@ -1,6 +1,7 @@
1
1
  Finds files using fast pattern matching that works with any codebase size.
2
2
 
3
3
  <instruction>
4
+ - `paths` is required and accepts an array of globs, files, or directories
4
5
  - You **SHOULD** perform multiple searches in parallel when potentially useful
5
6
  </instruction>
6
7
 
@@ -10,7 +11,7 @@ Matching file paths sorted by modification time (most recent first). Truncated a
10
11
 
11
12
  <examples>
12
13
  # Find files
13
- `{"pattern": "src/**/*.ts", "limit": 1000}`
14
+ `{"paths": ["src/**/*.ts"], "limit": 1000}`
14
15
  </examples>
15
16
 
16
17
  <avoid>
@@ -1,69 +1,78 @@
1
- Applies precise file edits using full anchors from `read` output (for example `160sr`).
1
+ Your patch language is a compact, line-anchored edit format.
2
2
 
3
- Read the file first. Copy the full anchors exactly as shown by `read`.
3
+ A patch contains one or more file sections. The first non-blank line of every edit section **MUST** be `@PATH`.
4
+ Operations reference lines in the file by their line number and hash, called "Anchors", e.g. `5th`, `123ab`.
5
+ You **MUST** copy them verbatim from the latest output for the file you're editing.
4
6
 
5
- <operations>
6
- **Top level**
7
- - `edits` — array of edit entries
8
- - `path` (required) — file path for all edits in this request
7
+ This format is purely textual. The tool has NO awareness of language, indentation, brackets, fences, or table widths. You are responsible for emitting valid syntax in your replacements/insertions.
9
8
 
10
- **Edit entry**: `{ loc, content }`
11
- - `loc` where to apply the edit (see below)
12
- - `content` replacement/inserted lines (`string[]`, one element per line; `null` to delete)
9
+ <ops>
10
+ @PATH header: subsequent ops apply to PATH
11
+ < ANCHOR insert lines BEFORE the anchored line (or BOF); payload follows as `|TEXT` lines
12
+ + ANCHOR insert lines AFTER the anchored line (or EOF); payload follows as `|TEXT` lines
13
+ - A..B delete the line range (inclusive); `- A` for one line
14
+ = A..B replace the range with payload `|TEXT` lines, or with one blank line if no payload follows
15
+ </ops>
13
16
 
14
- **`loc` values**
15
- - `"append"` / `"prepend"` insert at end/start of file
16
- - `{ append: "123th" }` / `{ prepend: "123th" }` insert after/before anchored line
17
- - `{ range: { pos: "123th", end: "123th" } }` replace inclusive range `pos..end` with new content (set `pos == end` for single-line replace)
18
- </operations>
17
+ <rules>
18
+ - Every line of inserted/replacement content **MUST** be emitted as a payload line starting with `|`.
19
+ - `|` is syntax, not content. The inserted text begins after the first `|`; use a bare `|` to insert a blank line.
20
+ - `< A` inserts before line A; `+ A` inserts after line A. `< BOF` / `+ BOF` both prepend; `< EOF` / `+ EOF` both append.
21
+ - `= A..B` replaces the inclusive range with the following payload lines. `= A` (or `= A..B`) with no payload blanks the range to a single empty line.
22
+ - `- A..B` deletes the inclusive range; omit `..B` for one line.
23
+ </rules>
24
+
25
+ <case file="a.ts">
26
+ {{hline 1 "const DEF = \"guest\";"}}
27
+ {{hline 2 ""}}
28
+ {{hline 3 "export function label(name) {"}}
29
+ {{hline 4 "\tconst clean = name || DEF;"}}
30
+ {{hline 5 "\treturn clean.trim();"}}
31
+ {{hline 6 "}"}}
32
+ </case>
19
33
 
20
34
  <examples>
21
- All examples below reference the same file:
35
+ # Replace one line (preserve the leading tab from the original)
36
+ @a.ts
37
+ = {{hrefr 5}}
38
+ | return clean.trim().toUpperCase();
39
+
40
+ # Replace a contiguous range with multiple lines
41
+ @a.ts
42
+ = {{hrefr 3}}..{{hrefr 6}}
43
+ |export function label(name: string): string {
44
+ | const clean = (name || DEF).trim();
45
+ | return clean.length === 0 ? DEF : clean.toUpperCase();
46
+ |}
47
+
48
+ # Insert BEFORE a line
49
+ @a.ts
50
+ < {{hrefr 5}}
51
+ | const debug = false;
52
+
53
+ # Insert AFTER a line
54
+ @a.ts
55
+ + {{hrefr 4}}
56
+ | if (clean.length === 0) return DEF;
57
+
58
+ # Append to end of file
59
+ @a.ts
60
+ + EOF
61
+ |export const done = true;
22
62
 
23
- ```ts title="a.ts"
24
- {{hline 1 "// @ts-ignore"}}
25
- {{hline 2 "const timeout = 5000;"}}
26
- {{hline 3 "const tag = \"DO NOT SHIP\";"}}
27
- {{hline 4 ""}}
28
- {{hline 5 "function alpha() {"}}
29
- {{hline 6 "\tlog();"}}
30
- {{hline 7 "}"}}
31
- {{hline 8 ""}}
32
- {{hline 9 "function beta() {"}}
33
- {{hline 10 "\t// TODO: remove after migration"}}
34
- {{hline 11 "\tlegacy();"}}
35
- {{hline 12 "\ttry {"}}
36
- {{hline 13 "\t\treturn parse(data);"}}
37
- {{hline 14 "\t} catch (err) {"}}
38
- {{hline 15 "\t\tconsole.error(err);"}}
39
- {{hline 16 "\t\treturn null;"}}
40
- {{hline 17 "\t}"}}
41
- {{hline 18 "}"}}
42
- ```
63
+ # Delete a single line
64
+ @a.ts
65
+ - {{hrefr 2}}
43
66
 
44
- # Replace a block body
45
- Replace only the catch body. Do not target the shared boundary line `} catch (err) {`.
46
- `{path:"a.ts",edits:[{loc:{range:{pos:{{href 15}},end:{{href 16}}}},content:["\t\tif (isEnoent(err)) return null;","\t\tthrow err;"]}]}`
47
- # Replace whole block including closing brace
48
- Replace `alpha`'s entire body including the closing `}`. `end` **MUST** be {{href 7}} because `content` includes `}`.
49
- `{path:"a.ts",edits:[{loc:{range:{pos:{{href 6}},end:{{href 7}}}},content:["\tvalidate();","\tlog();","}"]}]}`
50
- **Wrong**: `end: {{href 6}}` — line 7 (`}`) survives AND content emits `}`, producing two closing braces.
51
- # Replace one line
52
- Single-line replace uses `pos == end`.
53
- `{path:"a.ts",edits:[{loc:{range:{pos:{{href 2}},end:{{href 2}}}},content:["const timeout = 30_000;"]}]}`
54
- # Delete a range
55
- `{path:"a.ts",edits:[{loc:{range:{pos:{{href 10}},end:{{href 11}}}},content:null}]}`
56
- # Insert before a sibling
57
- When adding a sibling declaration, prefer `prepend` on the next declaration.
58
- `{path:"a.ts",edits:[{loc:{prepend:{{href 9}}},content:["function gamma() {","\tvalidate();","}",""]}]}`
67
+ # Blank a line in place (no payload required)
68
+ @a.ts
69
+ = {{hrefr 2}}
59
70
  </examples>
60
71
 
61
72
  <critical>
62
- - Make the minimum exact edit.
63
- - Copy the full anchors exactly as shown by `read/search` (for example `160sr`, not just `sr`).
64
- - `range` requires both `pos` and `end`.
65
- - **Closing-delimiter check**: when your replacement `content` ends with a closing delimiter (`}`, `*/`, `)`, `]`), compare it against the line immediately after `end` in the file. If they match, extend `end` to include that line — otherwise the original delimiter survives and `content` adds a second copy.
66
- - For a range, replace only the body or the whole range don't split range boundaries.
67
- - `content` must be literal file content with matching indentation. If the file uses tabs, use real tabs.
68
- - You **MUST NOT** use this tool to reformat or clean up unrelated code — use project-specific linters or code formatters instead.
73
+ - Always copy anchors exactly from tool output, but **NEVER** include line content after the `|` separator in the op line.
74
+ - Only emit changed lines. Do not restate unchanged context as payload.
75
+ - Every inserted/replacement content line **MUST** start with `|`; raw content lines are invalid.
76
+ - Do not write unified diff syntax (`@@`, `-OLD`, `+NEW`).
77
+ - To replace a block, use one `= A..B` op followed by all replacement `|TEXT` payload lines.
69
78
  </critical>
@@ -2,13 +2,13 @@ Searches files using powerful regex matching.
2
2
 
3
3
  <instruction>
4
4
  - Supports full regex syntax (e.g., `log.*Error`, `function\\s+\\w+`); literal braces need escaping (`interface\\{\\}` for `interface{}` in Go)
5
- - `path` is required and accepts a file, directory, glob, comma-separated path list, or internal URL
5
+ - `paths` is required and accepts an array of files, directories, globs, or internal URLs
6
6
  - Cross-line patterns are detected from literal `\n` or escaped `\\n` in `pattern`
7
7
  </instruction>
8
8
 
9
9
  <output>
10
10
  {{#if IS_HASHLINE_MODE}}
11
- - Text output is anchor-prefixed: `*123th|content` (match) or ` 123th|content` (context, leading space). The 2-letter ID is a content fingerprint.
11
+ - Text output is anchor-prefixed: `*5th|content` (match) or ` 9x}|content` (context, leading space). The 2-char suffix is a content fingerprint.
12
12
  {{else}}
13
13
  {{#if IS_LINE_NUMBER_MODE}}
14
14
  - Text output is line-number-prefixed