@oh-my-pi/pi-coding-agent 8.3.0 → 8.4.1

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 (108) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/package.json +6 -6
  3. package/scripts/format-prompts.ts +65 -23
  4. package/src/commit/agentic/prompts/session-user.md +0 -1
  5. package/src/commit/agentic/prompts/split-confirm.md +1 -1
  6. package/src/commit/agentic/prompts/system.md +1 -1
  7. package/src/commit/prompts/analysis-system.md +23 -26
  8. package/src/commit/prompts/analysis-user.md +1 -1
  9. package/src/commit/prompts/changelog-system.md +1 -2
  10. package/src/commit/prompts/changelog-user.md +1 -2
  11. package/src/commit/prompts/file-observer-system.md +1 -3
  12. package/src/commit/prompts/file-observer-user.md +1 -2
  13. package/src/commit/prompts/reduce-system.md +16 -16
  14. package/src/commit/prompts/reduce-user.md +1 -1
  15. package/src/commit/prompts/summary-retry.md +1 -2
  16. package/src/commit/prompts/summary-system.md +10 -10
  17. package/src/commit/prompts/summary-user.md +1 -1
  18. package/src/commit/prompts/types-description.md +1 -1
  19. package/src/config/keybindings.ts +3 -0
  20. package/src/config/model-registry.ts +8 -0
  21. package/src/config/settings-manager.ts +5 -0
  22. package/src/discovery/helpers.ts +12 -2
  23. package/src/extensibility/extensions/runner.ts +1 -0
  24. package/src/extensibility/extensions/types.ts +3 -0
  25. package/src/internal-urls/index.ts +1 -0
  26. package/src/internal-urls/plan-protocol.ts +95 -0
  27. package/src/ipy/executor.ts +4 -0
  28. package/src/modes/components/status-line/presets.ts +7 -7
  29. package/src/modes/components/status-line/segments.ts +16 -0
  30. package/src/modes/components/status-line/types.ts +4 -0
  31. package/src/modes/components/status-line-segment-editor.ts +1 -0
  32. package/src/modes/components/status-line.ts +16 -2
  33. package/src/modes/controllers/command-controller.ts +42 -0
  34. package/src/modes/controllers/event-controller.ts +14 -0
  35. package/src/modes/controllers/extension-ui-controller.ts +2 -0
  36. package/src/modes/controllers/input-controller.ts +18 -2
  37. package/src/modes/interactive-mode.ts +249 -1
  38. package/src/modes/rpc/rpc-mode.ts +4 -0
  39. package/src/modes/theme/theme.ts +17 -107
  40. package/src/modes/types.ts +9 -0
  41. package/src/patch/applicator.ts +202 -38
  42. package/src/patch/fuzzy.ts +135 -25
  43. package/src/patch/index.ts +34 -5
  44. package/src/patch/parser.ts +5 -0
  45. package/src/patch/types.ts +25 -0
  46. package/src/plan-mode/state.ts +6 -0
  47. package/src/prompts/agents/explore.md +1 -1
  48. package/src/prompts/agents/frontmatter.md +1 -1
  49. package/src/prompts/agents/init.md +1 -1
  50. package/src/prompts/agents/plan.md +1 -12
  51. package/src/prompts/agents/reviewer.md +7 -7
  52. package/src/prompts/agents/task.md +1 -2
  53. package/src/prompts/compaction/branch-summary-preamble.md +1 -1
  54. package/src/prompts/compaction/branch-summary.md +3 -1
  55. package/src/prompts/compaction/compaction-summary.md +3 -1
  56. package/src/prompts/compaction/compaction-turn-prefix.md +2 -1
  57. package/src/prompts/compaction/compaction-update-summary.md +3 -1
  58. package/src/prompts/review-request.md +4 -1
  59. package/src/prompts/system/custom-system-prompt.md +8 -8
  60. package/src/prompts/system/file-operations.md +1 -1
  61. package/src/prompts/system/plan-mode-active.md +136 -0
  62. package/src/prompts/system/plan-mode-approved.md +11 -0
  63. package/src/prompts/system/plan-mode-reference.md +13 -0
  64. package/src/prompts/system/plan-mode-subagent.md +38 -0
  65. package/src/prompts/system/summarization-system.md +1 -1
  66. package/src/prompts/system/system-prompt.md +17 -27
  67. package/src/prompts/system/title-system.md +1 -1
  68. package/src/prompts/system/ttsr-interrupt.md +1 -1
  69. package/src/prompts/system/web-search.md +1 -1
  70. package/src/prompts/tools/ask.md +1 -3
  71. package/src/prompts/tools/bash.md +1 -1
  72. package/src/prompts/tools/calculator.md +1 -1
  73. package/src/prompts/tools/enter-plan-mode.md +73 -0
  74. package/src/prompts/tools/exit-plan-mode.md +23 -0
  75. package/src/prompts/tools/fetch.md +1 -1
  76. package/src/prompts/tools/find.md +1 -1
  77. package/src/prompts/tools/gemini-image.md +1 -1
  78. package/src/prompts/tools/grep.md +1 -1
  79. package/src/prompts/tools/lsp.md +1 -1
  80. package/src/prompts/tools/patch.md +1 -3
  81. package/src/prompts/tools/python.md +2 -4
  82. package/src/prompts/tools/read.md +1 -1
  83. package/src/prompts/tools/replace.md +16 -16
  84. package/src/prompts/tools/ssh.md +1 -4
  85. package/src/prompts/tools/task.md +1 -3
  86. package/src/prompts/tools/todo-write.md +13 -16
  87. package/src/prompts/tools/web-search.md +1 -1
  88. package/src/prompts/tools/write.md +1 -1
  89. package/src/sdk.ts +67 -10
  90. package/src/session/agent-session.ts +268 -0
  91. package/src/session/session-manager.ts +3 -0
  92. package/src/task/agents.ts +1 -1
  93. package/src/task/executor.ts +50 -17
  94. package/src/task/index.ts +34 -7
  95. package/src/task/types.ts +3 -3
  96. package/src/task/worker-protocol.ts +8 -0
  97. package/src/task/worker.ts +11 -0
  98. package/src/tools/enter-plan-mode.ts +76 -0
  99. package/src/tools/exit-plan-mode.ts +62 -0
  100. package/src/tools/find.ts +5 -2
  101. package/src/tools/grep.ts +13 -12
  102. package/src/tools/index.ts +38 -2
  103. package/src/tools/plan-mode-guard.ts +46 -0
  104. package/src/tools/read.ts +8 -4
  105. package/src/tools/write.ts +3 -2
  106. package/src/utils/tools-manager.ts +38 -9
  107. package/src/web/search/providers/perplexity.ts +3 -1
  108. package/src/web/search/types.ts +3 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [8.4.1] - 2026-01-25
6
+
7
+ ### Added
8
+ - Added core plan mode with plan file approval workflow and tool gating
9
+ - Added plan:// internal URLs for plan file access and subagent plan-mode system prompt
10
+ - Added plan mode toggle shortcut with paused status indicator
11
+
12
+ ### Fixed
13
+ - Fixed plan reference injection and workflow prompt parameters for plan mode
14
+ - Fixed tool downloads hanging on slow/blocked GitHub by adding timeouts and zip extraction fallback
15
+ - Fixed missing UI notification when tools are downloaded or installed on demand
16
+ ## [8.4.0] - 2026-01-25
17
+
18
+ ### Added
19
+ - Added extension API to set working/loading messages during streaming
20
+ - Added task worker propagation of context files, skills, and prompt templates
21
+ - Added subagent option to skip Python preflight checks when Python tooling is unused
22
+ - Model field now accepts string arrays for fallback model prioritization
23
+
24
+ ### Changed
25
+ - Merged patch application warnings into edit tool diagnostics output
26
+ - Cached Python prelude docs for subagent workers to avoid repeated warmups
27
+ - Simplified image placeholders inserted on paste to match Claude-style markers
28
+
29
+ ### Fixed
30
+ - Rewrote empty or corrupted session files to restore valid headers
31
+ - Improved patch applicator ambiguity errors with match previews and overlap detection
32
+ - Fixed Task tool agent model resolution to honor comma-separated model lists
5
33
  ## [8.3.0] - 2026-01-25
6
34
 
7
35
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "8.3.0",
3
+ "version": "8.4.1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -75,11 +75,11 @@
75
75
  "test": "bun test"
76
76
  },
77
77
  "dependencies": {
78
- "@oh-my-pi/omp-stats": "8.3.0",
79
- "@oh-my-pi/pi-agent-core": "8.3.0",
80
- "@oh-my-pi/pi-ai": "8.3.0",
81
- "@oh-my-pi/pi-tui": "8.3.0",
82
- "@oh-my-pi/pi-utils": "8.3.0",
78
+ "@oh-my-pi/omp-stats": "8.4.1",
79
+ "@oh-my-pi/pi-agent-core": "8.4.1",
80
+ "@oh-my-pi/pi-ai": "8.4.1",
81
+ "@oh-my-pi/pi-tui": "8.4.1",
82
+ "@oh-my-pi/pi-utils": "8.4.1",
83
83
  "@openai/agents": "^0.4.3",
84
84
  "@sinclair/typebox": "^0.34.46",
85
85
  "ajv": "^8.17.1",
@@ -3,16 +3,22 @@
3
3
  * Format prompt files (mixed XML + Markdown + Handlebars).
4
4
  *
5
5
  * Rules:
6
- * 1. No blank line between "text:" and following list/block
6
+ * 1. No blank line before list items
7
7
  * 2. No blank line after opening XML tag or Handlebars block
8
8
  * 3. No blank line before closing XML tag or Handlebars block
9
- * 4. Collapse 2+ blank lines to single blank line
10
- * 5. Trim trailing whitespace (preserve indentation)
11
- * 6. Ensure single newline at EOF
9
+ * 4. Strip leading whitespace from closing XML tags and Handlebars (lines starting with {{)
10
+ * 5. Compact markdown tables (remove padding)
11
+ * 6. Collapse 2+ blank lines to single blank line
12
+ * 7. Trim trailing whitespace (preserve indentation)
13
+ * 8. No trailing newline at EOF
12
14
  */
13
15
  import { Glob } from "bun";
14
16
 
15
17
  const PROMPTS_DIR = new URL("../src/prompts/", import.meta.url).pathname;
18
+ const COMMIT_PROMPTS_DIR = new URL("../src/commit/prompts/", import.meta.url).pathname;
19
+ const AGENTIC_PROMPTS_DIR = new URL("../src/commit/agentic/prompts/", import.meta.url).pathname;
20
+
21
+ const PROMPT_DIRS = [PROMPTS_DIR, COMMIT_PROMPTS_DIR, AGENTIC_PROMPTS_DIR];
16
22
 
17
23
  // Opening XML tag (not self-closing, not closing)
18
24
  const OPENING_XML = /^<([a-z_-]+)(?:\s+[^>]*)?>$/;
@@ -22,12 +28,38 @@ const CLOSING_XML = /^<\/([a-z_-]+)>$/;
22
28
  const OPENING_HBS = /^\{\{#/;
23
29
  // Handlebars block end: {{/if}}, {{/has}}, {{/list}}, etc.
24
30
  const CLOSING_HBS = /^\{\{\//;
25
- // Line ending with colon (intro to a list) - handles **bold:** too
26
- const ENDS_WITH_COLON = /:\**\s*$/;
27
- // List item or Handlebars conditional that acts like list
28
- const LIST_OR_BLOCK = /^(\s*)[-*]|\d+\.\s|^\{\{#/;
31
+ // List item (- or * or 1.)
32
+ const LIST_ITEM = /^[-*]|\d+\.\s/;
29
33
  // Code fence
30
34
  const CODE_FENCE = /^```/;
35
+ // Table row
36
+ const TABLE_ROW = /^\|.*\|$/;
37
+ // Table separator (|---|---|)
38
+ const TABLE_SEP = /^\|[-:\s|]+\|$/;
39
+
40
+ /** Compact a table row by trimming cell padding */
41
+ function compactTableRow(line: string): string {
42
+ // Split by |, trim each cell, rejoin
43
+ const cells = line.split("|");
44
+ return cells.map((c) => c.trim()).join("|");
45
+ }
46
+
47
+ /** Compact a table separator row */
48
+ function compactTableSep(line: string): string {
49
+ // Normalize to minimal |---|---|
50
+ const cells = line.split("|").filter((c) => c.trim());
51
+ const normalized = cells.map((c) => {
52
+ const trimmed = c.trim();
53
+ // Preserve alignment markers
54
+ const left = trimmed.startsWith(":");
55
+ const right = trimmed.endsWith(":");
56
+ if (left && right) return ":---:";
57
+ if (left) return ":---";
58
+ if (right) return "---:";
59
+ return "---";
60
+ });
61
+ return "|" + normalized.join("|") + "|";
62
+ }
31
63
 
32
64
  function formatPrompt(content: string): string {
33
65
  const lines = content.split("\n");
@@ -37,9 +69,6 @@ function formatPrompt(content: string): string {
37
69
  for (let i = 0; i < lines.length; i++) {
38
70
  let line = lines[i];
39
71
 
40
- // Trim trailing whitespace (preserve leading)
41
- line = line.trimEnd();
42
-
43
72
  const trimmed = line.trim();
44
73
 
45
74
  // Track code blocks - don't modify inside them
@@ -54,6 +83,20 @@ function formatPrompt(content: string): string {
54
83
  continue;
55
84
  }
56
85
 
86
+ // Strip leading whitespace from closing XML tags and Handlebars
87
+ if (CLOSING_XML.test(trimmed) || trimmed.startsWith("{{")) {
88
+ line = trimmed;
89
+ } else if (TABLE_SEP.test(trimmed)) {
90
+ // Compact table separator
91
+ line = compactTableSep(trimmed);
92
+ } else if (TABLE_ROW.test(trimmed)) {
93
+ // Compact table row
94
+ line = compactTableRow(trimmed);
95
+ } else {
96
+ // Trim trailing whitespace (preserve leading for non-closing-tags)
97
+ line = line.trimEnd();
98
+ }
99
+
57
100
  const isBlank = trimmed === "";
58
101
 
59
102
  // Skip blank lines that violate our rules
@@ -61,8 +104,8 @@ function formatPrompt(content: string): string {
61
104
  const prevLine = result[result.length - 1]?.trim() ?? "";
62
105
  const nextLine = lines[i + 1]?.trim() ?? "";
63
106
 
64
- // Rule 1: No blank between "text:" and list/block
65
- if (ENDS_WITH_COLON.test(prevLine) && LIST_OR_BLOCK.test(nextLine)) {
107
+ // Rule 1: No blank line before list items
108
+ if (LIST_ITEM.test(nextLine)) {
66
109
  continue;
67
110
  }
68
111
 
@@ -93,11 +136,10 @@ function formatPrompt(content: string): string {
93
136
  result.push(line);
94
137
  }
95
138
 
96
- // Rule 6: Single newline at EOF
139
+ // Rule 8: No trailing newline at EOF
97
140
  while (result.length > 0 && result[result.length - 1].trim() === "") {
98
141
  result.pop();
99
142
  }
100
- result.push("");
101
143
 
102
144
  return result.join("\n");
103
145
  }
@@ -106,24 +148,24 @@ async function main() {
106
148
  const glob = new Glob("**/*.md");
107
149
  const files: string[] = [];
108
150
  let changed = 0;
151
+ const check = process.argv.includes("--check");
109
152
 
110
- for await (const path of glob.scan(PROMPTS_DIR)) {
111
- files.push(path);
153
+ for (const dir of PROMPT_DIRS) {
154
+ for await (const path of glob.scan(dir)) {
155
+ files.push(`${dir}${path}`);
156
+ }
112
157
  }
113
158
 
114
- const check = process.argv.includes("--check");
115
-
116
- for (const relativePath of files) {
117
- const fullPath = `${PROMPTS_DIR}${relativePath}`;
159
+ for (const fullPath of files) {
118
160
  const original = await Bun.file(fullPath).text();
119
161
  const formatted = formatPrompt(original);
120
162
 
121
163
  if (original !== formatted) {
122
164
  if (check) {
123
- console.log(`Would format: ${relativePath}`);
165
+ console.log(`Would format: ${fullPath}`);
124
166
  } else {
125
167
  await Bun.write(fullPath, formatted);
126
- console.log(`Formatted: ${relativePath}`);
168
+ console.log(`Formatted: ${fullPath}`);
127
169
  }
128
170
  changed++;
129
171
  }
@@ -19,7 +19,6 @@ You may include entries from this list in the propose_changelog `deletions` fiel
19
19
  {{name}}:
20
20
  {{#list items prefix="- " join="\n"}}{{this}}{{/list}}
21
21
  {{/each}}
22
-
23
22
  {{/each}}
24
23
  {{/if}}
25
24
 
@@ -1 +1 @@
1
- Split commit plan has {{count}} commits. Proceed? (y/N):
1
+ Split commit plan has {{count}} commits. Proceed? (y/N):
@@ -37,4 +37,4 @@ Tool guidance:
37
37
  ## Changelog Requirements
38
38
 
39
39
  If changelog targets are provided, you MUST call `propose_changelog` before finishing.
40
- If you propose a split commit plan, include changelog target files in the relevant commit changes.
40
+ If you propose a split commit plan, include changelog target files in the relevant commit changes.
@@ -4,7 +4,6 @@ You are a senior release engineer who writes precise, changelog-ready commit cla
4
4
 
5
5
  <instructions>
6
6
  Classify this git diff into conventional commit format. Get this right — it affects release notes and semantic versioning.
7
-
8
7
  ## 1. Determine Scope
9
8
 
10
9
  Apply scope when 60%+ of line changes target a single component:
@@ -16,7 +15,6 @@ Use null for: cross-cutting changes, no dominant component, project-wide refacto
16
15
  Forbidden scopes (use null): src, lib, include, tests, benches, examples, docs, project name, app, main, entire, all, misc.
17
16
 
18
17
  Prefer scopes from <common_scopes> over inventing new ones.
19
-
20
18
  ## 2. Generate Details (0-6 items)
21
19
 
22
20
  Each detail:
@@ -39,17 +37,16 @@ Priority: user-visible -> perf/security -> architecture -> internal.
39
37
  Exclude: import changes, whitespace, formatting, trivial renames, debug prints, comment-only, file moves without modification.
40
38
 
41
39
  State only visible rationale. If unclear, use neutral: "Updated logic for correctness."
42
-
43
40
  ## 3. Assign Changelog Metadata
44
41
 
45
- | Condition | changelog_category |
46
- |-----------|--------------------|
47
- | New public API, feature, capability | "Added" |
48
- | Modified existing behavior | "Changed" |
49
- | Bug fix, correction | "Fixed" |
50
- | Feature marked for removal | "Deprecated" |
51
- | Feature/API removed | "Removed" |
52
- | Security fix or improvement | "Security" |
42
+ |Condition|changelog_category|
43
+ |---|---|
44
+ |New public API, feature, capability|"Added"|
45
+ |Modified existing behavior|"Changed"|
46
+ |Bug fix, correction|"Fixed"|
47
+ |Feature marked for removal|"Deprecated"|
48
+ |Feature/API removed|"Removed"|
49
+ |Security fix or improvement|"Security"|
53
50
 
54
51
  user_visible: true for: new features, APIs, breaking changes, user-affecting bug fixes, user-facing docs, security fixes.
55
52
 
@@ -62,20 +59,20 @@ Omit changelog_category when user_visible is false.
62
59
  Call create_conventional_analysis with:
63
60
 
64
61
  {
65
- "type": "feat|fix|refactor|docs|test|chore|style|perf|build|ci|revert",
66
- "scope": "component-name" | null,
67
- "details": [
68
- {
69
- "text": "Past-tense description ending with period.",
70
- "changelog_category": "Added|Changed|Fixed|Deprecated|Removed|Security",
71
- "user_visible": true
72
- },
73
- {
74
- "text": "Internal change description.",
75
- "user_visible": false
76
- }
77
- ],
78
- "issue_refs": []
62
+ "type": "feat|fix|refactor|docs|test|chore|style|perf|build|ci|revert",
63
+ "scope": "component-name" | null,
64
+ "details": [
65
+ {
66
+ "text": "Past-tense description ending with period.",
67
+ "changelog_category": "Added|Changed|Fixed|Deprecated|Removed|Security",
68
+ "user_visible": true
69
+ },
70
+ {
71
+ "text": "Internal change description.",
72
+ "user_visible": false
73
+ }
74
+ ],
75
+ "issue_refs": []
79
76
  }
80
77
  </output_format>
81
78
 
@@ -152,4 +149,4 @@ Call create_conventional_analysis with:
152
149
  </example>
153
150
  </examples>
154
151
 
155
- Be thorough. This matters.
152
+ Be thorough. This matters.
@@ -38,4 +38,4 @@
38
38
 
39
39
  <diff>
40
40
  {{ diff }}
41
- </diff>
41
+ </diff>
@@ -2,7 +2,6 @@ You are an expert changelog writer who analyzes git diffs and produces Keep a Ch
2
2
 
3
3
  <instructions>
4
4
  Analyze the diff and return JSON changelog entries.
5
-
6
5
  1. Identify user-visible changes only
7
6
  2. Categorize each change (Added, Changed, Deprecated, Removed, Fixed, Security, Breaking Changes)
8
7
  3. Write entries starting with past-tense verb describing user impact
@@ -53,4 +52,4 @@ Return ONLY valid JSON. No markdown fences, no explanation.
53
52
 
54
53
  With entries: {"entries": {"Added": ["entry 1"], "Fixed": ["entry 2"]}}
55
54
  No changelog-worthy changes: {"entries": {}}
56
- </output_format>
55
+ </output_format>
@@ -3,7 +3,6 @@ Changelog: {{ changelog_path }}
3
3
  {{#if is_package_changelog}}Scope: Package-level changelog. Omit package name prefix from entries.{{/if}}
4
4
  </context>
5
5
  {{#if existing_entries}}
6
-
7
6
  <existing_entries>
8
7
  Already documented—skip these:
9
8
  {{ existing_entries }}
@@ -16,4 +15,4 @@ Already documented—skip these:
16
15
 
17
16
  <diff>
18
17
  {{ diff }}
19
- </diff>
18
+ </diff>
@@ -2,7 +2,6 @@
2
2
 
3
3
  <instructions>
4
4
  Extract factual observations from the diff. This matters—be precise.
5
-
6
5
  1. Use past-tense verb + specific target + optional purpose
7
6
  2. Max 100 characters per observation
8
7
  3. Consolidate related changes (e.g., "renamed 5 helper functions")
@@ -17,10 +16,9 @@ Exclude: import reordering, whitespace/formatting, comment-only changes, debug s
17
16
 
18
17
  <output_format>
19
18
  Plain list, no preamble, no summary, no markdown formatting.
20
-
21
19
  - added 'parse_config()' function for TOML configuration loading
22
20
  - removed deprecated 'legacy_init()' and all callers
23
21
  - changed 'Connection::new()' to accept '&Config' instead of individual params
24
22
  </output_format>
25
23
 
26
- Observations only. Classification happens in reduce phase.
24
+ Observations only. Classification happens in reduce phase.
@@ -2,8 +2,7 @@
2
2
  {{ diff }}
3
3
  </file>
4
4
  {{#if context_header}}
5
-
6
5
  <related_files>
7
6
  {{ context_header }}
8
7
  </related_files>
9
- {{/if}}
8
+ {{/if}}
@@ -41,20 +41,20 @@ Input observations:
41
41
 
42
42
  Output:
43
43
  {
44
- "type": "fix",
45
- "scope": "api",
46
- "details": [
47
- {
48
- "text": "Added token refresh guard to prevent duplicate refreshes.",
49
- "changelog_category": "Fixed",
50
- "user_visible": true
51
- },
52
- {
53
- "text": "Introduced retry wrapper for 429 responses.",
54
- "changelog_category": "Fixed",
55
- "user_visible": true
56
- }
57
- ],
58
- "issue_refs": []
44
+ "type": "fix",
45
+ "scope": "api",
46
+ "details": [
47
+ {
48
+ "text": "Added token refresh guard to prevent duplicate refreshes.",
49
+ "changelog_category": "Fixed",
50
+ "user_visible": true
51
+ },
52
+ {
53
+ "text": "Introduced retry wrapper for 429 responses.",
54
+ "changelog_category": "Fixed",
55
+ "user_visible": true
56
+ }
57
+ ],
58
+ "issue_refs": []
59
59
  }
60
- </example>
60
+ </example>
@@ -14,4 +14,4 @@
14
14
 
15
15
  <scope_candidates>
16
16
  {{ scope_candidates }}
17
- </scope_candidates>
17
+ </scope_candidates>
@@ -1,4 +1,3 @@
1
1
  {{#if base_context}}
2
2
  {{ base_context }}
3
-
4
- {{/if}}Previous summary failed validation: {{ errors }}
3
+ {{/if}}Previous summary failed validation: {{ errors }}
@@ -15,15 +15,15 @@ Get this right.
15
15
  </instructions>
16
16
 
17
17
  <verb_reference>
18
- | Type | Use instead |
19
- |----------|-------------------------------------------------|
20
- | feat | added, introduced, implemented, enabled |
21
- | fix | corrected, resolved, patched, addressed |
22
- | refactor | restructured, reorganized, migrated, simplified |
23
- | perf | optimized, reduced, eliminated, accelerated |
24
- | docs | documented, clarified, expanded |
25
- | build | upgraded, pinned, configured |
26
- | chore | cleaned, removed, renamed, organized |
18
+ |Type|Use instead|
19
+ |---|---|
20
+ |feat|added, introduced, implemented, enabled|
21
+ |fix|corrected, resolved, patched, addressed|
22
+ |refactor|restructured, reorganized, migrated, simplified|
23
+ |perf|optimized, reduced, eliminated, accelerated|
24
+ |docs|documented, clarified, expanded|
25
+ |build|upgraded, pinned, configured|
26
+ |chore|cleaned, removed, renamed, organized|
27
27
  </verb_reference>
28
28
 
29
29
  <examples>
@@ -49,4 +49,4 @@ comprehensive, various, several, improved, enhanced, quickly, simply, basically,
49
49
 
50
50
  <output_format>
51
51
  Output the description text only. Include motivation, name specifics, stay focused.
52
- </output_format>
52
+ </output_format>
@@ -10,4 +10,4 @@
10
10
 
11
11
  <diff_stat>
12
12
  {{ stat }}
13
- </diff_stat>
13
+ </diff_stat>
@@ -1,2 +1,2 @@
1
1
  Types: feat, fix, refactor, perf, docs, test, build, ci, chore, style, revert.
2
- Format: <type>(<scope>): <summary> with past-tense summary.
2
+ Format: <type>(<scope>): <summary> with past-tense summary.
@@ -23,6 +23,7 @@ export type AppAction =
23
23
  | "cycleModelForward"
24
24
  | "cycleModelBackward"
25
25
  | "selectModel"
26
+ | "togglePlanMode"
26
27
  | "expandTools"
27
28
  | "toggleThinking"
28
29
  | "externalEditor"
@@ -55,6 +56,7 @@ export const DEFAULT_APP_KEYBINDINGS: Record<AppAction, KeyId | KeyId[]> = {
55
56
  cycleModelForward: "ctrl+p",
56
57
  cycleModelBackward: "shift+ctrl+p",
57
58
  selectModel: "ctrl+l",
59
+ togglePlanMode: "alt+shift+p",
58
60
  historySearch: "ctrl+r",
59
61
  expandTools: "ctrl+o",
60
62
  toggleThinking: "ctrl+t",
@@ -82,6 +84,7 @@ const APP_ACTIONS: AppAction[] = [
82
84
  "cycleModelForward",
83
85
  "cycleModelBackward",
84
86
  "selectModel",
87
+ "togglePlanMode",
85
88
  "historySearch",
86
89
  "expandTools",
87
90
  "toggleThinking",
@@ -19,12 +19,18 @@ import type { AuthStorage } from "../session/auth-storage";
19
19
 
20
20
  const Ajv = (AjvModule as any).default || AjvModule;
21
21
 
22
+ const OpenRouterRoutingSchema = Type.Object({
23
+ only: Type.Optional(Type.Array(Type.String())),
24
+ order: Type.Optional(Type.Array(Type.String())),
25
+ });
26
+
22
27
  // Schema for OpenAI compatibility settings
23
28
  const OpenAICompatSchema = Type.Object({
24
29
  supportsStore: Type.Optional(Type.Boolean()),
25
30
  supportsDeveloperRole: Type.Optional(Type.Boolean()),
26
31
  supportsReasoningEffort: Type.Optional(Type.Boolean()),
27
32
  maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
33
+ openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
28
34
  });
29
35
 
30
36
  // Schema for custom model definition
@@ -36,6 +42,7 @@ const ModelDefinitionSchema = Type.Object({
36
42
  Type.Literal("openai-completions"),
37
43
  Type.Literal("openai-responses"),
38
44
  Type.Literal("openai-codex-responses"),
45
+ Type.Literal("azure-openai-responses"),
39
46
  Type.Literal("anthropic-messages"),
40
47
  Type.Literal("google-generative-ai"),
41
48
  Type.Literal("google-vertex"),
@@ -63,6 +70,7 @@ const ProviderConfigSchema = Type.Object({
63
70
  Type.Literal("openai-completions"),
64
71
  Type.Literal("openai-responses"),
65
72
  Type.Literal("openai-codex-responses"),
73
+ Type.Literal("azure-openai-responses"),
66
74
  Type.Literal("anthropic-messages"),
67
75
  Type.Literal("google-generative-ai"),
68
76
  Type.Literal("google-vertex"),
@@ -155,6 +155,7 @@ export interface TodoCompletionSettings {
155
155
  export type StatusLineSegmentId =
156
156
  | "pi"
157
157
  | "model"
158
+ | "plan_mode"
158
159
  | "path"
159
160
  | "git"
160
161
  | "subagents"
@@ -547,6 +548,10 @@ export class SettingsManager {
547
548
  return { ...this.settings };
548
549
  }
549
550
 
551
+ getPlansDirectory(_cwd: string = this.cwd ?? process.cwd()): string {
552
+ return path.join(getAgentDir(), "plans");
553
+ }
554
+
550
555
  /**
551
556
  * Access the underlying agent storage (null for in-memory settings).
552
557
  */
@@ -156,13 +156,23 @@ export function parseArrayOrCSV(value: unknown): string[] | undefined {
156
156
  return undefined;
157
157
  }
158
158
 
159
+ /**
160
+ * Parse model field into a prioritized list.
161
+ */
162
+ export function parseModelList(value: unknown): string[] | undefined {
163
+ const parsed = parseArrayOrCSV(value);
164
+ if (!parsed) return undefined;
165
+ const normalized = parsed.map(entry => entry.trim()).filter(Boolean);
166
+ return normalized.length > 0 ? normalized : undefined;
167
+ }
168
+
159
169
  /** Parsed agent fields from frontmatter (excludes source/filePath/systemPrompt) */
160
170
  export interface ParsedAgentFields {
161
171
  name: string;
162
172
  description: string;
163
173
  tools?: string[];
164
174
  spawns?: string[] | "*";
165
- model?: string;
175
+ model?: string[];
166
176
  output?: unknown;
167
177
  thinkingLevel?: ThinkingLevel;
168
178
  }
@@ -202,7 +212,7 @@ export function parseAgentFields(frontmatter: Record<string, unknown>): ParsedAg
202
212
  }
203
213
 
204
214
  const output = frontmatter.output !== undefined ? frontmatter.output : undefined;
205
- const model = typeof frontmatter.model === "string" ? frontmatter.model : undefined;
215
+ const model = parseModelList(frontmatter.model);
206
216
  const thinkingLevel = parseThinkingLevel(frontmatter);
207
217
 
208
218
  return { name, description, tools, spawns, model, output, thinkingLevel };
@@ -85,6 +85,7 @@ const noOpUIContext: ExtensionUIContext = {
85
85
  input: async (_title, _placeholder, _dialogOptions) => undefined,
86
86
  notify: () => {},
87
87
  setStatus: () => {},
88
+ setWorkingMessage: () => {},
88
89
  setWidget: () => {},
89
90
  setFooter: () => {},
90
91
  setHeader: () => {},
@@ -69,6 +69,9 @@ export interface ExtensionUIContext {
69
69
  /** Set status text in the footer/status bar. Pass undefined to clear. */
70
70
  setStatus(key: string, text: string | undefined): void;
71
71
 
72
+ /** Set the working/loading message shown during streaming. Call with no argument to restore default. */
73
+ setWorkingMessage(message?: string): void;
74
+
72
75
  /** Set a widget to display above the editor. Accepts string array or component factory. */
73
76
  setWidget(key: string, content: string[] | undefined): void;
74
77
  setWidget(key: string, content: ((tui: TUI, theme: Theme) => Component & { dispose?(): void }) | undefined): void;
@@ -22,6 +22,7 @@
22
22
  export { AgentProtocolHandler, type AgentProtocolOptions } from "./agent-protocol";
23
23
  export { ArtifactProtocolHandler, type ArtifactProtocolOptions } from "./artifact-protocol";
24
24
  export { applyQuery, parseQuery, pathToQuery } from "./json-query";
25
+ export { PlanProtocolHandler, type PlanProtocolOptions, resolvePlanUrlToPath } from "./plan-protocol";
25
26
  export { InternalUrlRouter } from "./router";
26
27
  export { RuleProtocolHandler, type RuleProtocolOptions } from "./rule-protocol";
27
28
  export { SkillProtocolHandler, type SkillProtocolOptions } from "./skill-protocol";