@cruxy/cli 0.29.4 → 0.29.5
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { resolveToolPath } from "./paths.js";
|
|
4
|
+
import { resolveToolPath, toPosix } from "./paths.js";
|
|
5
5
|
import { applyEol, detectEol, findMatch, tierLabel } from "./match.js";
|
|
6
6
|
/** How many leading lines of a created file the approval preview shows. */
|
|
7
7
|
const PREVIEW_LINES = 20;
|
|
@@ -116,7 +116,9 @@ export const applyPatchTool = {
|
|
|
116
116
|
};
|
|
117
117
|
/** Validate one operation against the filesystem and compute its final bytes. */
|
|
118
118
|
async function planOp(i, op, abs, ctx) {
|
|
119
|
-
|
|
119
|
+
// Forward-slash for model-facing output (the `applied` lines and error
|
|
120
|
+
// messages), consistent with every other path tool — see {@link toPosix}.
|
|
121
|
+
const rel = toPosix(path.relative(ctx.cwd, abs));
|
|
120
122
|
if (op.type === "create") {
|
|
121
123
|
if (await exists(abs)) {
|
|
122
124
|
return { ok: false, error: opError(i, op, "file already exists") };
|
|
@@ -69,6 +69,16 @@ export declare function resolveInRoot(ctx: ToolContext, p: string): Promise<stri
|
|
|
69
69
|
* shared by `glob` and `grep_files` so the two walk-rooted tools cannot diverge
|
|
70
70
|
* (closing G1/G2). Path *arguments* go through {@link resolveToolPath}; glob
|
|
71
71
|
* *patterns* are not resolvable paths, so this predicate guards the walk instead.
|
|
72
|
+
*
|
|
73
|
+
* NB — a known internal inconsistency, documented rather than fixed: this split on
|
|
74
|
+
* `/[/\\]/` (and the sibling `firstSegmentIsRoot` in grep_files) treats `\` as a
|
|
75
|
+
* separator on EVERY platform, but the resolution kernel {@link confineToRoot} →
|
|
76
|
+
* `path.resolve` does NOT on POSIX (there `\` is a legal filename character). The
|
|
77
|
+
* two only diverge for a `..\`-style argument on POSIX, which is unreachable from
|
|
78
|
+
* anything the tools EMIT (grep/apply_patch produce `\` only on Windows, where
|
|
79
|
+
* `path.resolve` does treat it as a separator). It is left as-is deliberately:
|
|
80
|
+
* "fixing" confineToRoot to fold `\`→`/` on POSIX would break access to files
|
|
81
|
+
* whose names legitimately contain a backslash.
|
|
72
82
|
*/
|
|
73
83
|
export declare function isEscapingPattern(pattern: string): boolean;
|
|
74
84
|
/**
|
|
@@ -76,6 +86,25 @@ export declare function isEscapingPattern(pattern: string): boolean;
|
|
|
76
86
|
* constant so every no-path tool that fans across roots renders the same label.
|
|
77
87
|
*/
|
|
78
88
|
export declare const ROOT_LABEL_SEP = " \u25B8 ";
|
|
89
|
+
/**
|
|
90
|
+
* Rewrite the OS path separator to forward slashes for MODEL-FACING output — the
|
|
91
|
+
* one display convention every path tool shares. The rest of the toolset already
|
|
92
|
+
* emits POSIX paths on Windows (the indexer normalises the same way in
|
|
93
|
+
* `indexing/walker.ts`, and git's porcelain and tinyglobby both hand back `/`);
|
|
94
|
+
* this seam is what keeps the two `path.relative`-based stragglers, `grep_files`
|
|
95
|
+
* and `apply_patch`, from drifting to native `src\b.ts`.
|
|
96
|
+
*
|
|
97
|
+
* It splits on `path.sep`, NOT a literal `\`, so it only ever rewrites the real OS
|
|
98
|
+
* separator: on POSIX (`path.sep === "/"`) it is a no-op that PRESERVES a literal
|
|
99
|
+
* backslash in a filename — legal on POSIX — rather than mangling it; on Windows
|
|
100
|
+
* (`path.sep === "\\"`) it turns `path.relative`'s `src\b.ts` into `src/b.ts`.
|
|
101
|
+
*
|
|
102
|
+
* Purely cosmetic at the boundary — the model never needs native separators back:
|
|
103
|
+
* a path it echoes into read_file/edit_file resolves through {@link confineToRoot}
|
|
104
|
+
* → `path.resolve`, which accepts `/` on Windows, so the forward-slash form
|
|
105
|
+
* round-trips as-is.
|
|
106
|
+
*/
|
|
107
|
+
export declare function toPosix(p: string): string;
|
|
79
108
|
/**
|
|
80
109
|
* Prefix a root-relative path with its root name — but ONLY in a genuine
|
|
81
110
|
* multi-root session. In single-root the label is dropped so output stays
|
package/dist/tools/file/paths.js
CHANGED
|
@@ -92,6 +92,16 @@ export async function resolveInRoot(ctx, p) {
|
|
|
92
92
|
* shared by `glob` and `grep_files` so the two walk-rooted tools cannot diverge
|
|
93
93
|
* (closing G1/G2). Path *arguments* go through {@link resolveToolPath}; glob
|
|
94
94
|
* *patterns* are not resolvable paths, so this predicate guards the walk instead.
|
|
95
|
+
*
|
|
96
|
+
* NB — a known internal inconsistency, documented rather than fixed: this split on
|
|
97
|
+
* `/[/\\]/` (and the sibling `firstSegmentIsRoot` in grep_files) treats `\` as a
|
|
98
|
+
* separator on EVERY platform, but the resolution kernel {@link confineToRoot} →
|
|
99
|
+
* `path.resolve` does NOT on POSIX (there `\` is a legal filename character). The
|
|
100
|
+
* two only diverge for a `..\`-style argument on POSIX, which is unreachable from
|
|
101
|
+
* anything the tools EMIT (grep/apply_patch produce `\` only on Windows, where
|
|
102
|
+
* `path.resolve` does treat it as a separator). It is left as-is deliberately:
|
|
103
|
+
* "fixing" confineToRoot to fold `\`→`/` on POSIX would break access to files
|
|
104
|
+
* whose names legitimately contain a backslash.
|
|
95
105
|
*/
|
|
96
106
|
export function isEscapingPattern(pattern) {
|
|
97
107
|
return path.isAbsolute(pattern) || pattern.split(/[/\\]/).includes("..");
|
|
@@ -101,6 +111,27 @@ export function isEscapingPattern(pattern) {
|
|
|
101
111
|
* constant so every no-path tool that fans across roots renders the same label.
|
|
102
112
|
*/
|
|
103
113
|
export const ROOT_LABEL_SEP = " ▸ ";
|
|
114
|
+
/**
|
|
115
|
+
* Rewrite the OS path separator to forward slashes for MODEL-FACING output — the
|
|
116
|
+
* one display convention every path tool shares. The rest of the toolset already
|
|
117
|
+
* emits POSIX paths on Windows (the indexer normalises the same way in
|
|
118
|
+
* `indexing/walker.ts`, and git's porcelain and tinyglobby both hand back `/`);
|
|
119
|
+
* this seam is what keeps the two `path.relative`-based stragglers, `grep_files`
|
|
120
|
+
* and `apply_patch`, from drifting to native `src\b.ts`.
|
|
121
|
+
*
|
|
122
|
+
* It splits on `path.sep`, NOT a literal `\`, so it only ever rewrites the real OS
|
|
123
|
+
* separator: on POSIX (`path.sep === "/"`) it is a no-op that PRESERVES a literal
|
|
124
|
+
* backslash in a filename — legal on POSIX — rather than mangling it; on Windows
|
|
125
|
+
* (`path.sep === "\\"`) it turns `path.relative`'s `src\b.ts` into `src/b.ts`.
|
|
126
|
+
*
|
|
127
|
+
* Purely cosmetic at the boundary — the model never needs native separators back:
|
|
128
|
+
* a path it echoes into read_file/edit_file resolves through {@link confineToRoot}
|
|
129
|
+
* → `path.resolve`, which accepts `/` on Windows, so the forward-slash form
|
|
130
|
+
* round-trips as-is.
|
|
131
|
+
*/
|
|
132
|
+
export function toPosix(p) {
|
|
133
|
+
return p.split(path.sep).join("/");
|
|
134
|
+
}
|
|
104
135
|
/**
|
|
105
136
|
* Prefix a root-relative path with its root name — but ONLY in a genuine
|
|
106
137
|
* multi-root session. In single-root the label is dropped so output stays
|
|
@@ -109,11 +140,13 @@ export const ROOT_LABEL_SEP = " ▸ ";
|
|
|
109
140
|
* point at a different root than the one that was walked (the honesty pin).
|
|
110
141
|
*/
|
|
111
142
|
export function labelPath(root, rel, isMultiRoot) {
|
|
112
|
-
|
|
143
|
+
// `rel` is normalised to forward slashes exactly once on each branch: via
|
|
144
|
+
// {@link labelName} when labelled, or directly when the label is dropped.
|
|
145
|
+
return isMultiRoot ? labelName(root.name, rel) : toPosix(rel);
|
|
113
146
|
}
|
|
114
147
|
/** As {@link labelPath} but from a bare root name (for hits that carry only a name). */
|
|
115
148
|
export function labelName(rootName, rel) {
|
|
116
|
-
return `${rootName}${ROOT_LABEL_SEP}${rel}`;
|
|
149
|
+
return `${rootName}${ROOT_LABEL_SEP}${toPosix(rel)}`;
|
|
117
150
|
}
|
|
118
151
|
/**
|
|
119
152
|
* The Funnel-B selector for **no-path** tools (`glob`, `grep_files`, `list_files`,
|