@cursor/july 0.1.103 → 0.1.105
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/dist/channels/change-monitors/change-monitors-channel.d.ts +5 -0
- package/dist/channels/change-monitors/change-monitors-channel.d.ts.map +1 -0
- package/dist/channels/change-monitors/change-monitors-channel.js +41 -0
- package/dist/channels/change-monitors/index.d.ts +4 -0
- package/dist/channels/change-monitors/index.d.ts.map +1 -0
- package/dist/channels/change-monitors/index.js +2 -0
- package/dist/channels/change-monitors/types.d.ts +39 -0
- package/dist/channels/change-monitors/types.d.ts.map +1 -0
- package/dist/channels/change-monitors/types.js +1 -0
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.js +1 -0
- package/dist/channels/deployments/types.d.ts +2 -0
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/github/defaults.d.ts.map +1 -1
- package/dist/channels/github/defaults.js +4 -0
- package/dist/channels/github/progress.d.ts +22 -0
- package/dist/channels/github/progress.d.ts.map +1 -1
- package/dist/channels/github/progress.js +32 -5
- package/dist/channels/issues/index.d.ts +4 -0
- package/dist/channels/issues/index.d.ts.map +1 -0
- package/dist/channels/issues/index.js +2 -0
- package/dist/channels/issues/issues-channel.d.ts +5 -0
- package/dist/channels/issues/issues-channel.d.ts.map +1 -0
- package/dist/channels/issues/issues-channel.js +37 -0
- package/dist/channels/issues/types.d.ts +33 -0
- package/dist/channels/issues/types.d.ts.map +1 -0
- package/dist/channels/issues/types.js +1 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DW9pXkpL.js → app.BVwaffdK.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.CJxMklAy.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.CdEIUwDg.js → VPLocalSearchBox.WuWTZ1x8.js} +1 -1
- package/dist/docs/assets/chunks/{theme.YZMHgPgg.js → theme.BYdI_6sF.js} +2 -2
- package/dist/docs/assets/{guides_github.md.DKYiT9do.js → guides_github.md.c0gdGJ-D.js} +1 -1
- package/dist/docs/assets/{guides_github.md.DKYiT9do.lean.js → guides_github.md.c0gdGJ-D.lean.js} +1 -1
- package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.js +9 -0
- package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.lean.js +1 -0
- package/dist/docs/assets/index.md.BW_6tOgR.js +5 -0
- package/dist/docs/assets/index.md.BW_6tOgR.lean.js +1 -0
- package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.js → reference_channels.md.DBGcaB7m.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.lean.js → reference_channels.md.DBGcaB7m.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.CJNc_xYK.js → reference_cli.md.C6Lo8SEd.js} +2 -1
- package/dist/docs/assets/{reference_extensions.md.DykHW3qL.js → reference_extensions.md.CGmMLblt.js} +1 -1
- package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.js +3 -0
- package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.lean.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/design/filesystem.md +376 -0
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-agents.html +5 -5
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +6 -6
- package/dist/docs/guides/github.md +5 -0
- package/dist/docs/guides/grokbot-agents.html +35 -0
- package/dist/docs/guides/grokbot-agents.md +88 -0
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +5 -5
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +6 -1
- package/dist/docs/llms-full.txt +562 -2
- package/dist/docs/llms.txt +3 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +6 -6
- package/dist/docs/reference/channels.md +20 -0
- package/dist/docs/reference/cli.html +7 -6
- package/dist/docs/reference/cli.md +2 -1
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/extensions.html +6 -6
- package/dist/docs/reference/extensions.md +2 -0
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/result.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/skills/ab.html +4 -4
- package/dist/docs/skills/create-agent.html +4 -4
- package/dist/docs/skills/debug.html +4 -4
- package/dist/docs/skills/evals.html +4 -4
- package/dist/docs/skills/framework-map.html +4 -4
- package/dist/docs/skills/github.html +4 -4
- package/dist/docs/skills/hillclimb.html +4 -4
- package/dist/docs/skills/index.html +4 -4
- package/dist/docs/skills/mcp-auth.html +4 -4
- package/dist/docs/skills/otel.html +4 -4
- package/dist/docs/skills/setup-slack.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +4 -4
- package/dist/docs/templates/code-wiki.html +5 -5
- package/dist/docs/templates/demo.html +5 -5
- package/dist/docs/templates/grokbot-agents.html +29 -0
- package/dist/docs/templates/grokbot-agents.md +48 -0
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/files.d.ts +7 -2
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +7 -2
- package/dist/filesystem.d.ts +440 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +1694 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/continuation-channel.d.ts +4 -2
- package/dist/internal/continuation-channel.d.ts.map +1 -1
- package/dist/internal/continuation-channel.js +21 -12
- package/dist/internal/continuation-identity.d.ts +13 -5
- package/dist/internal/continuation-identity.d.ts.map +1 -1
- package/dist/internal/continuation-identity.js +74 -35
- package/dist/internal/cursor-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +1 -0
- package/dist/internal/discovery/agent.d.ts.map +1 -1
- package/dist/internal/discovery/agent.js +80 -8
- package/dist/internal/discovery/info.d.ts.map +1 -1
- package/dist/internal/discovery/info.js +3 -0
- package/dist/internal/factory-resource-channel.d.ts +33 -0
- package/dist/internal/factory-resource-channel.d.ts.map +1 -0
- package/dist/internal/factory-resource-channel.js +77 -0
- package/dist/internal/filesystem/errors.d.ts +64 -0
- package/dist/internal/filesystem/errors.d.ts.map +1 -0
- package/dist/internal/filesystem/errors.js +175 -0
- package/dist/internal/filesystem/format.d.ts +28 -0
- package/dist/internal/filesystem/format.d.ts.map +1 -0
- package/dist/internal/filesystem/format.js +213 -0
- package/dist/internal/filesystem/merge.d.ts +34 -0
- package/dist/internal/filesystem/merge.d.ts.map +1 -0
- package/dist/internal/filesystem/merge.js +196 -0
- package/dist/internal/filesystem/scratch.d.ts +59 -0
- package/dist/internal/filesystem/scratch.d.ts.map +1 -0
- package/dist/internal/filesystem/scratch.js +212 -0
- package/dist/internal/filesystem/sdk-schemas.d.ts +29 -0
- package/dist/internal/filesystem/sdk-schemas.d.ts.map +1 -0
- package/dist/internal/filesystem/sdk-schemas.js +153 -0
- package/dist/internal/filesystem/tools.d.ts +42 -0
- package/dist/internal/filesystem/tools.d.ts.map +1 -0
- package/dist/internal/filesystem/tools.js +618 -0
- package/dist/internal/filesystem/walk.d.ts +74 -0
- package/dist/internal/filesystem/walk.d.ts.map +1 -0
- package/dist/internal/filesystem/walk.js +339 -0
- package/dist/internal/framework-file-storage.d.ts +3 -1
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +3 -1
- package/dist/internal/guest-network.d.ts +5 -2
- package/dist/internal/guest-network.d.ts.map +1 -1
- package/dist/internal/guest-network.js +7 -2
- package/dist/internal/hosted-delivery.d.ts +7 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +41 -3
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -1
- package/dist/internal/hosted-managed-automation.js +27 -2
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +4 -1
- package/dist/internal/sdk-runner.d.ts +13 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +21 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +11 -3
- package/dist/internal/session-engine.d.ts +30 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +484 -82
- package/dist/internal/store-mirror.d.ts +2 -2
- package/dist/internal/store-mirror.js +2 -2
- package/dist/internal/tenant-slack/relay.d.ts +2 -0
- package/dist/internal/tenant-slack/relay.d.ts.map +1 -1
- package/dist/internal/tenant-slack/relay.js +18 -8
- package/dist/internal/testing/factory-resource-fixtures.d.ts +48 -0
- package/dist/internal/testing/factory-resource-fixtures.d.ts.map +1 -0
- package/dist/internal/testing/factory-resource-fixtures.js +41 -0
- package/dist/internal/workspace-sync.d.ts +31 -6
- package/dist/internal/workspace-sync.d.ts.map +1 -1
- package/dist/internal/workspace-sync.js +67 -12
- package/dist/internal/workspace.d.ts +9 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +16 -0
- package/dist/playground/assets/{index-59KvgREd.js → index-BaQzjaNK.js} +40 -40
- package/dist/playground/assets/index-CceqnLTX.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +60 -11
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +6 -1
- package/docs/design/filesystem.md +376 -0
- package/docs/guides/github.md +5 -0
- package/docs/guides/grokbot-agents.md +93 -0
- package/docs/reference/channels.md +20 -0
- package/docs/reference/cli.md +2 -1
- package/docs/reference/extensions.md +2 -0
- package/docs/templates/grokbot-agents.md +53 -0
- package/package.json +25 -2
- package/src/channels/change-monitors/change-monitors-channel.ts +62 -0
- package/src/channels/change-monitors/index.ts +8 -0
- package/src/channels/change-monitors/types.ts +44 -0
- package/src/channels/deployments/deployments-channel.ts +1 -0
- package/src/channels/deployments/types.ts +2 -0
- package/src/channels/github/defaults.ts +4 -0
- package/src/channels/github/progress.ts +76 -10
- package/src/channels/issues/index.ts +9 -0
- package/src/channels/issues/issues-channel.ts +51 -0
- package/src/channels/issues/types.ts +41 -0
- package/src/files.ts +9 -2
- package/src/filesystem.ts +2074 -0
- package/src/index.ts +28 -0
- package/src/internal/continuation-channel.ts +29 -12
- package/src/internal/continuation-identity.ts +122 -37
- package/src/internal/cursor-agent-template.ts +1 -0
- package/src/internal/discovery/agent.ts +87 -1
- package/src/internal/discovery/info.ts +3 -0
- package/src/internal/factory-resource-channel.ts +122 -0
- package/src/internal/filesystem/errors.ts +220 -0
- package/src/internal/filesystem/format.ts +276 -0
- package/src/internal/filesystem/merge.ts +195 -0
- package/src/internal/filesystem/scratch.ts +255 -0
- package/src/internal/filesystem/sdk-schemas.ts +204 -0
- package/src/internal/filesystem/tools.ts +821 -0
- package/src/internal/filesystem/walk.ts +369 -0
- package/src/internal/framework-file-storage.ts +3 -1
- package/src/internal/guest-network.ts +8 -2
- package/src/internal/hosted-delivery.ts +71 -2
- package/src/internal/hosted-managed-automation.ts +49 -5
- package/src/internal/init-project.ts +4 -1
- package/src/internal/sdk-runner.ts +23 -0
- package/src/internal/server.ts +16 -0
- package/src/internal/session-engine.ts +595 -73
- package/src/internal/store-mirror.ts +2 -2
- package/src/internal/tenant-slack/relay.ts +28 -4
- package/src/internal/testing/factory-resource-fixtures.ts +85 -0
- package/src/internal/workspace-sync.ts +84 -12
- package/src/internal/workspace.ts +20 -0
- package/src/types.ts +58 -11
- package/templates/grokbot-agents/README.md +26 -0
- package/templates/grokbot-agents/agent/agent.ts +6 -0
- package/templates/grokbot-agents/agent/extensions/grokbot.ts +5 -0
- package/templates/grokbot-agents/agent/instructions.md +7 -0
- package/templates/grokbot-agents/evals/evals.config.ts +5 -0
- package/templates/grokbot-agents/evals/grokbot-agents.eval.ts +18 -0
- package/templates/grokbot-agents/init.json +18 -0
- package/templates/grokbot-agents/package.json +17 -0
- package/templates/grokbot-agents/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.nrl1ifBq.js +0 -1
- package/dist/docs/assets/index.md.D75GWhPl.js +0 -5
- package/dist/docs/assets/index.md.D75GWhPl.lean.js +0 -1
- package/dist/playground/assets/index-MbMxXg-y.css +0 -1
- /package/dist/docs/assets/{reference_cli.md.CJNc_xYK.lean.js → reference_cli.md.C6Lo8SEd.lean.js} +0 -0
- /package/dist/docs/assets/{reference_extensions.md.DykHW3qL.lean.js → reference_extensions.md.CGmMLblt.lean.js} +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed FileSystem throws. Every method error is a {@link PathError}
|
|
3
|
+
* so composers (`unionFs`) can rewrite `path` without scraping
|
|
4
|
+
* `error.message`.
|
|
5
|
+
*
|
|
6
|
+
* Messages stay the FileSystem contract strings. `path` is the
|
|
7
|
+
* absolute posix path the error is about (child-root on a mount,
|
|
8
|
+
* namespace path after `unionFs` remaps it).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
|
|
13
|
+
function normalizeErrorPath(input: string): string {
|
|
14
|
+
const slashes = input.replace(/\\/g, "/");
|
|
15
|
+
if (slashes === "") {
|
|
16
|
+
return "/";
|
|
17
|
+
}
|
|
18
|
+
const withSlash = slashes.startsWith("/") ? slashes : `/${slashes}`;
|
|
19
|
+
const resolved = path.posix.normalize(withSlash);
|
|
20
|
+
if (resolved.length > 1 && resolved.endsWith("/")) {
|
|
21
|
+
return resolved.slice(0, -1);
|
|
22
|
+
}
|
|
23
|
+
return resolved === "." || resolved === "" ? "/" : resolved;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ROOT_MESSAGE_PREFIX =
|
|
27
|
+
/^(not found|read-only mount|write failed|edit failed|no mount for path|delete is not supported): \/$/;
|
|
28
|
+
|
|
29
|
+
function rewritePathInMessage(
|
|
30
|
+
message: string,
|
|
31
|
+
from: string,
|
|
32
|
+
to: string
|
|
33
|
+
): string {
|
|
34
|
+
if (from === to) {
|
|
35
|
+
return message;
|
|
36
|
+
}
|
|
37
|
+
if (from === "/") {
|
|
38
|
+
return ROOT_MESSAGE_PREFIX.test(message)
|
|
39
|
+
? message.replace(/\/$/, to)
|
|
40
|
+
: message;
|
|
41
|
+
}
|
|
42
|
+
return message.includes(from) ? message.split(from).join(to) : message;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Base of every FileSystem method throw. Config / factory errors
|
|
47
|
+
* (`FileSystemConfigError`, empty `overlayFs`) are not PathErrors.
|
|
48
|
+
*/
|
|
49
|
+
export class PathError extends Error {
|
|
50
|
+
readonly path: string;
|
|
51
|
+
|
|
52
|
+
constructor(path: string, message: string) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = new.target.name;
|
|
55
|
+
this.path = normalizeErrorPath(path);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Same error at a rewritten path. `unionFs` prepends the mount
|
|
60
|
+
* prefix this way. Subclass identity and extra fields stay.
|
|
61
|
+
*/
|
|
62
|
+
withPath(path: string): this {
|
|
63
|
+
const next = normalizeErrorPath(path);
|
|
64
|
+
if (next === this.path) {
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
return this.recreate(
|
|
68
|
+
next,
|
|
69
|
+
rewritePathInMessage(this.message, this.path, next)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected recreate(path: string, message: string): this {
|
|
74
|
+
return new PathError(path, message) as this;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class FileNotFoundError extends PathError {
|
|
79
|
+
constructor(path: string, message?: string) {
|
|
80
|
+
super(path, message ?? `not found: ${normalizeErrorPath(path)}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected override recreate(path: string, message: string): this {
|
|
84
|
+
return new FileNotFoundError(path, message) as this;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class ReadOnlyMountError extends PathError {
|
|
89
|
+
constructor(path: string, message?: string) {
|
|
90
|
+
super(path, message ?? `read-only mount: ${normalizeErrorPath(path)}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected override recreate(path: string, message: string): this {
|
|
94
|
+
return new ReadOnlyMountError(path, message) as this;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export class NoMountError extends PathError {
|
|
99
|
+
constructor(path: string, message?: string) {
|
|
100
|
+
super(path, message ?? `no mount for path: ${normalizeErrorPath(path)}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected override recreate(path: string, message: string): this {
|
|
104
|
+
return new NoMountError(path, message) as this;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class WriteFailedError extends PathError {
|
|
109
|
+
constructor(path: string, message?: string) {
|
|
110
|
+
super(path, message ?? `write failed: ${normalizeErrorPath(path)}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
protected override recreate(path: string, message: string): this {
|
|
114
|
+
return new WriteFailedError(path, message) as this;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export class DeleteNotSupportedError extends PathError {
|
|
119
|
+
constructor(path: string, message?: string) {
|
|
120
|
+
super(
|
|
121
|
+
path,
|
|
122
|
+
message ?? `delete is not supported: ${normalizeErrorPath(path)}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected override recreate(path: string, message: string): this {
|
|
127
|
+
return new DeleteNotSupportedError(path, message) as this;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Exact-edit contract failures (`oldText is empty`, uniqueness).
|
|
133
|
+
* `message` is the contract string; it does not embed `path`.
|
|
134
|
+
*/
|
|
135
|
+
export class EditError extends PathError {
|
|
136
|
+
protected override recreate(path: string, message: string): this {
|
|
137
|
+
return new EditError(path, message) as this;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export class InvalidGrepPatternError extends PathError {
|
|
142
|
+
readonly pattern: string;
|
|
143
|
+
|
|
144
|
+
constructor(pattern: string, path = "/", message?: string) {
|
|
145
|
+
super(path, message ?? `invalid grep pattern: ${pattern}`);
|
|
146
|
+
this.pattern = pattern;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected override recreate(path: string, message: string): this {
|
|
150
|
+
return new InvalidGrepPatternError(this.pattern, path, message) as this;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function isFileNotFoundError(error: unknown): boolean {
|
|
155
|
+
return (
|
|
156
|
+
error instanceof FileNotFoundError ||
|
|
157
|
+
(error instanceof Error && error.message.startsWith("not found:"))
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function isInvalidGrepPatternError(error: unknown): boolean {
|
|
162
|
+
return (
|
|
163
|
+
error instanceof InvalidGrepPatternError ||
|
|
164
|
+
(error instanceof Error &&
|
|
165
|
+
error.message.startsWith("invalid grep pattern:"))
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const CONTRACT_PATH_LABELS = [
|
|
170
|
+
"not found: ",
|
|
171
|
+
"read-only mount: ",
|
|
172
|
+
"write failed: ",
|
|
173
|
+
"edit failed: ",
|
|
174
|
+
"no mount for path: ",
|
|
175
|
+
"delete is not supported: ",
|
|
176
|
+
] as const;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Remap a child throw onto a mount prefix. Typed {@link PathError}
|
|
180
|
+
* keeps its class and rewrites `path`. Untyped `Error` still gets
|
|
181
|
+
* a best-effort message rewrite so older custom trees keep working.
|
|
182
|
+
*/
|
|
183
|
+
export function remapPathError(error: unknown, prefix: string): Error {
|
|
184
|
+
if (error instanceof PathError) {
|
|
185
|
+
return error.withPath(joinMountPath(error.path, prefix));
|
|
186
|
+
}
|
|
187
|
+
if (!(error instanceof Error)) {
|
|
188
|
+
return new PathError(prefix, String(error));
|
|
189
|
+
}
|
|
190
|
+
return rewriteUntypedMountError(error, prefix);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function joinMountPath(childPath: string, prefix: string): string {
|
|
194
|
+
const mount = normalizeErrorPath(prefix);
|
|
195
|
+
const child = normalizeErrorPath(childPath);
|
|
196
|
+
if (mount === "/") {
|
|
197
|
+
return child;
|
|
198
|
+
}
|
|
199
|
+
if (child === "/") {
|
|
200
|
+
return mount;
|
|
201
|
+
}
|
|
202
|
+
return `${mount}${child}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function rewriteUntypedMountError(error: Error, prefix: string): Error {
|
|
206
|
+
for (const label of CONTRACT_PATH_LABELS) {
|
|
207
|
+
if (!error.message.startsWith(label)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const childPath = error.message.slice(label.length);
|
|
211
|
+
const next = `${label}${joinMountPath(childPath, prefix)}`;
|
|
212
|
+
if (next === error.message) {
|
|
213
|
+
return error;
|
|
214
|
+
}
|
|
215
|
+
const rewritten = new Error(next);
|
|
216
|
+
rewritten.name = error.name;
|
|
217
|
+
return rewritten;
|
|
218
|
+
}
|
|
219
|
+
return error;
|
|
220
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing strings that match Cursor SDK native tool output
|
|
3
|
+
* (LocalLsExecutor / read / grep / glob formatters).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { posix } from "node:path";
|
|
7
|
+
|
|
8
|
+
import type { GrepUnionResult } from "../../filesystem.js";
|
|
9
|
+
import type { LsDirectoryTreeNode } from "./walk.js";
|
|
10
|
+
|
|
11
|
+
const LS_RENDER_CHARACTER_BUDGET = 10_000;
|
|
12
|
+
const GLOB_TOTAL_CHARACTER_BUDGET = 5_000;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Number already-sliced Read content. `FileSystem.read` applies offset/
|
|
16
|
+
* limit; do not window again or line numbers restart at 1.
|
|
17
|
+
*/
|
|
18
|
+
export function slicedReadStartLine(
|
|
19
|
+
totalLines: number,
|
|
20
|
+
offset?: number
|
|
21
|
+
): number {
|
|
22
|
+
if (offset === undefined) {
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
if (offset < 0) {
|
|
26
|
+
return Math.max(1, totalLines + offset + 1);
|
|
27
|
+
}
|
|
28
|
+
return Math.max(1, offset);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function formatNumberedLines(
|
|
32
|
+
content: string,
|
|
33
|
+
options: {
|
|
34
|
+
offset?: number;
|
|
35
|
+
limit?: number;
|
|
36
|
+
/** When set, `content` is already the window; number from this line. */
|
|
37
|
+
startLine?: number;
|
|
38
|
+
totalLines?: number;
|
|
39
|
+
} = {}
|
|
40
|
+
): string {
|
|
41
|
+
const lines = content === "" ? [] : content.split("\n");
|
|
42
|
+
if (lines.length === 0) {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
const { offset, limit, startLine } = options;
|
|
46
|
+
let start = 1;
|
|
47
|
+
let window = lines;
|
|
48
|
+
const total = options.totalLines ?? lines.length;
|
|
49
|
+
if (startLine !== undefined) {
|
|
50
|
+
start = startLine;
|
|
51
|
+
} else if (offset !== undefined || limit !== undefined) {
|
|
52
|
+
const effectiveOffset = offset ?? 1;
|
|
53
|
+
const effectiveLimit =
|
|
54
|
+
limit ?? (effectiveOffset < 0 ? Math.abs(effectiveOffset) : total);
|
|
55
|
+
start =
|
|
56
|
+
effectiveOffset < 0
|
|
57
|
+
? Math.max(1, total + effectiveOffset + 1)
|
|
58
|
+
: Math.max(1, effectiveOffset);
|
|
59
|
+
const end = Math.min(total, start + effectiveLimit - 1);
|
|
60
|
+
window = lines.slice(start - 1, end);
|
|
61
|
+
}
|
|
62
|
+
return renderNumberedWindow(window, start, total);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function renderNumberedWindow(
|
|
66
|
+
window: readonly string[],
|
|
67
|
+
start: number,
|
|
68
|
+
total: number
|
|
69
|
+
): string {
|
|
70
|
+
const numbered = window.map(
|
|
71
|
+
(line, index) => `${String(start + index).padStart(6, " ")}|${line}`
|
|
72
|
+
);
|
|
73
|
+
let result = numbered.join("\n");
|
|
74
|
+
if (start > 1) {
|
|
75
|
+
result = `... ${start - 1} ${start - 1 === 1 ? "line" : "lines"} not shown ...\n${result}`;
|
|
76
|
+
}
|
|
77
|
+
const end = start + window.length - 1;
|
|
78
|
+
if (end < total) {
|
|
79
|
+
result += `\n... ${total - end} ${total - end === 1 ? "line" : "lines"} not shown ...`;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function formatLsTree(result: LsDirectoryTreeNode): string {
|
|
85
|
+
if (
|
|
86
|
+
result.childrenWereProcessed &&
|
|
87
|
+
result.childrenDirs.length === 0 &&
|
|
88
|
+
result.childrenFiles.length === 0
|
|
89
|
+
) {
|
|
90
|
+
const trailingSlash = result.absPath.endsWith("/") ? "" : "/";
|
|
91
|
+
return `${result.absPath}${trailingSlash}\n... no children found ...`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const characterBudget = LS_RENDER_CHARACTER_BUDGET;
|
|
95
|
+
let toolResponse = renderDirectoryTree(result).result;
|
|
96
|
+
if (toolResponse.length > characterBudget) {
|
|
97
|
+
toolResponse = renderDirectoryTree(result, true, 0).result;
|
|
98
|
+
if (toolResponse.length > characterBudget) {
|
|
99
|
+
toolResponse = renderDirectoryTree(result, false, 0).result;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const notes: string[] = [];
|
|
104
|
+
if (!result.childrenWereProcessed || toolResponse.length > characterBudget) {
|
|
105
|
+
if (toolResponse.length > characterBudget) {
|
|
106
|
+
toolResponse = toolResponse.slice(0, characterBudget);
|
|
107
|
+
const lastNewlineIndex = toolResponse.lastIndexOf("\n");
|
|
108
|
+
if (lastNewlineIndex > 0) {
|
|
109
|
+
toolResponse = toolResponse.slice(0, lastNewlineIndex + 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
toolResponse += " ...\n";
|
|
113
|
+
notes.push(
|
|
114
|
+
`target_directory is too large (either too many children or their names are too long).` +
|
|
115
|
+
` Immediate children were truncated. Try doing more specific search or use the script tool.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (notes.length === 1) {
|
|
119
|
+
toolResponse += `\nNote: ${notes[0]}`;
|
|
120
|
+
}
|
|
121
|
+
return toolResponse;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function renderDirectoryTree(
|
|
125
|
+
rootDirectory: LsDirectoryTreeNode,
|
|
126
|
+
renderExtensionCounts = true,
|
|
127
|
+
renderDepthLimit?: number
|
|
128
|
+
): { result: string } {
|
|
129
|
+
function render(dir: LsDirectoryTreeNode, depth: number): string {
|
|
130
|
+
const indent = " ".repeat(depth);
|
|
131
|
+
let result: string;
|
|
132
|
+
if (depth === 0) {
|
|
133
|
+
const trailingSep = dir.absPath.endsWith("/") ? "" : "/";
|
|
134
|
+
result = `${dir.absPath}${trailingSep}\n`;
|
|
135
|
+
} else {
|
|
136
|
+
result = `${indent}- ${posix.basename(dir.absPath)}/\n`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
type LsChild =
|
|
140
|
+
| { type: "file"; name: string }
|
|
141
|
+
| { type: "dir"; name: string; dir: LsDirectoryTreeNode };
|
|
142
|
+
const allChildren: LsChild[] = [
|
|
143
|
+
...dir.childrenFiles.map(
|
|
144
|
+
(file): LsChild => ({ type: "file", name: file.name })
|
|
145
|
+
),
|
|
146
|
+
...dir.childrenDirs.map(
|
|
147
|
+
(childDir): LsChild => ({
|
|
148
|
+
type: "dir",
|
|
149
|
+
name: posix.basename(childDir.absPath),
|
|
150
|
+
dir: childDir,
|
|
151
|
+
})
|
|
152
|
+
),
|
|
153
|
+
];
|
|
154
|
+
allChildren.sort((left, right) => left.name.localeCompare(right.name));
|
|
155
|
+
|
|
156
|
+
const childIndent = " ".repeat(depth + 1);
|
|
157
|
+
for (const child of allChildren) {
|
|
158
|
+
if (child.type === "file") {
|
|
159
|
+
result += `${childIndent}- ${child.name}\n`;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const childDir = child.dir;
|
|
163
|
+
if (
|
|
164
|
+
childDir.childrenWereProcessed &&
|
|
165
|
+
(renderDepthLimit === undefined || depth < renderDepthLimit)
|
|
166
|
+
) {
|
|
167
|
+
result += render(childDir, depth + 1);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const extensionCounts = Object.entries(
|
|
171
|
+
childDir.fullSubtreeExtensionCounts
|
|
172
|
+
);
|
|
173
|
+
if (extensionCounts.length > 0 && renderExtensionCounts) {
|
|
174
|
+
const sorted = [...extensionCounts].sort(
|
|
175
|
+
([, countA], [, countB]) => countB - countA
|
|
176
|
+
);
|
|
177
|
+
const shown = sorted.slice(0, 3);
|
|
178
|
+
const summary = shown
|
|
179
|
+
.map(([ext, count]) => `${count} *${ext === "" ? "" : ext}`)
|
|
180
|
+
.join(", ");
|
|
181
|
+
result += `${childIndent}- ${child.name}/ (${childDir.numFiles} files: ${summary})\n`;
|
|
182
|
+
} else {
|
|
183
|
+
result += `${childIndent}- ${child.name}/\n`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return { result: render(rootDirectory, 0) };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function formatGlobOutput(
|
|
193
|
+
dir: string,
|
|
194
|
+
files: readonly string[]
|
|
195
|
+
): string {
|
|
196
|
+
const normalizedRoot = dir.replace(/\/+$/, "") || "/";
|
|
197
|
+
const prefix = normalizedRoot === "/" ? "/" : `${normalizedRoot}/`;
|
|
198
|
+
const relative = files.map((filePath) =>
|
|
199
|
+
filePath.startsWith(prefix) ? filePath.slice(prefix.length) : filePath
|
|
200
|
+
);
|
|
201
|
+
if (relative.length === 0) {
|
|
202
|
+
return `Result of search in '${dir}': 0 files found\n`;
|
|
203
|
+
}
|
|
204
|
+
let output = `Result of search in '${dir}' (total ${relative.length} file${relative.length === 1 ? "" : "s"}):\n`;
|
|
205
|
+
for (let i = 0; i < relative.length; i++) {
|
|
206
|
+
const file = relative[i] ?? "";
|
|
207
|
+
if (output.length + file.length > GLOB_TOTAL_CHARACTER_BUDGET) {
|
|
208
|
+
output += `... ${relative.length - i} more files ... (Do a more specific search if needed)\n`;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
output += `- ${file}\n`;
|
|
212
|
+
}
|
|
213
|
+
return output;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function formatGrepOutput(union: GrepUnionResult | undefined): string {
|
|
217
|
+
if (union === undefined) {
|
|
218
|
+
return "No matches found";
|
|
219
|
+
}
|
|
220
|
+
if (union.type === "files") {
|
|
221
|
+
if (union.output.files.length === 0) {
|
|
222
|
+
return "No files with matches found";
|
|
223
|
+
}
|
|
224
|
+
return union.output.files.join("\n");
|
|
225
|
+
}
|
|
226
|
+
if (union.type === "count") {
|
|
227
|
+
if (union.output.counts.length === 0) {
|
|
228
|
+
return "No matches found";
|
|
229
|
+
}
|
|
230
|
+
return union.output.counts
|
|
231
|
+
.map((row) => `${row.file}:${row.count}`)
|
|
232
|
+
.join("\n");
|
|
233
|
+
}
|
|
234
|
+
if (union.output.matches.length === 0) {
|
|
235
|
+
return "No matches found";
|
|
236
|
+
}
|
|
237
|
+
const lines: string[] = [];
|
|
238
|
+
let currentFile: string | undefined;
|
|
239
|
+
for (const match of union.output.matches) {
|
|
240
|
+
if (match.file !== currentFile) {
|
|
241
|
+
if (currentFile !== undefined) {
|
|
242
|
+
lines.push("");
|
|
243
|
+
}
|
|
244
|
+
currentFile = match.file;
|
|
245
|
+
lines.push(match.file);
|
|
246
|
+
}
|
|
247
|
+
const before = match.beforeContext ?? [];
|
|
248
|
+
const matchLine = match.lineNumber ?? 1;
|
|
249
|
+
before.forEach((line, index) => {
|
|
250
|
+
lines.push(` ${matchLine - before.length + index}-${line}`);
|
|
251
|
+
});
|
|
252
|
+
lines.push(` ${match.lineNumber ?? ""}:${match.line}`);
|
|
253
|
+
(match.afterContext ?? []).forEach((line, index) => {
|
|
254
|
+
lines.push(` ${matchLine + 1 + index}-${line}`);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return lines.join("\n");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function formatWriteOutput(path: string): string {
|
|
261
|
+
return `Wrote contents to ${path}`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function formatStrReplaceOutput(args: {
|
|
265
|
+
path: string;
|
|
266
|
+
replaceAll: boolean;
|
|
267
|
+
occurrencesModified: number;
|
|
268
|
+
}): string {
|
|
269
|
+
if (!args.replaceAll) {
|
|
270
|
+
return `The file ${args.path} has been updated.`;
|
|
271
|
+
}
|
|
272
|
+
const occurrenceLabel =
|
|
273
|
+
args.occurrencesModified === 1 ? "occurrence" : "occurrences";
|
|
274
|
+
const verb = args.occurrencesModified === 1 ? "has" : "have";
|
|
275
|
+
return `${args.occurrencesModified} ${occurrenceLabel} in file ${args.path} ${verb} been updated.`;
|
|
276
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* K-way merge of already-sorted streams. Each source must be ordered
|
|
3
|
+
* under the same `compare`; the merge does not re-sort a source.
|
|
4
|
+
*
|
|
5
|
+
* A binary heap of heads keeps the next yield O(log k) in the number of
|
|
6
|
+
* sources. `unique` skips later copies of a key so an earlier source can
|
|
7
|
+
* shadow a later one (the union rule).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface MergeSortedOptions<T> {
|
|
11
|
+
compare: (left: T, right: T) => number;
|
|
12
|
+
unique?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type Head<T> = {
|
|
16
|
+
value: T;
|
|
17
|
+
iterator: AsyncIterator<T>;
|
|
18
|
+
source: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Merge already-sorted iterables / async iterables under `compare`.
|
|
23
|
+
* Equal keys (`compare === 0`) keep source order: earlier `sources`
|
|
24
|
+
* index wins. With `unique`, those later copies are skipped.
|
|
25
|
+
*/
|
|
26
|
+
export async function* mergeSorted<T>(
|
|
27
|
+
sources: Array<Iterable<T> | AsyncIterable<T>>,
|
|
28
|
+
options: MergeSortedOptions<T>
|
|
29
|
+
): AsyncGenerator<T> {
|
|
30
|
+
const { compare, unique = false } = options;
|
|
31
|
+
const heap = new MinHeap<Head<T>>((left, right) => {
|
|
32
|
+
const byValue = compare(left.value, right.value);
|
|
33
|
+
return byValue !== 0 ? byValue : left.source - right.source;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const iterators = sources.map((source, sourceIndex) => {
|
|
37
|
+
const iterator =
|
|
38
|
+
Symbol.asyncIterator in source
|
|
39
|
+
? source[Symbol.asyncIterator]()
|
|
40
|
+
: (async function* () {
|
|
41
|
+
yield* source;
|
|
42
|
+
})();
|
|
43
|
+
return { iterator, sourceIndex };
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
await Promise.all(
|
|
47
|
+
iterators.map(async ({ iterator, sourceIndex }) => {
|
|
48
|
+
const next = await iterator.next();
|
|
49
|
+
if (!next.done) {
|
|
50
|
+
heap.push({ value: next.value, iterator, source: sourceIndex });
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
while (heap.size > 0) {
|
|
56
|
+
const best = heap.pop();
|
|
57
|
+
if (best === undefined) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
yield best.value;
|
|
61
|
+
if (unique) {
|
|
62
|
+
await advanceMatching(heap, best, compare);
|
|
63
|
+
} else {
|
|
64
|
+
await pushNext(heap, best);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function pushNext<T>(
|
|
70
|
+
heap: MinHeap<Head<T>>,
|
|
71
|
+
head: Head<T>
|
|
72
|
+
): Promise<void> {
|
|
73
|
+
const next = await head.iterator.next();
|
|
74
|
+
if (!next.done) {
|
|
75
|
+
heap.push({
|
|
76
|
+
value: next.value,
|
|
77
|
+
iterator: head.iterator,
|
|
78
|
+
source: head.source,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Advance every head whose key equals `best` (compare === 0). */
|
|
84
|
+
async function advanceMatching<T>(
|
|
85
|
+
heap: MinHeap<Head<T>>,
|
|
86
|
+
best: Head<T>,
|
|
87
|
+
compare: (left: T, right: T) => number
|
|
88
|
+
): Promise<void> {
|
|
89
|
+
await pushNext(heap, best);
|
|
90
|
+
while (heap.size > 0) {
|
|
91
|
+
const peek = heap.peek();
|
|
92
|
+
if (peek === undefined || compare(peek.value, best.value) !== 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const duplicate = heap.pop();
|
|
96
|
+
if (duplicate === undefined) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
await pushNext(heap, duplicate);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Binary min-heap. `compare(a, b) < 0` means `a` is before `b`.
|
|
105
|
+
* Exported for the invariant tests; composers use {@link mergeSorted}.
|
|
106
|
+
*/
|
|
107
|
+
export class MinHeap<T> {
|
|
108
|
+
private readonly items: T[] = [];
|
|
109
|
+
|
|
110
|
+
constructor(private readonly compare: (left: T, right: T) => number) {}
|
|
111
|
+
|
|
112
|
+
get size(): number {
|
|
113
|
+
return this.items.length;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
peek(): T | undefined {
|
|
117
|
+
return this.items[0];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
push(item: T): void {
|
|
121
|
+
this.items.push(item);
|
|
122
|
+
this.bubbleUp(this.items.length - 1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pop(): T | undefined {
|
|
126
|
+
const first = this.items[0];
|
|
127
|
+
if (first === undefined) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
const last = this.items.pop();
|
|
131
|
+
if (last === undefined || this.items.length === 0) {
|
|
132
|
+
return first;
|
|
133
|
+
}
|
|
134
|
+
this.items[0] = last;
|
|
135
|
+
this.bubbleDown(0);
|
|
136
|
+
return first;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private bubbleUp(index: number): void {
|
|
140
|
+
let current = index;
|
|
141
|
+
while (current > 0) {
|
|
142
|
+
const parent = Math.floor((current - 1) / 2);
|
|
143
|
+
const parentItem = this.items[parent];
|
|
144
|
+
const currentItem = this.items[current];
|
|
145
|
+
if (
|
|
146
|
+
parentItem === undefined ||
|
|
147
|
+
currentItem === undefined ||
|
|
148
|
+
this.compare(currentItem, parentItem) >= 0
|
|
149
|
+
) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.items[parent] = currentItem;
|
|
153
|
+
this.items[current] = parentItem;
|
|
154
|
+
current = parent;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private bubbleDown(index: number): void {
|
|
159
|
+
let current = index;
|
|
160
|
+
for (;;) {
|
|
161
|
+
const left = current * 2 + 1;
|
|
162
|
+
const right = left + 1;
|
|
163
|
+
let smallest = current;
|
|
164
|
+
const currentItem = this.items[smallest];
|
|
165
|
+
const leftItem = this.items[left];
|
|
166
|
+
if (
|
|
167
|
+
leftItem !== undefined &&
|
|
168
|
+
currentItem !== undefined &&
|
|
169
|
+
this.compare(leftItem, currentItem) < 0
|
|
170
|
+
) {
|
|
171
|
+
smallest = left;
|
|
172
|
+
}
|
|
173
|
+
const smallestItem = this.items[smallest];
|
|
174
|
+
const rightItem = this.items[right];
|
|
175
|
+
if (
|
|
176
|
+
rightItem !== undefined &&
|
|
177
|
+
smallestItem !== undefined &&
|
|
178
|
+
this.compare(rightItem, smallestItem) < 0
|
|
179
|
+
) {
|
|
180
|
+
smallest = right;
|
|
181
|
+
}
|
|
182
|
+
if (smallest === current) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const swapCurrent = this.items[current];
|
|
186
|
+
const swapSmallest = this.items[smallest];
|
|
187
|
+
if (swapCurrent === undefined || swapSmallest === undefined) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this.items[current] = swapSmallest;
|
|
191
|
+
this.items[smallest] = swapCurrent;
|
|
192
|
+
current = smallest;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|