@oh-my-pi/pi-coding-agent 14.3.0 → 14.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.
- package/CHANGELOG.md +98 -1
- package/package.json +7 -7
- package/src/autoresearch/prompt.md +1 -1
- package/src/commit/agentic/prompts/analyze-file.md +1 -1
- package/src/config/model-registry.ts +67 -15
- package/src/config/prompt-templates.ts +5 -5
- package/src/config/settings-schema.ts +4 -4
- package/src/cursor.ts +3 -8
- package/src/discovery/helpers.ts +3 -3
- package/src/edit/diff.ts +50 -47
- package/src/edit/index.ts +86 -57
- package/src/edit/line-hash.ts +743 -24
- package/src/edit/modes/apply-patch.ts +0 -9
- package/src/edit/modes/atom.ts +893 -0
- package/src/edit/modes/chunk.ts +14 -24
- package/src/edit/modes/hashline.ts +193 -146
- package/src/edit/modes/patch.ts +5 -9
- package/src/edit/modes/replace.ts +6 -11
- package/src/edit/renderer.ts +14 -10
- package/src/edit/streaming.ts +50 -16
- package/src/exec/bash-executor.ts +2 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +4 -12
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/lsp/defaults.json +142 -652
- package/src/lsp/index.ts +1 -1
- package/src/mcp/render.ts +1 -8
- package/src/modes/components/assistant-message.ts +4 -0
- package/src/modes/components/diff.ts +23 -14
- package/src/modes/components/footer.ts +21 -16
- package/src/modes/components/session-selector.ts +3 -3
- package/src/modes/components/settings-defs.ts +6 -1
- package/src/modes/components/todo-reminder.ts +1 -8
- package/src/modes/components/tool-execution.ts +1 -4
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/print-mode.ts +8 -0
- package/src/prompts/agents/librarian.md +1 -1
- package/src/prompts/agents/reviewer.md +4 -4
- package/src/prompts/ci-green-request.md +1 -1
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +3 -3
- package/src/prompts/system/subagent-yield-reminder.md +11 -0
- package/src/prompts/system/system-prompt.md +3 -0
- package/src/prompts/tools/ask.md +3 -2
- package/src/prompts/tools/ast-edit.md +16 -20
- package/src/prompts/tools/ast-grep.md +19 -24
- package/src/prompts/tools/atom.md +87 -0
- package/src/prompts/tools/chunk-edit.md +37 -161
- package/src/prompts/tools/debug.md +4 -5
- package/src/prompts/tools/exit-plan-mode.md +4 -5
- package/src/prompts/tools/find.md +4 -8
- package/src/prompts/tools/github.md +18 -0
- package/src/prompts/tools/grep.md +4 -5
- package/src/prompts/tools/hashline.md +22 -89
- package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
- package/src/prompts/tools/inspect-image.md +6 -6
- package/src/prompts/tools/lsp.md +1 -1
- package/src/prompts/tools/patch.md +12 -19
- package/src/prompts/tools/python.md +3 -2
- package/src/prompts/tools/read-chunk.md +2 -3
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/ssh.md +8 -17
- package/src/prompts/tools/todo-write.md +54 -41
- package/src/sdk.ts +14 -9
- package/src/session/agent-session.ts +25 -2
- package/src/session/session-manager.ts +4 -1
- package/src/task/executor.ts +43 -48
- package/src/task/render.ts +11 -13
- package/src/tools/ask.ts +7 -7
- package/src/tools/ast-edit.ts +45 -41
- package/src/tools/ast-grep.ts +77 -85
- package/src/tools/bash.ts +8 -9
- package/src/tools/browser.ts +32 -30
- package/src/tools/calculator.ts +4 -4
- package/src/tools/cancel-job.ts +1 -1
- package/src/tools/checkpoint.ts +2 -2
- package/src/tools/debug.ts +41 -37
- package/src/tools/exit-plan-mode.ts +1 -1
- package/src/tools/find.ts +4 -4
- package/src/tools/gh-renderer.ts +12 -4
- package/src/tools/gh.ts +509 -697
- package/src/tools/grep.ts +116 -131
- package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
- package/src/tools/index.ts +14 -32
- package/src/tools/inspect-image.ts +3 -3
- package/src/tools/json-tree.ts +114 -114
- package/src/tools/match-line-format.ts +8 -7
- package/src/tools/notebook.ts +8 -7
- package/src/tools/poll-tool.ts +2 -1
- package/src/tools/python.ts +9 -23
- package/src/tools/read.ts +32 -25
- package/src/tools/render-mermaid.ts +1 -1
- package/src/tools/render-utils.ts +18 -0
- package/src/tools/renderers.ts +2 -2
- package/src/tools/report-tool-issue.ts +3 -2
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +12 -10
- package/src/tools/search-tool-bm25.ts +2 -4
- package/src/tools/ssh.ts +4 -4
- package/src/tools/todo-write.ts +172 -147
- package/src/tools/vim.ts +14 -15
- package/src/tools/write.ts +4 -4
- package/src/tools/{submit-result.ts → yield.ts} +11 -13
- package/src/utils/edit-mode.ts +2 -1
- package/src/utils/file-display-mode.ts +10 -5
- package/src/utils/git.ts +9 -5
- package/src/utils/shell-snapshot.ts +2 -3
- package/src/vim/render.ts +4 -4
- package/src/prompts/system/subagent-submit-reminder.md +0 -11
- package/src/prompts/tools/gh-issue-view.md +0 -11
- package/src/prompts/tools/gh-pr-checkout.md +0 -12
- package/src/prompts/tools/gh-pr-diff.md +0 -12
- package/src/prompts/tools/gh-pr-push.md +0 -12
- package/src/prompts/tools/gh-pr-view.md +0 -11
- package/src/prompts/tools/gh-repo-view.md +0 -11
- package/src/prompts/tools/gh-run-watch.md +0 -12
- package/src/prompts/tools/gh-search-issues.md +0 -11
- package/src/prompts/tools/gh-search-prs.md +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,102 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [14.4.1] - 2026-04-26
|
|
6
|
+
### Breaking Changes
|
|
7
|
+
|
|
8
|
+
- Replaced the legacy `gh_repo_view`, `gh_issue_view`, `gh_pr_view`, `gh_pr_diff`, `gh_pr_checkout`, `gh_pr_push`, `gh_run_watch`, `gh_search_issues`, and `gh_search_prs` tool names with only `github`, which requires updating existing callers that invoked the old `gh_*` tools
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added a `sed` verb to the `atom` edit tool for line-local substitutions using sed-style syntax (`s/pattern/replacement/`) with `g`, `i`, and `F` flags and model-tolerant delimiter choices
|
|
13
|
+
- Added the unified `github` tool with op-based dispatch for repository, issue, pull request, search, checkout, push, and Actions watch workflows
|
|
14
|
+
- Added `op` routing so callers can select `repo_view`, `issue_view`, `pr_view`, `pr_diff`, `pr_checkout`, `pr_push`, `search_issues`, `search_prs`, or `run_watch` through a single tool entry point
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Changed hashline-based read and match output formatting to use `LINE+ID|content` as the anchor/content separator, and updated match/context markers to `>` for matches and `:` for context
|
|
19
|
+
- Updated GitHub CLI render output to show `GitHub <op>` for tool calls dispatched through `github` operations
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
|
|
23
|
+
- Removed the built-in `taplo` Language Server entry from default LSP settings, so TOML files no longer have default TOML server startup
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Fixed `atom` `loc` parsing so path-qualified anchors like `path:263ti| ...` and single-anchor locs containing hyphens no longer mis-parse as ranges
|
|
28
|
+
- Fixed hashline anchor handling in `atom` edits so a provided content hint after the anchor (`|` or `:` suffix) can rebond a stale hash to the intended line
|
|
29
|
+
- Fixed `atom` `sed` execution to tolerate common model-emitted forms such as `/pat/rep/`, and to apply safe literal fallbacks for regex failures or metacharacter-heavy patterns while still erroring when no match is possible
|
|
30
|
+
|
|
31
|
+
## [14.4.0] - 2026-04-26
|
|
32
|
+
|
|
33
|
+
### Breaking Changes
|
|
34
|
+
|
|
35
|
+
- Removed multi-pattern array input from `ast_grep` by changing `pat` to a single pattern string, so call sites using `pat: [...]` must be updated to send one query per invocation
|
|
36
|
+
- Removed `lang`, `glob`, and `sel` options from `ast_edit` and `ast_grep`, and moved those behaviors into the required `path` argument
|
|
37
|
+
- Required `path` for `ast_edit` and `ast_grep`, so invocations that relied on implicit repo-root searching are no longer valid
|
|
38
|
+
- Changed `todo_write` from multi-field verb payloads to an ordered array of flat operations, while retaining `replace` for harness bootstrap compatibility
|
|
39
|
+
- Renamed atom edit operations from `before` and `after` to `pre` and `post`, so existing `atom` payloads using the old operation keys must be updated
|
|
40
|
+
- Changed the hashline anchor format from `LINE#ID:content` to `LINEID:content` (no `#` separator, colon between anchor and content, no padding on line numbers); expanded the bigram alphabet from 40 hand-picked English bigrams to the full 647 single-token 2-letter bigrams — invalidates every previously captured `LINE#ID` reference
|
|
41
|
+
- Renamed the subagent completion contract from `submit_result` to `yield`, so subagent sessions must now finish with the `yield` tool and the `requireYieldTool` option; `submit_result`/`requireSubmitResultTool` and old completion calls are no longer recognized
|
|
42
|
+
- Changed the hashline and chunk anchor ID format from the prior hex-like tokens to two-letter BPE bigrams (for example `#th`), which invalidates previously captured `LINE#ID`/chunk selectors and requires re-reading to refresh anchors
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- Added inline file overrides in atom locators (`loc: "a.ts:160sr"`) so cross-file edits can be written without a separate per-entry `path` field
|
|
47
|
+
- Added `openai` to the `providers.image` options, allowing image generation to be explicitly routed through the active GPT Responses/Codex model
|
|
48
|
+
- Added `between` atom edit operation to replace only the lines between two surviving anchors while preserving the boundary anchors
|
|
49
|
+
- Added conflict detection for `between` atom edits to require non-overlapping regions and forbid edits targeting lines strictly inside those regions
|
|
50
|
+
- Added `atom` edit mode to `edit` with single-anchor operations (`set`, `before`, `after`, `del`, `sub`, `ins`) for hashline-anchored line edits
|
|
51
|
+
- Added support for request-level `path` defaults in patch, replace, and chunk edits so shared file paths no longer need to be repeated in every entry
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- Updated `atom` and `hashline` edit anchor validation to auto-rebase a stale anchor within ±2 lines when the same hash matches a unique nearby line, continuing the edit with a warning instead of immediate failure
|
|
56
|
+
- Changed bash command output labels from `[full result: artifact://…]` to `[raw output: artifact://…]` for artifact references produced from large command output
|
|
57
|
+
- Changed `todo_write` `done`, `rm`, and `drop` operations to target all tasks when neither `task` nor `phase` is provided, and made `append` create the target phase automatically when missing
|
|
58
|
+
- Updated `ast_edit` and `ast_grep` to pass file-selection intent through `path` (including inline globs and comma/space-separated path lists) instead of separate `glob` filters
|
|
59
|
+
- Changed `ast_grep` pagination API from `offset` to `skip`
|
|
60
|
+
- Flattened `todo_write` operation arguments to `{ op, task?, phase?, items? }[]` and removed task details from the persisted todo shape
|
|
61
|
+
- Changed `grep` truncation output to report `Result limit reached; narrow path.` and label match/result caps as `first N`
|
|
62
|
+
- Changed JSON tree output to truncate inline argument pairs by available width and add an ellipsis when values no longer fit in the display
|
|
63
|
+
- Changed JSON tree rendering to hide harness-internal `intent` and `__partialJson` fields from top-level tool output
|
|
64
|
+
- Simplified the `grep` tool schema by requiring `path`, folding glob and type filtering into path globs, auto-detecting multiline patterns, removing model-controlled context and limit options, and renaming result skipping to `skip`.
|
|
65
|
+
- Changed atom edit request format to use a shared `loc` selector, including range (`"160sr-9ab"`) and boundary (`"^"`, `"$"`) forms instead of per-operation anchor fields
|
|
66
|
+
- Changed atom edit payload fields so `set`, `pre`, and `post` now require line-array values and `sub` now takes a `[find, replace]` tuple, with boundary deletion now expressed as `set: []`
|
|
67
|
+
- Changed edit diff wrapping to preserve the active line-prefix separator (`|` or `│`) while keeping continuation lines aligned by line-number width
|
|
68
|
+
- Changed Vim focus and viewport rendering to align cursor/selection markers and line numbers in a single gutter format
|
|
69
|
+
- Changed auto image provider selection for `providers.image=auto` to try active GPT image generation before Antigravity, OpenRouter, and Gemini
|
|
70
|
+
- Updated atom and hashline anchor validation to require the full `line+suffix` anchor format and report missing-line-number errors more clearly, including guidance when only a 2-letter suffix is provided
|
|
71
|
+
- Changed read, grep, and ast-edit line-prefixed output to drop fixed-width line number padding, so anchors render in natural width without leading spaces
|
|
72
|
+
- Updated terminal diff rendering to use a continuous `│` gutter and hide repeated line numbers on adjacent diff lines
|
|
73
|
+
- Updated subagent reminders, prompts, and rendered subagent output to reference `yield` completion and report missing/final results from `yield` tool data
|
|
74
|
+
- Updated the `edit` workflow to treat `atom` mode like hashline mode for read output, so hashline anchors are shown when `atom` is selected
|
|
75
|
+
- Adjusted patch/replace/chunk tooling to accept optional entry paths and to apply a top-level path default
|
|
76
|
+
- Updated hashline/chunk selector parsing to the new stable bigram token set used for checksums
|
|
77
|
+
- Renamed the image generation implementation module to `image-gen` and routed active GPT Responses/Codex models through OpenAI's hosted `image_generation` tool with WebP output
|
|
78
|
+
|
|
79
|
+
### Removed
|
|
80
|
+
|
|
81
|
+
- Removed line-range support from `atom` mode selectors, including `loc` values like `160sr-170ab`, so edits must target a single anchor (`160sr`, `^`, or `$`) per entry
|
|
82
|
+
- Removed the atom `del` verb and now require anchored-line deletion to be requested with `set: []`
|
|
83
|
+
- Removed `todo_write` task details and the `add_notes` operation
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
|
|
87
|
+
- Improved no-op edit diagnostics for `atom` and `hashline` operations so edits that leave content unchanged now fail with contextual details (edit index, locator, and reason), including guidance for `replace_range` no-op cases
|
|
88
|
+
- Wrapped `todo_write` operations in an `ops` object so Codex/OpenAI function schemas always use a JSON Schema object.
|
|
89
|
+
- Fixed JSON tree rendering for tool arguments by excluding injected internal keys from displayed root records
|
|
90
|
+
- Printed assistant `errorMessage` text in print mode output to stderr so message-level errors are visible during non-interactive runs
|
|
91
|
+
- Displayed assistant `errorMessage` text in the assistant message component for completed tool responses with non-terminal stop reasons
|
|
92
|
+
- Fixed atom input handling to ignore null optional verb fields so entries with `pre`, `set`, `post`, or `sub` set to `null` remain valid
|
|
93
|
+
- Fixed status-line Git branch rendering to degrade gracefully when the process hits `ENFILE`/`EMFILE` while reading optional Git refs
|
|
94
|
+
- Changed hashline mismatch failure output to show a clean numbered context block with numbered gutter and full-anchor alignment guidance when edits are rejected after the file changed
|
|
95
|
+
- Fixed `atom` mode to apply multiple edits on the same anchor line without index-shift artifacts, so mixed operations like `before`, `after`, `set`, `sub`, `ins`, and `del` now resolve consistently
|
|
96
|
+
- Fixed `atom` mode `append_file` insertion to preserve a file’s trailing newline sentinel when appending content
|
|
97
|
+
- Fixed `read` output for raw archive entries so hashline anchors, line numbers, and chunked formatting are not injected into raw content
|
|
98
|
+
- Fixed hashline parsing so lines like `# Note:` or `# TODO:` are no longer misinterpreted and stripped as hashline prefixes
|
|
99
|
+
- Adjusted patch and replace validation to report a clear missing-path error when neither an entry path nor a top-level path is provided
|
|
100
|
+
|
|
5
101
|
## [14.3.0] - 2026-04-25
|
|
6
102
|
|
|
7
103
|
### Added
|
|
@@ -60,6 +156,7 @@
|
|
|
60
156
|
- Fixed Mermaid fenced markdown rendering in assistant messages on terminals without image protocol support ([#650](https://github.com/can1357/oh-my-pi/issues/650))
|
|
61
157
|
- Fixed chunk edit path parsing so plan-mode edits to section-addressed `local://PLAN.md:<selector>` paths are classified as writes to the plan file
|
|
62
158
|
- Fixed SQLite `read` helper queries to reject `where=` clauses with SQL control syntax that could override the structured selector's pagination; raw SQL remains available through `q=SELECT ...`
|
|
159
|
+
- Fixed `models` provider transport overrides so `headers`-only entries apply without requiring `baseUrl`, including runtime `registerProvider()` overrides that now persist across `refresh()` / `refreshProvider()`, preserve existing `baseUrl` on subsequent headers-only updates, clear stale transport overrides when a provider is re-registered under a different extension source, and keep runtime transport headers authoritative when `modelOverrides` set overlapping header keys
|
|
63
160
|
|
|
64
161
|
## [14.2.0] - 2026-04-23
|
|
65
162
|
|
|
@@ -7186,4 +7283,4 @@ Initial public release.
|
|
|
7186
7283
|
- Git branch display in footer
|
|
7187
7284
|
- Message queueing during streaming responses
|
|
7188
7285
|
- OAuth integration for Gmail and Google Calendar access
|
|
7189
|
-
- HTML export with syntax highlighting and collapsible sections
|
|
7286
|
+
- HTML export with syntax highlighting and collapsible sections
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "14.
|
|
4
|
+
"version": "14.4.1",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@agentclientprotocol/sdk": "0.20.0",
|
|
48
48
|
"@mozilla/readability": "^0.6.0",
|
|
49
|
-
"@oh-my-pi/omp-stats": "14.
|
|
50
|
-
"@oh-my-pi/pi-agent-core": "14.
|
|
51
|
-
"@oh-my-pi/pi-ai": "14.
|
|
52
|
-
"@oh-my-pi/pi-natives": "14.
|
|
53
|
-
"@oh-my-pi/pi-tui": "14.
|
|
54
|
-
"@oh-my-pi/pi-utils": "14.
|
|
49
|
+
"@oh-my-pi/omp-stats": "14.4.1",
|
|
50
|
+
"@oh-my-pi/pi-agent-core": "14.4.1",
|
|
51
|
+
"@oh-my-pi/pi-ai": "14.4.1",
|
|
52
|
+
"@oh-my-pi/pi-natives": "14.4.1",
|
|
53
|
+
"@oh-my-pi/pi-tui": "14.4.1",
|
|
54
|
+
"@oh-my-pi/pi-utils": "14.4.1",
|
|
55
55
|
"@sinclair/typebox": "^0.34.49",
|
|
56
56
|
"@xterm/headless": "^6.0.0",
|
|
57
57
|
"ajv": "^8.20.0",
|
|
@@ -300,6 +300,7 @@ interface ProviderValidationModel {
|
|
|
300
300
|
|
|
301
301
|
interface ProviderValidationConfig {
|
|
302
302
|
baseUrl?: string;
|
|
303
|
+
headers?: Record<string, string>;
|
|
303
304
|
apiKey?: string;
|
|
304
305
|
api?: Api;
|
|
305
306
|
auth?: ProviderAuthMode;
|
|
@@ -321,9 +322,9 @@ function validateProviderConfiguration(
|
|
|
321
322
|
if (models.length === 0) {
|
|
322
323
|
if (mode === "models-config") {
|
|
323
324
|
const hasModelOverrides = config.modelOverrides && Object.keys(config.modelOverrides).length > 0;
|
|
324
|
-
if (!config.baseUrl && !config.compat && !hasModelOverrides && !config.discovery) {
|
|
325
|
+
if (!config.baseUrl && !config.headers && !config.compat && !hasModelOverrides && !config.discovery) {
|
|
325
326
|
throw new Error(
|
|
326
|
-
`Provider ${providerName}: must specify "baseUrl", "compat", "modelOverrides", "discovery", or "models"`,
|
|
327
|
+
`Provider ${providerName}: must specify "baseUrl", "headers", "compat", "modelOverrides", "discovery", or "models"`,
|
|
327
328
|
);
|
|
328
329
|
}
|
|
329
330
|
}
|
|
@@ -378,6 +379,7 @@ export const ModelsConfigFile = new ConfigFile<ModelsConfig>("models", ModelsCon
|
|
|
378
379
|
providerName,
|
|
379
380
|
{
|
|
380
381
|
baseUrl: providerConfig.baseUrl,
|
|
382
|
+
headers: providerConfig.headers,
|
|
381
383
|
apiKey: providerConfig.apiKey,
|
|
382
384
|
api: providerConfig.api as Api | undefined,
|
|
383
385
|
auth: (providerConfig.auth ?? "apiKey") as ProviderAuthMode,
|
|
@@ -770,6 +772,7 @@ export class ModelRegistry {
|
|
|
770
772
|
// models registered by extensions survive the model selector's offline reload.
|
|
771
773
|
#runtimeModelOverlays: CustomModelOverlay[] = [];
|
|
772
774
|
#runtimeProviderApiKeys: Map<string, string> = new Map();
|
|
775
|
+
#runtimeProviderOverrides: Map<string, ProviderOverride> = new Map();
|
|
773
776
|
#runtimeProvidersBySource: Map<string, Set<string>> = new Map();
|
|
774
777
|
#runtimeProviderSourceByName: Map<string, string> = new Map();
|
|
775
778
|
|
|
@@ -883,8 +886,8 @@ export class ModelRegistry {
|
|
|
883
886
|
const withConfigModels = this.#mergeCustomModels(resolvedDefaults, this.#customModelOverlays);
|
|
884
887
|
// Merge runtime extension models so they survive refresh() cycles
|
|
885
888
|
const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
|
|
886
|
-
|
|
887
|
-
this.#models = this.#
|
|
889
|
+
const withModelOverrides = this.#applyModelOverrides(combined, this.#modelOverrides);
|
|
890
|
+
this.#models = this.#applyRuntimeProviderOverrides(withModelOverrides);
|
|
888
891
|
this.#rebuildCanonicalIndex();
|
|
889
892
|
}
|
|
890
893
|
|
|
@@ -1175,7 +1178,8 @@ export class ModelRegistry {
|
|
|
1175
1178
|
const withConfigModels = this.#mergeCustomModels(resolved, this.#customModelOverlays);
|
|
1176
1179
|
// Merge runtime extension models so they survive online discovery completion
|
|
1177
1180
|
const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
|
|
1178
|
-
|
|
1181
|
+
const withModelOverrides = this.#applyModelOverrides(combined, this.#modelOverrides);
|
|
1182
|
+
this.#models = this.#applyRuntimeProviderOverrides(withModelOverrides);
|
|
1179
1183
|
this.#rebuildCanonicalIndex();
|
|
1180
1184
|
}
|
|
1181
1185
|
|
|
@@ -1657,6 +1661,32 @@ export class ModelRegistry {
|
|
|
1657
1661
|
});
|
|
1658
1662
|
}
|
|
1659
1663
|
|
|
1664
|
+
#mergeProviderOverride(baseOverride: ProviderOverride | undefined, override: ProviderOverride): ProviderOverride {
|
|
1665
|
+
return {
|
|
1666
|
+
baseUrl: override.baseUrl ?? baseOverride?.baseUrl,
|
|
1667
|
+
apiKey: override.apiKey ?? baseOverride?.apiKey,
|
|
1668
|
+
headers: override.headers ? { ...(baseOverride?.headers ?? {}), ...override.headers } : baseOverride?.headers,
|
|
1669
|
+
compat: override.compat ? mergeCompat(baseOverride?.compat, override.compat) : baseOverride?.compat,
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
#applyProviderTransportOverride<T extends { baseUrl?: string; headers?: Record<string, string> }>(
|
|
1673
|
+
entry: T,
|
|
1674
|
+
override: Pick<ProviderOverride, "baseUrl" | "headers">,
|
|
1675
|
+
): T {
|
|
1676
|
+
return {
|
|
1677
|
+
...entry,
|
|
1678
|
+
baseUrl: override.baseUrl ?? entry.baseUrl,
|
|
1679
|
+
headers: override.headers ? { ...entry.headers, ...override.headers } : entry.headers,
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
#applyRuntimeProviderOverrides(models: Model<Api>[]): Model<Api>[] {
|
|
1683
|
+
if (this.#runtimeProviderOverrides.size === 0) return models;
|
|
1684
|
+
return models.map(model => {
|
|
1685
|
+
const override = this.#runtimeProviderOverrides.get(model.provider);
|
|
1686
|
+
if (!override) return model;
|
|
1687
|
+
return this.#applyProviderTransportOverride(model, override);
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1660
1690
|
#applyModelOverrides(models: Model<Api>[], overrides: Map<string, Map<string, ModelOverride>>): Model<Api>[] {
|
|
1661
1691
|
if (overrides.size === 0) return models;
|
|
1662
1692
|
return models.map(model => {
|
|
@@ -1916,6 +1946,12 @@ export class ModelRegistry {
|
|
|
1916
1946
|
return this.authStorage.hasOAuth(model.provider);
|
|
1917
1947
|
}
|
|
1918
1948
|
|
|
1949
|
+
#clearRuntimeProviderState(providerName: string): void {
|
|
1950
|
+
this.#runtimeProviderApiKeys.delete(providerName);
|
|
1951
|
+
this.#runtimeProviderOverrides.delete(providerName);
|
|
1952
|
+
this.#runtimeModelOverlays = this.#runtimeModelOverlays.filter(overlay => overlay.provider !== providerName);
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1919
1955
|
/**
|
|
1920
1956
|
* Remove custom API/OAuth registrations for a specific extension source.
|
|
1921
1957
|
*/
|
|
@@ -1932,8 +1968,7 @@ export class ModelRegistry {
|
|
|
1932
1968
|
continue;
|
|
1933
1969
|
}
|
|
1934
1970
|
this.#runtimeProviderSourceByName.delete(providerName);
|
|
1935
|
-
this.#
|
|
1936
|
-
this.#runtimeModelOverlays = this.#runtimeModelOverlays.filter(overlay => overlay.provider !== providerName);
|
|
1971
|
+
this.#clearRuntimeProviderState(providerName);
|
|
1937
1972
|
}
|
|
1938
1973
|
this.#reloadStaticModels();
|
|
1939
1974
|
this.#rebuildCanonicalIndex();
|
|
@@ -1970,6 +2005,7 @@ export class ModelRegistry {
|
|
|
1970
2005
|
providerName,
|
|
1971
2006
|
{
|
|
1972
2007
|
baseUrl: config.baseUrl,
|
|
2008
|
+
headers: config.headers,
|
|
1973
2009
|
apiKey: config.apiKey,
|
|
1974
2010
|
api: config.api,
|
|
1975
2011
|
oauthConfigured: Boolean(config.oauth),
|
|
@@ -1993,6 +2029,7 @@ export class ModelRegistry {
|
|
|
1993
2029
|
});
|
|
1994
2030
|
}
|
|
1995
2031
|
|
|
2032
|
+
let sourceHandoff = false;
|
|
1996
2033
|
if (sourceId) {
|
|
1997
2034
|
this.#registeredProviderSources.add(sourceId);
|
|
1998
2035
|
const previousSourceId = this.#runtimeProviderSourceByName.get(providerName);
|
|
@@ -2002,12 +2039,18 @@ export class ModelRegistry {
|
|
|
2002
2039
|
if (previousProviders && previousProviders.size === 0) {
|
|
2003
2040
|
this.#runtimeProvidersBySource.delete(previousSourceId);
|
|
2004
2041
|
}
|
|
2042
|
+
this.#clearRuntimeProviderState(providerName);
|
|
2043
|
+
sourceHandoff = true;
|
|
2005
2044
|
}
|
|
2006
2045
|
const sourceProviders = this.#runtimeProvidersBySource.get(sourceId) ?? new Set<string>();
|
|
2007
2046
|
sourceProviders.add(providerName);
|
|
2008
2047
|
this.#runtimeProvidersBySource.set(sourceId, sourceProviders);
|
|
2009
2048
|
this.#runtimeProviderSourceByName.set(providerName, sourceId);
|
|
2010
2049
|
}
|
|
2050
|
+
if (sourceHandoff) {
|
|
2051
|
+
this.#reloadStaticModels();
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2011
2054
|
if (config.apiKey) {
|
|
2012
2055
|
this.#customProviderApiKeys.set(providerName, config.apiKey);
|
|
2013
2056
|
// Persist runtime API keys so they survive #reloadStaticModels() cycles
|
|
@@ -2042,29 +2085,38 @@ export class ModelRegistry {
|
|
|
2042
2085
|
for (const overlay of newOverlays) {
|
|
2043
2086
|
nextModels.push(finalizeCustomModel(overlay, { useDefaults: true }));
|
|
2044
2087
|
}
|
|
2088
|
+
const runtimeTransportOverride = this.#runtimeProviderOverrides.get(providerName);
|
|
2089
|
+
const withRuntimeTransportOverride = runtimeTransportOverride
|
|
2090
|
+
? nextModels.map(model => {
|
|
2091
|
+
if (model.provider !== providerName) return model;
|
|
2092
|
+
return this.#applyProviderTransportOverride(model, runtimeTransportOverride);
|
|
2093
|
+
})
|
|
2094
|
+
: nextModels;
|
|
2045
2095
|
|
|
2046
2096
|
if (config.oauth?.modifyModels) {
|
|
2047
2097
|
const credential = this.authStorage.getOAuthCredential(providerName);
|
|
2048
2098
|
if (credential) {
|
|
2049
|
-
this.#models = config.oauth.modifyModels(
|
|
2099
|
+
this.#models = config.oauth.modifyModels(withRuntimeTransportOverride, credential);
|
|
2050
2100
|
this.#rebuildCanonicalIndex();
|
|
2051
2101
|
return;
|
|
2052
2102
|
}
|
|
2053
2103
|
}
|
|
2054
2104
|
|
|
2055
|
-
this.#models =
|
|
2105
|
+
this.#models = withRuntimeTransportOverride;
|
|
2056
2106
|
this.#rebuildCanonicalIndex();
|
|
2057
2107
|
return;
|
|
2058
2108
|
}
|
|
2059
2109
|
|
|
2060
|
-
if (config.baseUrl) {
|
|
2110
|
+
if (config.baseUrl || config.headers) {
|
|
2111
|
+
const transportOverride = { baseUrl: config.baseUrl, headers: config.headers };
|
|
2112
|
+
const nextRuntimeOverride = this.#mergeProviderOverride(
|
|
2113
|
+
this.#runtimeProviderOverrides.get(providerName),
|
|
2114
|
+
transportOverride,
|
|
2115
|
+
);
|
|
2116
|
+
this.#runtimeProviderOverrides.set(providerName, nextRuntimeOverride);
|
|
2061
2117
|
this.#models = this.#models.map(m => {
|
|
2062
2118
|
if (m.provider !== providerName) return m;
|
|
2063
|
-
return
|
|
2064
|
-
...m,
|
|
2065
|
-
baseUrl: config.baseUrl ?? m.baseUrl,
|
|
2066
|
-
headers: config.headers ? { ...m.headers, ...config.headers } : m.headers,
|
|
2067
|
-
};
|
|
2119
|
+
return this.#applyProviderTransportOverride(m, transportOverride);
|
|
2068
2120
|
});
|
|
2069
2121
|
this.#rebuildCanonicalIndex();
|
|
2070
2122
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
parseFrontmatter,
|
|
10
10
|
prompt,
|
|
11
11
|
} from "@oh-my-pi/pi-utils";
|
|
12
|
-
import { computeLineHash } from "../edit/line-hash";
|
|
12
|
+
import { computeLineHash, HASHLINE_CONTENT_SEPARATOR } from "../edit/line-hash";
|
|
13
13
|
import { jtdToTypeScript } from "../tools/jtd-to-typescript";
|
|
14
14
|
import { parseCommandArgs, substituteArgs } from "../utils/command-args";
|
|
15
15
|
|
|
@@ -40,13 +40,13 @@ function formatHashlineRef(lineNum: unknown, content: unknown): { num: number; t
|
|
|
40
40
|
const num = typeof lineNum === "number" ? lineNum : Number.parseInt(String(lineNum), 10);
|
|
41
41
|
const raw = typeof content === "string" ? content : String(content ?? "");
|
|
42
42
|
const text = raw.replace(/\\t/g, "\t").replace(/\\n/g, "\n").replace(/\\r/g, "\r");
|
|
43
|
-
const ref = `${num}
|
|
43
|
+
const ref = `${num}${computeLineHash(num, text)}`;
|
|
44
44
|
return { num, text, ref };
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* {{href lineNum "content"}} — compute a real hashline ref for prompt examples.
|
|
49
|
-
* Returns `"
|
|
49
|
+
* Returns `"lineNumBIGRAM"` (e.g., `"42nd"`) using the actual hash algorithm.
|
|
50
50
|
*/
|
|
51
51
|
prompt.registerHelper("href", (lineNum: unknown, content: unknown): string => {
|
|
52
52
|
const { ref } = formatHashlineRef(lineNum, content);
|
|
@@ -55,11 +55,11 @@ prompt.registerHelper("href", (lineNum: unknown, content: unknown): string => {
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* {{hline lineNum "content"}} — format a full read-style line with prefix.
|
|
58
|
-
* Returns `"
|
|
58
|
+
* Returns `"lineNumBIGRAM|content"` (pipe between anchor and content).
|
|
59
59
|
*/
|
|
60
60
|
prompt.registerHelper("hline", (lineNum: unknown, content: unknown): string => {
|
|
61
61
|
const { ref, text } = formatHashlineRef(lineNum, content);
|
|
62
|
-
return `${ref}
|
|
62
|
+
return `${ref}${HASHLINE_CONTENT_SEPARATOR}${text}`;
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -955,7 +955,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
955
955
|
// Edit tool
|
|
956
956
|
"edit.mode": {
|
|
957
957
|
type: "enum",
|
|
958
|
-
values: ["replace", "patch", "hashline", "chunk", "vim", "apply_patch"] as const,
|
|
958
|
+
values: ["replace", "patch", "hashline", "chunk", "vim", "apply_patch", "atom"] as const,
|
|
959
959
|
default: "hashline",
|
|
960
960
|
ui: {
|
|
961
961
|
tab: "editing",
|
|
@@ -1021,7 +1021,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1021
1021
|
ui: {
|
|
1022
1022
|
tab: "editing",
|
|
1023
1023
|
label: "Hash Lines",
|
|
1024
|
-
description: "Include line hashes in read output for hashline edit mode (LINE
|
|
1024
|
+
description: "Include line hashes in read output for hashline edit mode (LINE+ID|content)",
|
|
1025
1025
|
},
|
|
1026
1026
|
},
|
|
1027
1027
|
|
|
@@ -1349,7 +1349,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1349
1349
|
tab: "tools",
|
|
1350
1350
|
label: "GitHub CLI",
|
|
1351
1351
|
description:
|
|
1352
|
-
"Enable
|
|
1352
|
+
"Enable the github tool (op-based dispatch for repository, issue, pull request, diff, search, checkout, push, and Actions watch workflows)",
|
|
1353
1353
|
},
|
|
1354
1354
|
},
|
|
1355
1355
|
|
|
@@ -1702,7 +1702,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1702
1702
|
},
|
|
1703
1703
|
"providers.image": {
|
|
1704
1704
|
type: "enum",
|
|
1705
|
-
values: ["auto", "gemini", "openrouter"] as const,
|
|
1705
|
+
values: ["auto", "openai", "gemini", "openrouter"] as const,
|
|
1706
1706
|
default: "auto",
|
|
1707
1707
|
ui: {
|
|
1708
1708
|
tab: "providers",
|
package/src/cursor.ts
CHANGED
|
@@ -177,16 +177,11 @@ export class CursorExecHandlers implements ICursorExecHandlers {
|
|
|
177
177
|
|
|
178
178
|
async grep(args: Parameters<NonNullable<ICursorExecHandlers["grep"]>>[0]) {
|
|
179
179
|
const toolCallId = decodeToolCallId(args.toolCallId);
|
|
180
|
+
const grepPath = args.glob ? `${args.path || "."}/${args.glob}` : args.path || ".";
|
|
180
181
|
const toolResultMessage = await executeTool(this.options, "grep", toolCallId, {
|
|
181
182
|
pattern: args.pattern,
|
|
182
|
-
path:
|
|
183
|
-
|
|
184
|
-
mode: args.outputMode || undefined,
|
|
185
|
-
context: args.context ?? args.contextBefore ?? args.contextAfter ?? undefined,
|
|
186
|
-
ignore_case: args.caseInsensitive || undefined,
|
|
187
|
-
type: args.type || undefined,
|
|
188
|
-
limit: args.headLimit ?? undefined,
|
|
189
|
-
multiline: args.multiline || undefined,
|
|
183
|
+
path: grepPath,
|
|
184
|
+
i: args.caseInsensitive || undefined,
|
|
190
185
|
});
|
|
191
186
|
return toolResultMessage;
|
|
192
187
|
}
|
package/src/discovery/helpers.ts
CHANGED
|
@@ -221,9 +221,9 @@ export function parseAgentFields(frontmatter: Record<string, unknown>): ParsedAg
|
|
|
221
221
|
|
|
222
222
|
let tools = parseArrayOrCSV(frontmatter.tools)?.map(tool => tool.toLowerCase());
|
|
223
223
|
|
|
224
|
-
// Subagents with explicit tool lists always need
|
|
225
|
-
if (tools && !tools.includes("
|
|
226
|
-
tools = [...tools, "
|
|
224
|
+
// Subagents with explicit tool lists always need yield
|
|
225
|
+
if (tools && !tools.includes("yield")) {
|
|
226
|
+
tools = [...tools, "yield"];
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// Parse spawns field (array, "*", or CSV)
|
package/src/edit/diff.ts
CHANGED
|
@@ -50,17 +50,8 @@ export class ApplyPatchError extends Error {
|
|
|
50
50
|
// Diff String Generation
|
|
51
51
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
52
52
|
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
if (lines.length > 1 && lines[lines.length - 1] === "") {
|
|
56
|
-
lines.pop();
|
|
57
|
-
}
|
|
58
|
-
return Math.max(1, lines.length);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function formatNumberedDiffLine(prefix: "+" | "-" | " ", lineNum: number, width: number, content: string): string {
|
|
62
|
-
const padded = String(lineNum).padStart(width, " ");
|
|
63
|
-
return `${prefix}${padded}|${content}`;
|
|
53
|
+
function formatNumberedDiffLine(prefix: "+" | "-" | " ", lineNum: number, content: string): string {
|
|
54
|
+
return `${prefix}${lineNum}|${content}`;
|
|
64
55
|
}
|
|
65
56
|
|
|
66
57
|
/**
|
|
@@ -71,9 +62,6 @@ export function generateDiffString(oldContent: string, newContent: string, conte
|
|
|
71
62
|
const parts = Diff.diffLines(oldContent, newContent);
|
|
72
63
|
const output: string[] = [];
|
|
73
64
|
|
|
74
|
-
const maxLineNum = Math.max(countContentLines(oldContent), countContentLines(newContent));
|
|
75
|
-
const lineNumWidth = String(maxLineNum).length;
|
|
76
|
-
|
|
77
65
|
let oldLineNum = 1;
|
|
78
66
|
let newLineNum = 1;
|
|
79
67
|
let lastWasChange = false;
|
|
@@ -95,10 +83,10 @@ export function generateDiffString(oldContent: string, newContent: string, conte
|
|
|
95
83
|
// Show the change
|
|
96
84
|
for (const line of raw) {
|
|
97
85
|
if (part.added) {
|
|
98
|
-
output.push(formatNumberedDiffLine("+", newLineNum,
|
|
86
|
+
output.push(formatNumberedDiffLine("+", newLineNum, line));
|
|
99
87
|
newLineNum++;
|
|
100
88
|
} else {
|
|
101
|
-
output.push(formatNumberedDiffLine("-", oldLineNum,
|
|
89
|
+
output.push(formatNumberedDiffLine("-", oldLineNum, line));
|
|
102
90
|
oldLineNum++;
|
|
103
91
|
}
|
|
104
92
|
}
|
|
@@ -108,40 +96,57 @@ export function generateDiffString(oldContent: string, newContent: string, conte
|
|
|
108
96
|
const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
|
|
109
97
|
|
|
110
98
|
if (lastWasChange || nextPartIsChange) {
|
|
111
|
-
|
|
112
|
-
let
|
|
113
|
-
let
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
99
|
+
const contextLimit = Math.max(0, contextLines);
|
|
100
|
+
let leadingSkip = 0;
|
|
101
|
+
let middleSkip = 0;
|
|
102
|
+
let trailingSkip = 0;
|
|
103
|
+
let linesToShow: string[];
|
|
104
|
+
|
|
105
|
+
if (lastWasChange && nextPartIsChange) {
|
|
106
|
+
if (raw.length > contextLimit * 2) {
|
|
107
|
+
const leadingContext = raw.slice(0, contextLimit);
|
|
108
|
+
const trailingContext = raw.slice(raw.length - contextLimit);
|
|
109
|
+
middleSkip = raw.length - leadingContext.length - trailingContext.length;
|
|
110
|
+
linesToShow = [...leadingContext, ...trailingContext];
|
|
111
|
+
} else {
|
|
112
|
+
linesToShow = raw;
|
|
113
|
+
}
|
|
114
|
+
} else if (nextPartIsChange) {
|
|
115
|
+
leadingSkip = Math.max(0, raw.length - contextLimit);
|
|
116
|
+
linesToShow = raw.slice(leadingSkip);
|
|
117
|
+
} else {
|
|
118
|
+
trailingSkip = Math.max(0, raw.length - contextLimit);
|
|
119
|
+
linesToShow = raw.slice(0, contextLimit);
|
|
125
120
|
}
|
|
126
121
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
newLineNum += skipStart;
|
|
122
|
+
if (leadingSkip > 0) {
|
|
123
|
+
output.push(formatNumberedDiffLine(" ", oldLineNum, "..."));
|
|
124
|
+
oldLineNum += leadingSkip;
|
|
125
|
+
newLineNum += leadingSkip;
|
|
132
126
|
}
|
|
133
127
|
|
|
134
|
-
|
|
135
|
-
|
|
128
|
+
const firstChunkLength = middleSkip > 0 ? contextLimit : linesToShow.length;
|
|
129
|
+
for (const line of linesToShow.slice(0, firstChunkLength)) {
|
|
130
|
+
output.push(formatNumberedDiffLine(" ", oldLineNum, line));
|
|
136
131
|
oldLineNum++;
|
|
137
132
|
newLineNum++;
|
|
138
133
|
}
|
|
139
134
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
135
|
+
if (middleSkip > 0) {
|
|
136
|
+
output.push(formatNumberedDiffLine(" ", oldLineNum, "..."));
|
|
137
|
+
oldLineNum += middleSkip;
|
|
138
|
+
newLineNum += middleSkip;
|
|
139
|
+
for (const line of linesToShow.slice(firstChunkLength)) {
|
|
140
|
+
output.push(formatNumberedDiffLine(" ", oldLineNum, line));
|
|
141
|
+
oldLineNum++;
|
|
142
|
+
newLineNum++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (trailingSkip > 0) {
|
|
147
|
+
output.push(formatNumberedDiffLine(" ", oldLineNum, "..."));
|
|
148
|
+
oldLineNum += trailingSkip;
|
|
149
|
+
newLineNum += trailingSkip;
|
|
145
150
|
}
|
|
146
151
|
} else {
|
|
147
152
|
// Skip these context lines entirely
|
|
@@ -184,8 +189,6 @@ export function generateUnifiedDiffString(oldContent: string, newContent: string
|
|
|
184
189
|
const patch = Diff.structuredPatch("", "", oldContent, newContent, "", "", { context: contextLines });
|
|
185
190
|
const output: string[] = [];
|
|
186
191
|
let firstChangedLine: number | undefined;
|
|
187
|
-
const maxLineNum = Math.max(countContentLines(oldContent), countContentLines(newContent));
|
|
188
|
-
const lineNumWidth = String(maxLineNum).length;
|
|
189
192
|
for (const hunk of patch.hunks) {
|
|
190
193
|
output.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`);
|
|
191
194
|
let oldLine = hunk.oldStart;
|
|
@@ -193,18 +196,18 @@ export function generateUnifiedDiffString(oldContent: string, newContent: string
|
|
|
193
196
|
for (const line of hunk.lines) {
|
|
194
197
|
if (line.startsWith("-")) {
|
|
195
198
|
if (firstChangedLine === undefined) firstChangedLine = newLine;
|
|
196
|
-
output.push(formatNumberedDiffLine("-", oldLine,
|
|
199
|
+
output.push(formatNumberedDiffLine("-", oldLine, line.slice(1)));
|
|
197
200
|
oldLine++;
|
|
198
201
|
continue;
|
|
199
202
|
}
|
|
200
203
|
if (line.startsWith("+")) {
|
|
201
204
|
if (firstChangedLine === undefined) firstChangedLine = newLine;
|
|
202
|
-
output.push(formatNumberedDiffLine("+", newLine,
|
|
205
|
+
output.push(formatNumberedDiffLine("+", newLine, line.slice(1)));
|
|
203
206
|
newLine++;
|
|
204
207
|
continue;
|
|
205
208
|
}
|
|
206
209
|
if (line.startsWith(" ")) {
|
|
207
|
-
output.push(formatNumberedDiffLine(" ", oldLine,
|
|
210
|
+
output.push(formatNumberedDiffLine(" ", oldLine, line.slice(1)));
|
|
208
211
|
oldLine++;
|
|
209
212
|
newLine++;
|
|
210
213
|
continue;
|