@arnilo/prism-tools 0.0.0-stage → 0.13.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 +418 -0
- package/LICENSE +21 -0
- package/README.md +44 -2
- package/browser/README.md +98 -0
- package/browser/skills/browser-automation/SKILL.md +14 -0
- package/browser/src/actions/cdp.ts +131 -0
- package/browser/src/actions/dialog.ts +25 -0
- package/browser/src/actions/download_release.ts +22 -0
- package/browser/src/actions/index.ts +37 -0
- package/browser/src/actions/locator.ts +70 -0
- package/browser/src/actions/navigate.ts +23 -0
- package/browser/src/actions/screenshot.ts +27 -0
- package/browser/src/actions/scroll.ts +24 -0
- package/browser/src/actions/select_page.ts +21 -0
- package/browser/src/actions/types.ts +114 -0
- package/browser/src/actions/upload.ts +44 -0
- package/browser/src/actions/wait.ts +45 -0
- package/browser/src/browser.ts +197 -0
- package/browser/src/cdp.ts +178 -0
- package/browser/src/checkpoint.ts +189 -0
- package/browser/src/config.ts +72 -0
- package/browser/src/downloads.ts +253 -0
- package/browser/src/errors.ts +31 -0
- package/browser/src/evaluate.ts +101 -0
- package/browser/src/index.ts +129 -0
- package/browser/src/limits.ts +271 -0
- package/browser/src/manager.ts +730 -0
- package/browser/src/network.ts +263 -0
- package/browser/src/observe.ts +178 -0
- package/browser/src/policy.ts +133 -0
- package/browser/src/screenshot.ts +107 -0
- package/browser/src/shared-sandbox.ts +53 -0
- package/browser/src/snapshot.ts +99 -0
- package/browser/src/targets.ts +165 -0
- package/browser/src/tools.ts +480 -0
- package/browser/src/types.ts +316 -0
- package/browser/src/uploads.ts +174 -0
- package/coding/README.md +337 -0
- package/coding/src/agent/acp-operations.ts +146 -0
- package/coding/src/agent/artifacts.ts +38 -0
- package/coding/src/agent/ask-user-decision.ts +623 -0
- package/coding/src/agent/atomic-write.ts +23 -0
- package/coding/src/agent/bounded-file.ts +21 -0
- package/coding/src/agent/checks.ts +301 -0
- package/coding/src/agent/coding-checkpoint.ts +828 -0
- package/coding/src/agent/delete.ts +237 -0
- package/coding/src/agent/diagnostics-tool.ts +163 -0
- package/coding/src/agent/diagnostics.ts +262 -0
- package/coding/src/agent/edit-diff.ts +382 -0
- package/coding/src/agent/edit.ts +322 -0
- package/coding/src/agent/effects.ts +102 -0
- package/coding/src/agent/env.ts +28 -0
- package/coding/src/agent/execution-policy.ts +89 -0
- package/coding/src/agent/file-mutation-queue.ts +72 -0
- package/coding/src/agent/forge/github.ts +603 -0
- package/coding/src/agent/forge/index.ts +16 -0
- package/coding/src/agent/forge/types.ts +185 -0
- package/coding/src/agent/git-aware-repository.ts +305 -0
- package/coding/src/agent/git-exec.ts +326 -0
- package/coding/src/agent/git-status.ts +177 -0
- package/coding/src/agent/git-tools.ts +360 -0
- package/coding/src/agent/git.ts +1074 -0
- package/coding/src/agent/glob-match.ts +146 -0
- package/coding/src/agent/glob.ts +182 -0
- package/coding/src/agent/goal-verify.ts +385 -0
- package/coding/src/agent/grep-worker.ts +74 -0
- package/coding/src/agent/grep.ts +459 -0
- package/coding/src/agent/identity.ts +48 -0
- package/coding/src/agent/index.ts +676 -0
- package/coding/src/agent/language/client.ts +429 -0
- package/coding/src/agent/language/framing.ts +190 -0
- package/coding/src/agent/language/index.ts +23 -0
- package/coding/src/agent/language/intelligence.ts +682 -0
- package/coding/src/agent/language/types.ts +187 -0
- package/coding/src/agent/lifecycle-events.ts +121 -0
- package/coding/src/agent/lifecycle.ts +166 -0
- package/coding/src/agent/limits.ts +214 -0
- package/coding/src/agent/move.ts +178 -0
- package/coding/src/agent/mutation-path.ts +52 -0
- package/coding/src/agent/output-accumulator.ts +304 -0
- package/coding/src/agent/path-utils.ts +120 -0
- package/coding/src/agent/process/index.ts +58 -0
- package/coding/src/agent/process/recovery.ts +508 -0
- package/coding/src/agent/process/sessions-host.ts +222 -0
- package/coding/src/agent/process/sessions-monitor.ts +141 -0
- package/coding/src/agent/process/sessions-recovery.ts +241 -0
- package/coding/src/agent/process/sessions-spawn.ts +613 -0
- package/coding/src/agent/process/sessions-teardown.ts +281 -0
- package/coding/src/agent/process/sessions.ts +30 -0
- package/coding/src/agent/process/types.ts +358 -0
- package/coding/src/agent/read-path-set.ts +126 -0
- package/coding/src/agent/read.ts +713 -0
- package/coding/src/agent/repository/glob.ts +147 -0
- package/coding/src/agent/repository/indexed-search.ts +480 -0
- package/coding/src/agent/repository/list.ts +131 -0
- package/coding/src/agent/repository/operations.ts +22 -0
- package/coding/src/agent/repository/path.ts +98 -0
- package/coding/src/agent/repository/search.ts +277 -0
- package/coding/src/agent/repository/types.ts +243 -0
- package/coding/src/agent/repository/walk.ts +126 -0
- package/coding/src/agent/repository.ts +12 -0
- package/coding/src/agent/shell-background.ts +264 -0
- package/coding/src/agent/shell-output.ts +75 -0
- package/coding/src/agent/shell.ts +526 -0
- package/coding/src/agent/todo.ts +399 -0
- package/coding/src/agent/truncate.ts +251 -0
- package/coding/src/agent/write.ts +170 -0
- package/coding/src/ask-user-host.ts +48 -0
- package/coding/src/config.ts +278 -0
- package/coding/src/hard-deny.ts +97 -0
- package/coding/src/index.ts +318 -0
- package/coding/src/principal-guard.ts +84 -0
- package/coding/src/sandbox.ts +92 -0
- package/coding/src/security/approval.ts +144 -0
- package/coding/src/security/command-rules.ts +57 -0
- package/coding/src/security/docker-cli.ts +222 -0
- package/coding/src/security/docker-sandbox.ts +1205 -0
- package/coding/src/security/e2b-sandbox.ts +853 -0
- package/coding/src/security/egress/dns-pin.ts +88 -0
- package/coding/src/security/egress/index.ts +32 -0
- package/coding/src/security/egress/limits.ts +57 -0
- package/coding/src/security/egress/policy.ts +137 -0
- package/coding/src/security/egress/proxy.ts +599 -0
- package/coding/src/security/egress/types.ts +30 -0
- package/coding/src/security/index.ts +199 -0
- package/coding/src/security/native-sandbox.ts +639 -0
- package/coding/src/security/path-containment.ts +55 -0
- package/coding/src/security/sandbox-coding-operations.ts +392 -0
- package/coding/src/security/sandbox-fs-operations.ts +644 -0
- package/coding/src/security/sandbox-limits.ts +147 -0
- package/coding/src/security/sandbox-tar.ts +308 -0
- package/coding/src/security/sandbox.ts +179 -0
- package/coding/src/security/semaphore.ts +55 -0
- package/coding/src/targets.ts +110 -0
- package/coding/src/workspace/diff.ts +78 -0
- package/coding/src/workspace/errors.ts +29 -0
- package/coding/src/workspace/index.ts +5 -0
- package/coding/src/workspace/merge.ts +151 -0
- package/coding/src/workspace/routing.ts +76 -0
- package/coding/src/workspace/service.ts +584 -0
- package/computer-use/README.md +105 -0
- package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
- package/computer-use/src/classify.ts +53 -0
- package/computer-use/src/command.ts +29 -0
- package/computer-use/src/config.ts +69 -0
- package/computer-use/src/create.ts +170 -0
- package/computer-use/src/devices.ts +247 -0
- package/computer-use/src/env.ts +28 -0
- package/computer-use/src/index.ts +157 -0
- package/computer-use/src/skill.ts +21 -0
- package/openapi/README.md +97 -0
- package/openapi/src/compile.ts +386 -0
- package/openapi/src/config.ts +73 -0
- package/openapi/src/errors.ts +16 -0
- package/openapi/src/execute.ts +241 -0
- package/openapi/src/index.ts +124 -0
- package/openapi/src/limits.ts +63 -0
- package/openapi/src/specs.ts +86 -0
- package/openapi/src/tools.ts +60 -0
- package/openapi/src/types.ts +61 -0
- package/package.json +171 -4
- package/research/README.md +72 -0
- package/research/skills/research-report/SKILL.md +12 -0
- package/research/skills/research-scoping/SKILL.md +10 -0
- package/research/skills/research-synthesis/SKILL.md +10 -0
- package/research/skills/source-triage/SKILL.md +10 -0
- package/research/src/check.ts +71 -0
- package/research/src/config.ts +41 -0
- package/research/src/index.ts +147 -0
- package/research/src/ledger.ts +157 -0
- package/research/src/urls.ts +21 -0
- package/web/README.md +287 -0
- package/web/skills/web-research/SKILL.md +17 -0
- package/web/src/brave.ts +66 -0
- package/web/src/cache.ts +151 -0
- package/web/src/config.ts +210 -0
- package/web/src/controls.ts +128 -0
- package/web/src/domains.ts +114 -0
- package/web/src/exa.ts +60 -0
- package/web/src/firecrawl.ts +160 -0
- package/web/src/index.ts +387 -0
- package/web/src/kagi.ts +73 -0
- package/web/src/limits.ts +55 -0
- package/web/src/native/convert.ts +84 -0
- package/web/src/native/fetch.ts +226 -0
- package/web/src/normalize.ts +131 -0
- package/web/src/obscura/cdp.ts +201 -0
- package/web/src/obscura/classify.ts +35 -0
- package/web/src/obscura/cli.ts +254 -0
- package/web/src/obscura/errors.ts +8 -0
- package/web/src/obscura/index.ts +59 -0
- package/web/src/obscura/limits.ts +92 -0
- package/web/src/obscura/mcp.ts +104 -0
- package/web/src/obscura/playwright.ts +133 -0
- package/web/src/obscura/process.ts +235 -0
- package/web/src/obscura/search-profile.ts +73 -0
- package/web/src/obscura/types.ts +48 -0
- package/web/src/obscura/web.ts +419 -0
- package/web/src/prompt-fetch.ts +88 -0
- package/web/src/render.ts +75 -0
- package/web/src/searxng.ts +78 -0
- package/web/src/slice.ts +139 -0
- package/web/src/tavily.ts +77 -0
- package/web/src/tools.ts +182 -0
- package/web/src/transport.ts +310 -0
- package/web/src/types.ts +199 -0
- package/work/README.md +77 -0
- package/work/THIRD_PARTY_NOTICES +24 -0
- package/work/docling/ocr.py +150 -0
- package/work/sandbox/Dockerfile +36 -0
- package/work/sandbox/soffice.sh +12 -0
- package/work/src/command.ts +29 -0
- package/work/src/config.ts +138 -0
- package/work/src/connectors/cli.ts +343 -0
- package/work/src/connectors/drafts.ts +647 -0
- package/work/src/connectors/errors.ts +8 -0
- package/work/src/connectors/file-bytes.ts +149 -0
- package/work/src/connectors/google-workspace-http.ts +399 -0
- package/work/src/connectors/google-workspace.ts +465 -0
- package/work/src/connectors/gws-tools.ts +378 -0
- package/work/src/connectors/http.ts +123 -0
- package/work/src/connectors/idempotency.ts +235 -0
- package/work/src/connectors/identity.ts +34 -0
- package/work/src/connectors/index.ts +93 -0
- package/work/src/connectors/limits.ts +50 -0
- package/work/src/connectors/m365-tools.ts +308 -0
- package/work/src/connectors/microsoft365-http.ts +428 -0
- package/work/src/connectors/microsoft365.ts +402 -0
- package/work/src/connectors/normalize.ts +120 -0
- package/work/src/connectors/tool-helpers.ts +398 -0
- package/work/src/connectors/tools.ts +42 -0
- package/work/src/connectors/types.ts +399 -0
- package/work/src/diagrams/canonicalize.ts +166 -0
- package/work/src/diagrams/caps.ts +52 -0
- package/work/src/diagrams/embed.ts +340 -0
- package/work/src/diagrams/errors.ts +76 -0
- package/work/src/diagrams/index.ts +81 -0
- package/work/src/diagrams/messages.ts +150 -0
- package/work/src/diagrams/origin.ts +52 -0
- package/work/src/diagrams/telemetry.ts +34 -0
- package/work/src/diagrams/xml.ts +267 -0
- package/work/src/document-extraction/index.ts +563 -0
- package/work/src/document-reader/ambient.d.ts +2 -0
- package/work/src/document-reader/errors.ts +7 -0
- package/work/src/document-reader/index.ts +181 -0
- package/work/src/document-reader/mistral-ocr.ts +318 -0
- package/work/src/document-reader/parsers.ts +171 -0
- package/work/src/documents/caps.ts +135 -0
- package/work/src/documents/diff.ts +269 -0
- package/work/src/documents/errors.ts +44 -0
- package/work/src/documents/fidelity.ts +98 -0
- package/work/src/documents/generate.ts +96 -0
- package/work/src/documents/hash.ts +8 -0
- package/work/src/documents/index.ts +14 -0
- package/work/src/documents/model-schema.ts +639 -0
- package/work/src/documents/parse.ts +252 -0
- package/work/src/documents/patch-history.ts +81 -0
- package/work/src/documents/patch.ts +476 -0
- package/work/src/documents/preview-html.ts +245 -0
- package/work/src/documents/preview.ts +227 -0
- package/work/src/documents/telemetry.ts +36 -0
- package/work/src/documents/translate/docx.ts +292 -0
- package/work/src/documents/translate/pptx.ts +304 -0
- package/work/src/documents/translate/xlsx.ts +137 -0
- package/work/src/documents/types.ts +188 -0
- package/work/src/index.ts +311 -0
- package/work/src/runtime/worker-pool-entry.ts +102 -0
- package/work/src/runtime/worker-pool.ts +267 -0
- package/work/src/sandbox/composition.ts +212 -0
- package/work/src/sandbox/image.ts +6 -0
- package/work/src/sandbox/index.ts +14 -0
- package/work/src/sheets/caps.ts +68 -0
- package/work/src/sheets/csv.ts +351 -0
- package/work/src/sheets/decimal.ts +388 -0
- package/work/src/sheets/errors.ts +52 -0
- package/work/src/sheets/index.ts +62 -0
- package/work/src/sheets/inference.ts +378 -0
- package/work/src/sheets/telemetry.ts +34 -0
- package/work/src/sheets/types.ts +162 -0
- package/work/src/sheets/xlsx.ts +388 -0
- package/work/src/skills/index.ts +8 -0
- package/work/src/skills/load.ts +43 -0
- package/work/src/tools/filesystem.ts +21 -0
- package/work/src/tools/index.ts +1 -0
- package/work/src/tools/local-filesystem.ts +33 -0
- package/work/src/tools/office.ts +311 -0
- package/work/vendor/hermes-agent/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
- package/work/vendor-lock.json +11 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared diff computation utilities for the edit and similar tools.
|
|
3
|
+
*
|
|
4
|
+
* Behavioral port of pi's core/tools/edit-diff for @arnilo/prism-tools/coding.
|
|
5
|
+
* The `diff` package provides unified-patch / display-diff generation.
|
|
6
|
+
* Exact matching, BOM/line-ending handling and the changed-line preview are
|
|
7
|
+
* dep-free.
|
|
8
|
+
*/
|
|
9
|
+
import * as Diff from "diff";
|
|
10
|
+
|
|
11
|
+
export interface Edit {
|
|
12
|
+
oldText: string;
|
|
13
|
+
newText: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function detectLineEnding(content: string): "\r\n" | "\n" {
|
|
17
|
+
const crlfIdx = content.indexOf("\r\n");
|
|
18
|
+
const lfIdx = content.indexOf("\n");
|
|
19
|
+
if (lfIdx === -1) return "\n";
|
|
20
|
+
if (crlfIdx === -1) return "\n";
|
|
21
|
+
return crlfIdx < lfIdx ? "\r\n" : "\n";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function normalizeToLF(text: string): string {
|
|
25
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function restoreLineEndings(text: string, ending: "\r\n" | "\n"): string {
|
|
29
|
+
return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface TextReplacement {
|
|
33
|
+
matchIndex: number;
|
|
34
|
+
matchLength: number;
|
|
35
|
+
newText: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Apply replacements right to left so earlier offsets stay valid. */
|
|
39
|
+
function applyReplacements(content: string, replacements: TextReplacement[]): string {
|
|
40
|
+
let result = content;
|
|
41
|
+
for (let i = replacements.length - 1; i >= 0; i--) {
|
|
42
|
+
const replacement = replacements[i];
|
|
43
|
+
result =
|
|
44
|
+
result.substring(0, replacement.matchIndex) +
|
|
45
|
+
replacement.newText +
|
|
46
|
+
result.substring(replacement.matchIndex + replacement.matchLength);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Strip UTF-8 BOM if present, return both the BOM (if any) and the text without it. */
|
|
52
|
+
export function stripBom(content: string): { bom: string; text: string } {
|
|
53
|
+
return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function countOccurrences(content: string, oldText: string): number {
|
|
57
|
+
let count = 0;
|
|
58
|
+
let index = content.indexOf(oldText);
|
|
59
|
+
while (index !== -1) {
|
|
60
|
+
count++;
|
|
61
|
+
index = content.indexOf(oldText, index + oldText.length);
|
|
62
|
+
}
|
|
63
|
+
return count;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const READ_LINE_PREFIX = /^\d+\t/;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Every non-empty line of `oldText` starts with `12\t`-style numbering: the model pasted `read`
|
|
70
|
+
* output instead of the file text. Reported, never stripped — a file line may really start that way.
|
|
71
|
+
*/
|
|
72
|
+
function looksReadPrefixed(oldText: string): boolean {
|
|
73
|
+
const lines = oldText.split("\n").filter((line) => line.length > 0);
|
|
74
|
+
return lines.length > 0 && lines.every((line) => READ_LINE_PREFIX.test(line));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getNotFoundError(path: string, content: string, oldText: string, editIndex: number, totalEdits: number): Error {
|
|
78
|
+
const base =
|
|
79
|
+
totalEdits === 1
|
|
80
|
+
? `Could not find the exact text in ${path}. The old text must match exactly including all whitespace and newlines.`
|
|
81
|
+
: `Could not find edits[${editIndex}] in ${path}. The oldText must match exactly including all whitespace and newlines.`;
|
|
82
|
+
if (looksReadPrefixed(oldText)) {
|
|
83
|
+
return new Error(`${base} oldText includes read line-number prefixes ("12\\t"); remove them.`);
|
|
84
|
+
}
|
|
85
|
+
// Cheap first-line substring scan: collect up to 3 unique nearby lines so the model can correct
|
|
86
|
+
// its oldText. No extra file read — `content` is the already-loaded target. Needle is clipped to
|
|
87
|
+
// 16 chars (min 4) to avoid noise from tiny/short oldText.
|
|
88
|
+
const firstLine = oldText
|
|
89
|
+
.split("\n")
|
|
90
|
+
.map((l) => l.trim())
|
|
91
|
+
.find((l) => l.length > 0);
|
|
92
|
+
if (!firstLine) return new Error(base);
|
|
93
|
+
const needle = firstLine.slice(0, 16);
|
|
94
|
+
if (needle.length < 4) return new Error(base);
|
|
95
|
+
const lines = content.split("\n");
|
|
96
|
+
const seen = new Set<string>();
|
|
97
|
+
const nearby: string[] = [];
|
|
98
|
+
for (let i = 0; i < lines.length && nearby.length < 3; i++) {
|
|
99
|
+
if (lines[i].includes(needle)) {
|
|
100
|
+
const snippet = lines[i].slice(0, 120);
|
|
101
|
+
if (seen.has(snippet)) continue;
|
|
102
|
+
seen.add(snippet);
|
|
103
|
+
nearby.push(` L${i + 1}: ${snippet}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (nearby.length === 0) return new Error(base);
|
|
107
|
+
return new Error(`${base}\nNearby:\n${nearby.join("\n")}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getDuplicateError(path: string, editIndex: number, totalEdits: number, occurrences: number): Error {
|
|
111
|
+
if (totalEdits === 1) {
|
|
112
|
+
return new Error(
|
|
113
|
+
`Found ${occurrences} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return new Error(
|
|
117
|
+
`Found ${occurrences} occurrences of edits[${editIndex}] in ${path}. Each oldText must be unique. Please provide more context to make it unique.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getEmptyOldTextError(path: string, editIndex: number, totalEdits: number): Error {
|
|
122
|
+
if (totalEdits === 1) {
|
|
123
|
+
return new Error(`oldText must not be empty in ${path}.`);
|
|
124
|
+
}
|
|
125
|
+
return new Error(`edits[${editIndex}].oldText must not be empty in ${path}.`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getNoChangeError(path: string, totalEdits: number): Error {
|
|
129
|
+
if (totalEdits === 1) {
|
|
130
|
+
return new Error(
|
|
131
|
+
`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return new Error(`No changes made to ${path}. The replacements produced identical content.`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface MatchedEdit {
|
|
138
|
+
editIndex: number;
|
|
139
|
+
matchIndex: number;
|
|
140
|
+
matchLength: number;
|
|
141
|
+
newText: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface AppliedEditsResult {
|
|
145
|
+
baseContent: string;
|
|
146
|
+
newContent: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Apply one or more exact-text replacements to LF-normalized content.
|
|
151
|
+
*
|
|
152
|
+
* All edits are matched against the same original content and must match
|
|
153
|
+
* exactly, unique, and disjoint; a miss throws with a Nearby hint. Replacements
|
|
154
|
+
* are applied in reverse order so offsets remain stable.
|
|
155
|
+
*/
|
|
156
|
+
export function applyEditsToNormalizedContent(normalizedContent: string, edits: Edit[], path: string): AppliedEditsResult {
|
|
157
|
+
const normalizedEdits = edits.map((edit) => ({
|
|
158
|
+
oldText: normalizeToLF(edit.oldText),
|
|
159
|
+
newText: normalizeToLF(edit.newText),
|
|
160
|
+
}));
|
|
161
|
+
for (let i = 0; i < normalizedEdits.length; i++) {
|
|
162
|
+
if (normalizedEdits[i].oldText.length === 0) {
|
|
163
|
+
throw getEmptyOldTextError(path, i, normalizedEdits.length);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const matchedEdits: MatchedEdit[] = [];
|
|
167
|
+
for (let i = 0; i < normalizedEdits.length; i++) {
|
|
168
|
+
const edit = normalizedEdits[i];
|
|
169
|
+
const matchIndex = normalizedContent.indexOf(edit.oldText);
|
|
170
|
+
if (matchIndex === -1) {
|
|
171
|
+
throw getNotFoundError(path, normalizedContent, edit.oldText, i, normalizedEdits.length);
|
|
172
|
+
}
|
|
173
|
+
const occurrences = countOccurrences(normalizedContent, edit.oldText);
|
|
174
|
+
if (occurrences > 1) {
|
|
175
|
+
throw getDuplicateError(path, i, normalizedEdits.length, occurrences);
|
|
176
|
+
}
|
|
177
|
+
matchedEdits.push({ editIndex: i, matchIndex, matchLength: edit.oldText.length, newText: edit.newText });
|
|
178
|
+
}
|
|
179
|
+
matchedEdits.sort((a, b) => a.matchIndex - b.matchIndex);
|
|
180
|
+
for (let i = 1; i < matchedEdits.length; i++) {
|
|
181
|
+
const previous = matchedEdits[i - 1];
|
|
182
|
+
const current = matchedEdits[i];
|
|
183
|
+
if (previous.matchIndex + previous.matchLength > current.matchIndex) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`edits[${previous.editIndex}] and edits[${current.editIndex}] overlap in ${path}. Merge them into one edit or target disjoint regions.`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const newContent = applyReplacements(normalizedContent, matchedEdits);
|
|
190
|
+
if (normalizedContent === newContent) {
|
|
191
|
+
throw getNoChangeError(path, normalizedEdits.length);
|
|
192
|
+
}
|
|
193
|
+
return { baseContent: normalizedContent, newContent };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Generate a standard unified patch. */
|
|
197
|
+
export function generateUnifiedPatch(path: string, oldContent: string, newContent: string, contextLines = 4): string {
|
|
198
|
+
return Diff.createTwoFilesPatch(path, path, oldContent, newContent, undefined, undefined, {
|
|
199
|
+
context: contextLines,
|
|
200
|
+
headerOptions: Diff.FILE_HEADERS_ONLY,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
interface DiffPart {
|
|
205
|
+
value: string;
|
|
206
|
+
added?: boolean;
|
|
207
|
+
removed?: boolean;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface EditDiffResult {
|
|
211
|
+
diff: string;
|
|
212
|
+
firstChangedLine: number | undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Generate a display-oriented diff string with line numbers and context.
|
|
217
|
+
* Returns both the diff string and the first changed line number (in the new file).
|
|
218
|
+
*/
|
|
219
|
+
export function generateDiffString(oldContent: string, newContent: string, contextLines = 4): EditDiffResult {
|
|
220
|
+
const parts = Diff.diffLines(oldContent, newContent) as DiffPart[];
|
|
221
|
+
const output: string[] = [];
|
|
222
|
+
const oldLines = oldContent.split("\n");
|
|
223
|
+
const newLines = newContent.split("\n");
|
|
224
|
+
const maxLineNum = Math.max(oldLines.length, newLines.length);
|
|
225
|
+
const lineNumWidth = String(maxLineNum).length;
|
|
226
|
+
let oldLineNum = 1;
|
|
227
|
+
let newLineNum = 1;
|
|
228
|
+
let lastWasChange = false;
|
|
229
|
+
let firstChangedLine: number | undefined;
|
|
230
|
+
for (let i = 0; i < parts.length; i++) {
|
|
231
|
+
const part = parts[i];
|
|
232
|
+
const raw = part.value.split("\n");
|
|
233
|
+
if (raw[raw.length - 1] === "") {
|
|
234
|
+
raw.pop();
|
|
235
|
+
}
|
|
236
|
+
if (part.added || part.removed) {
|
|
237
|
+
// Capture the first changed line (in the new file).
|
|
238
|
+
if (firstChangedLine === undefined) {
|
|
239
|
+
firstChangedLine = newLineNum;
|
|
240
|
+
}
|
|
241
|
+
for (const line of raw) {
|
|
242
|
+
if (part.added) {
|
|
243
|
+
const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
|
|
244
|
+
output.push(`+${lineNum} ${line}`);
|
|
245
|
+
newLineNum++;
|
|
246
|
+
} else {
|
|
247
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
248
|
+
output.push(`-${lineNum} ${line}`);
|
|
249
|
+
oldLineNum++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
lastWasChange = true;
|
|
253
|
+
} else {
|
|
254
|
+
// Context lines - only show a few before/after changes.
|
|
255
|
+
const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
|
|
256
|
+
const hasLeadingChange = lastWasChange;
|
|
257
|
+
const hasTrailingChange = nextPartIsChange;
|
|
258
|
+
if (hasLeadingChange && hasTrailingChange) {
|
|
259
|
+
if (raw.length <= contextLines * 2) {
|
|
260
|
+
for (const line of raw) {
|
|
261
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
262
|
+
output.push(` ${lineNum} ${line}`);
|
|
263
|
+
oldLineNum++;
|
|
264
|
+
newLineNum++;
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
const leadingLines = raw.slice(0, contextLines);
|
|
268
|
+
const trailingLines = raw.slice(raw.length - contextLines);
|
|
269
|
+
const skippedLines = raw.length - leadingLines.length - trailingLines.length;
|
|
270
|
+
for (const line of leadingLines) {
|
|
271
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
272
|
+
output.push(` ${lineNum} ${line}`);
|
|
273
|
+
oldLineNum++;
|
|
274
|
+
newLineNum++;
|
|
275
|
+
}
|
|
276
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
277
|
+
oldLineNum += skippedLines;
|
|
278
|
+
newLineNum += skippedLines;
|
|
279
|
+
for (const line of trailingLines) {
|
|
280
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
281
|
+
output.push(` ${lineNum} ${line}`);
|
|
282
|
+
oldLineNum++;
|
|
283
|
+
newLineNum++;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
} else if (hasLeadingChange) {
|
|
287
|
+
const shownLines = raw.slice(0, contextLines);
|
|
288
|
+
const skippedLines = raw.length - shownLines.length;
|
|
289
|
+
for (const line of shownLines) {
|
|
290
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
291
|
+
output.push(` ${lineNum} ${line}`);
|
|
292
|
+
oldLineNum++;
|
|
293
|
+
newLineNum++;
|
|
294
|
+
}
|
|
295
|
+
if (skippedLines > 0) {
|
|
296
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
297
|
+
oldLineNum += skippedLines;
|
|
298
|
+
newLineNum += skippedLines;
|
|
299
|
+
}
|
|
300
|
+
} else if (hasTrailingChange) {
|
|
301
|
+
const skippedLines = Math.max(0, raw.length - contextLines);
|
|
302
|
+
if (skippedLines > 0) {
|
|
303
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
304
|
+
oldLineNum += skippedLines;
|
|
305
|
+
newLineNum += skippedLines;
|
|
306
|
+
}
|
|
307
|
+
for (const line of raw.slice(skippedLines)) {
|
|
308
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
|
309
|
+
output.push(` ${lineNum} ${line}`);
|
|
310
|
+
oldLineNum++;
|
|
311
|
+
newLineNum++;
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
// Skip these context lines entirely.
|
|
315
|
+
oldLineNum += raw.length;
|
|
316
|
+
newLineNum += raw.length;
|
|
317
|
+
}
|
|
318
|
+
lastWasChange = false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return { diff: output.join("\n"), firstChangedLine };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Lines of `value` when split like the diff does (a trailing newline is not a line). */
|
|
325
|
+
function diffLineCount(value: string): number {
|
|
326
|
+
if (value.length === 0) return 0;
|
|
327
|
+
const lines = value.split("\n");
|
|
328
|
+
return lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export interface ChangedLinePreview {
|
|
332
|
+
/** `read`-style `N\ttext` lines around each changed region. */
|
|
333
|
+
text: string;
|
|
334
|
+
/** Changed lines left out by the cap. */
|
|
335
|
+
more: number;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Preview of the new file around its changed lines: `read`'s `N\ttext` format,
|
|
340
|
+
* `contextLines` lines either side of each region, at most `maxLines` lines
|
|
341
|
+
* total. `more` counts the changed lines the cap left out.
|
|
342
|
+
*/
|
|
343
|
+
export function generateChangedLinePreview(oldContent: string, newContent: string, contextLines = 2, maxLines = 40): ChangedLinePreview {
|
|
344
|
+
const parts = Diff.diffLines(oldContent, newContent) as DiffPart[];
|
|
345
|
+
const changed: number[] = [];
|
|
346
|
+
let newLineNumber = 1;
|
|
347
|
+
for (const part of parts) {
|
|
348
|
+
const count = diffLineCount(part.value);
|
|
349
|
+
if (part.added) {
|
|
350
|
+
for (let i = 0; i < count; i++) changed.push(newLineNumber + i);
|
|
351
|
+
newLineNumber += count;
|
|
352
|
+
} else if (!part.removed) {
|
|
353
|
+
newLineNumber += count;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (changed.length === 0) return { text: "", more: 0 };
|
|
357
|
+
const newLines = newContent.split("\n");
|
|
358
|
+
// A trailing newline is not an extra line (same rule as `read`).
|
|
359
|
+
const lastLine = newLines[newLines.length - 1] === "" ? newLines.length - 1 : newLines.length;
|
|
360
|
+
const windows: Array<[number, number]> = [];
|
|
361
|
+
for (const line of changed) {
|
|
362
|
+
const start = Math.max(1, line - contextLines);
|
|
363
|
+
const end = Math.min(lastLine, line + contextLines);
|
|
364
|
+
const last = windows[windows.length - 1];
|
|
365
|
+
if (last && start <= last[1] + 1) {
|
|
366
|
+
last[1] = Math.max(last[1], end);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
windows.push([start, end]);
|
|
370
|
+
}
|
|
371
|
+
const shown: string[] = [];
|
|
372
|
+
const shownChanged = new Set<number>();
|
|
373
|
+
for (const [start, end] of windows) {
|
|
374
|
+
for (let line = start; line <= end; line++) {
|
|
375
|
+
if (shown.length >= maxLines) break;
|
|
376
|
+
shown.push(`${line}\t${newLines[line - 1]}`);
|
|
377
|
+
if (changed.includes(line)) shownChanged.add(line);
|
|
378
|
+
}
|
|
379
|
+
if (shown.length >= maxLines) break;
|
|
380
|
+
}
|
|
381
|
+
return { text: shown.join("\n"), more: changed.length - shownChanged.size };
|
|
382
|
+
}
|