@gmickel/gno 1.30.7 → 1.32.0
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/README.md +6 -5
- package/assets/skill/SKILL.md +25 -0
- package/assets/skill/mcp-reference.md +6 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v1.30.7.zip → gno-browser-clipper-v1.32.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.32.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +19 -0
- package/spec/db/schema.sql +55 -0
- package/spec/mcp.md +176 -11
- package/spec/output-schemas/file-refactor-apply-result.schema.json +305 -0
- package/spec/output-schemas/file-refactor-preview.schema.json +393 -0
- package/spec/output-schemas/section-target-create-result.schema.json +20 -0
- package/spec/output-schemas/section-target-resolve-result.schema.json +194 -0
- package/spec/output-schemas/section-target.schema.json +118 -0
- package/spec/output-schemas/section.schema.json +113 -0
- package/src/core/document-capabilities.ts +13 -0
- package/src/core/file-ops.ts +129 -1
- package/src/core/file-refactor-adapter.ts +329 -0
- package/src/core/file-refactor-apply-edits.ts +61 -0
- package/src/core/file-refactor-apply-fs.ts +512 -0
- package/src/core/file-refactor-apply-safety.ts +340 -0
- package/src/core/file-refactor-apply-validate.ts +401 -0
- package/src/core/file-refactor-contract.ts +486 -0
- package/src/core/file-refactor-destination.ts +123 -0
- package/src/core/file-refactor-from-snapshot.ts +148 -0
- package/src/core/file-refactor-journal-port.ts +150 -0
- package/src/core/file-refactor-journal.ts +347 -0
- package/src/core/file-refactor-paths.ts +60 -0
- package/src/core/file-refactor-plan-classify.ts +208 -0
- package/src/core/file-refactor-plan-validate.ts +169 -0
- package/src/core/file-refactor-planner-types.ts +62 -0
- package/src/core/file-refactor-planner.ts +423 -0
- package/src/core/file-refactor-resolve.ts +280 -0
- package/src/core/file-refactor-service.ts +468 -0
- package/src/core/file-refactors.ts +84 -56
- package/src/core/link-destination-parse.ts +275 -0
- package/src/core/link-inventory-markdown.ts +454 -0
- package/src/core/link-inventory-opaque.ts +244 -0
- package/src/core/link-inventory-types.ts +47 -0
- package/src/core/link-inventory.ts +182 -0
- package/src/core/link-relevance.ts +150 -0
- package/src/core/section-parse.ts +187 -0
- package/src/core/section-target-link.ts +154 -0
- package/src/core/section-target-resolve.ts +351 -0
- package/src/core/section-target-transport.ts +519 -0
- package/src/core/section-target.ts +263 -0
- package/src/core/sections.ts +60 -115
- package/src/mcp/AGENTS.md +1 -0
- package/src/mcp/CLAUDE.md +1 -0
- package/src/mcp/http-egress.ts +1 -0
- package/src/mcp/tools/index.ts +37 -17
- package/src/mcp/tools/sections.ts +512 -0
- package/src/mcp/tools/workspace-write.ts +215 -97
- package/src/sdk/client.ts +238 -116
- package/src/sdk/index.ts +12 -0
- package/src/sdk/types.ts +61 -3
- package/src/serve/file-refactor-http.ts +239 -0
- package/src/serve/public/components/RefactorImpactPreview.tsx +227 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/section-links.ts +189 -0
- package/src/serve/public/pages/DocView.tsx +395 -77
- package/src/serve/routes/api.ts +191 -104
- package/src/serve/routes/section-targets.ts +221 -0
- package/src/serve/server.ts +34 -0
- package/src/store/migrations/026-file-refactor-recovery-journal.ts +72 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/sqlite/adapter.ts +452 -0
- package/src/store/sqlite/file-refactor-journal-store.ts +275 -0
- package/src/store/types.ts +84 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.30.7.zip.sha256 +0 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommonMark destination token helpers for reference-safe rewrites.
|
|
3
|
+
*
|
|
4
|
+
* @module src/core/link-destination-parse
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** ASCII punctuation escapable with `\` in CommonMark destinations. */
|
|
8
|
+
const COMMONMARK_ESCAPABLE = new Set([
|
|
9
|
+
"!",
|
|
10
|
+
'"',
|
|
11
|
+
"#",
|
|
12
|
+
"$",
|
|
13
|
+
"%",
|
|
14
|
+
"&",
|
|
15
|
+
"'",
|
|
16
|
+
"(",
|
|
17
|
+
")",
|
|
18
|
+
"*",
|
|
19
|
+
"+",
|
|
20
|
+
",",
|
|
21
|
+
"-",
|
|
22
|
+
".",
|
|
23
|
+
"/",
|
|
24
|
+
":",
|
|
25
|
+
";",
|
|
26
|
+
"<",
|
|
27
|
+
"=",
|
|
28
|
+
">",
|
|
29
|
+
"?",
|
|
30
|
+
"@",
|
|
31
|
+
"[",
|
|
32
|
+
"\\",
|
|
33
|
+
"]",
|
|
34
|
+
"^",
|
|
35
|
+
"_",
|
|
36
|
+
"`",
|
|
37
|
+
"{",
|
|
38
|
+
"|",
|
|
39
|
+
"}",
|
|
40
|
+
"~",
|
|
41
|
+
// Destination paths also use backslash-escaped spaces in CommonMark practice.
|
|
42
|
+
" ",
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
export interface LinkEncodingStyle {
|
|
46
|
+
spaces: "percent" | "backslash" | "raw";
|
|
47
|
+
parens: "percent" | "backslash" | "raw";
|
|
48
|
+
/** True when the destination was wrapped in angle brackets. */
|
|
49
|
+
angleBrackets: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function detectEncodingStyle(destination: string): LinkEncodingStyle {
|
|
53
|
+
const interior =
|
|
54
|
+
destination.startsWith("<") && destination.endsWith(">")
|
|
55
|
+
? destination.slice(1, -1)
|
|
56
|
+
: destination;
|
|
57
|
+
const hasBackslashSpace = /\\[ ]/.test(interior);
|
|
58
|
+
const hasBackslashParen = /\\[()]/.test(interior);
|
|
59
|
+
return {
|
|
60
|
+
spaces: interior.includes("%20")
|
|
61
|
+
? "percent"
|
|
62
|
+
: hasBackslashSpace
|
|
63
|
+
? "backslash"
|
|
64
|
+
: "raw",
|
|
65
|
+
parens:
|
|
66
|
+
interior.includes("%28") || interior.includes("%29")
|
|
67
|
+
? "percent"
|
|
68
|
+
: hasBackslashParen
|
|
69
|
+
? "backslash"
|
|
70
|
+
: "raw",
|
|
71
|
+
angleBrackets:
|
|
72
|
+
destination.startsWith("<") &&
|
|
73
|
+
destination.endsWith(">") &&
|
|
74
|
+
destination.length >= 2,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Unescape only safe CommonMark punctuation escapes for resolution.
|
|
80
|
+
* Leaves unknown backslash sequences intact.
|
|
81
|
+
*/
|
|
82
|
+
export function unescapeCommonMarkDestination(raw: string): string {
|
|
83
|
+
let result = "";
|
|
84
|
+
for (let i = 0; i < raw.length; i += 1) {
|
|
85
|
+
const ch = raw[i];
|
|
86
|
+
const next = raw[i + 1];
|
|
87
|
+
if (ch === "\\" && next !== undefined && COMMONMARK_ESCAPABLE.has(next)) {
|
|
88
|
+
result += next;
|
|
89
|
+
i += 1;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
result += ch ?? "";
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Strip a single outer CommonMark angle-bracket wrapper when present.
|
|
99
|
+
*/
|
|
100
|
+
export function stripAngleBracketDestination(raw: string): {
|
|
101
|
+
path: string;
|
|
102
|
+
angleBrackets: boolean;
|
|
103
|
+
} {
|
|
104
|
+
if (raw.startsWith("<") && raw.endsWith(">") && raw.length >= 2) {
|
|
105
|
+
return { path: raw.slice(1, -1), angleBrackets: true };
|
|
106
|
+
}
|
|
107
|
+
return { path: raw, angleBrackets: false };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Re-apply the observed escape/encoding style to a replacement path token.
|
|
112
|
+
* Does not wrap angle brackets — callers leave `<>` outside the edit span.
|
|
113
|
+
*/
|
|
114
|
+
export function applyDestinationEncodingStyle(
|
|
115
|
+
pathValue: string,
|
|
116
|
+
style: LinkEncodingStyle
|
|
117
|
+
): string {
|
|
118
|
+
let result = pathValue;
|
|
119
|
+
if (style.spaces === "percent") {
|
|
120
|
+
result = result.replaceAll(" ", "%20");
|
|
121
|
+
} else if (style.spaces === "backslash") {
|
|
122
|
+
result = result.replaceAll(" ", "\\ ");
|
|
123
|
+
}
|
|
124
|
+
if (style.parens === "percent") {
|
|
125
|
+
result = result.replaceAll("(", "%28").replaceAll(")", "%29");
|
|
126
|
+
} else if (style.parens === "backslash") {
|
|
127
|
+
result = result.replaceAll("(", "\\(").replaceAll(")", "\\)");
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function splitDestinationPath(raw: string): {
|
|
133
|
+
path: string;
|
|
134
|
+
query?: string;
|
|
135
|
+
anchor?: string;
|
|
136
|
+
} {
|
|
137
|
+
let remaining = raw;
|
|
138
|
+
let anchor: string | undefined;
|
|
139
|
+
let query: string | undefined;
|
|
140
|
+
|
|
141
|
+
const hashIndex = remaining.indexOf("#");
|
|
142
|
+
if (hashIndex >= 0) {
|
|
143
|
+
anchor = remaining.slice(hashIndex + 1);
|
|
144
|
+
remaining = remaining.slice(0, hashIndex);
|
|
145
|
+
}
|
|
146
|
+
const queryIndex = remaining.indexOf("?");
|
|
147
|
+
if (queryIndex >= 0) {
|
|
148
|
+
query = remaining.slice(queryIndex + 1);
|
|
149
|
+
remaining = remaining.slice(0, queryIndex);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
path: remaining,
|
|
153
|
+
query: query && query.length > 0 ? query : undefined,
|
|
154
|
+
anchor: anchor && anchor.length > 0 ? anchor : undefined,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Parse a CommonMark link destination inside parentheses, allowing balanced
|
|
160
|
+
* parentheses, angle-bracket destinations, and an optional title after whitespace.
|
|
161
|
+
*/
|
|
162
|
+
export function parseParenthesizedDestination(
|
|
163
|
+
markdown: string,
|
|
164
|
+
openParenOffset: number
|
|
165
|
+
): {
|
|
166
|
+
destinationRaw: string;
|
|
167
|
+
destinationStart: number;
|
|
168
|
+
destinationEnd: number;
|
|
169
|
+
closeParenOffset: number;
|
|
170
|
+
titlePresent: boolean;
|
|
171
|
+
angleBrackets: boolean;
|
|
172
|
+
} | null {
|
|
173
|
+
if (markdown[openParenOffset] !== "(") return null;
|
|
174
|
+
let i = openParenOffset + 1;
|
|
175
|
+
while (i < markdown.length && /[ \t]/.test(markdown[i] ?? "")) i += 1;
|
|
176
|
+
|
|
177
|
+
const destStart = i;
|
|
178
|
+
let depth = 0;
|
|
179
|
+
let destEnd = -1;
|
|
180
|
+
let angleBrackets = false;
|
|
181
|
+
|
|
182
|
+
if (markdown[i] === "<") {
|
|
183
|
+
const closeAngle = markdown.indexOf(">", i + 1);
|
|
184
|
+
if (closeAngle < 0) return null;
|
|
185
|
+
angleBrackets = true;
|
|
186
|
+
destEnd = closeAngle + 1;
|
|
187
|
+
i = destEnd;
|
|
188
|
+
while (i < markdown.length && /[ \t]/.test(markdown[i] ?? "")) i += 1;
|
|
189
|
+
if (markdown[i] === ")") {
|
|
190
|
+
const destinationRaw = markdown.slice(destStart, destEnd);
|
|
191
|
+
return {
|
|
192
|
+
destinationRaw,
|
|
193
|
+
destinationStart: destStart,
|
|
194
|
+
destinationEnd: destEnd,
|
|
195
|
+
closeParenOffset: i,
|
|
196
|
+
titlePresent: false,
|
|
197
|
+
angleBrackets,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// Title may follow; fall through with destEnd already set.
|
|
201
|
+
} else {
|
|
202
|
+
while (i < markdown.length) {
|
|
203
|
+
const ch = markdown[i];
|
|
204
|
+
if (ch === undefined) break;
|
|
205
|
+
if (ch === "\\" && i + 1 < markdown.length) {
|
|
206
|
+
i += 2;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (ch === "(") {
|
|
210
|
+
depth += 1;
|
|
211
|
+
i += 1;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (ch === ")") {
|
|
215
|
+
if (depth === 0) {
|
|
216
|
+
destEnd = i;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
depth -= 1;
|
|
220
|
+
i += 1;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (
|
|
224
|
+
depth === 0 &&
|
|
225
|
+
(ch === " " || ch === "\t" || ch === "\n" || ch === "\r")
|
|
226
|
+
) {
|
|
227
|
+
destEnd = i;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
i += 1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (destEnd < 0) return null;
|
|
235
|
+
|
|
236
|
+
let close = destEnd;
|
|
237
|
+
let titlePresent = false;
|
|
238
|
+
if (destEnd < markdown.length && /[ \t\r\n]/.test(markdown[destEnd] ?? "")) {
|
|
239
|
+
titlePresent = true;
|
|
240
|
+
let j = destEnd;
|
|
241
|
+
while (j < markdown.length && /[ \t\r\n]/.test(markdown[j] ?? "")) j += 1;
|
|
242
|
+
const opener = markdown[j];
|
|
243
|
+
if (opener === '"' || opener === "'") {
|
|
244
|
+
j += 1;
|
|
245
|
+
while (j < markdown.length && markdown[j] !== opener) {
|
|
246
|
+
if (markdown[j] === "\\" && j + 1 < markdown.length) j += 2;
|
|
247
|
+
else j += 1;
|
|
248
|
+
}
|
|
249
|
+
if (markdown[j] !== opener) return null;
|
|
250
|
+
j += 1;
|
|
251
|
+
} else if (opener === "(") {
|
|
252
|
+
j += 1;
|
|
253
|
+
while (j < markdown.length && markdown[j] !== ")") {
|
|
254
|
+
if (markdown[j] === "\\" && j + 1 < markdown.length) j += 2;
|
|
255
|
+
else j += 1;
|
|
256
|
+
}
|
|
257
|
+
if (markdown[j] !== ")") return null;
|
|
258
|
+
j += 1;
|
|
259
|
+
}
|
|
260
|
+
while (j < markdown.length && /[ \t]/.test(markdown[j] ?? "")) j += 1;
|
|
261
|
+
if (markdown[j] !== ")") return null;
|
|
262
|
+
close = j;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const destinationRaw = markdown.slice(destStart, destEnd);
|
|
266
|
+
if (!destinationRaw) return null;
|
|
267
|
+
return {
|
|
268
|
+
destinationRaw,
|
|
269
|
+
destinationStart: destStart,
|
|
270
|
+
destinationEnd: destEnd,
|
|
271
|
+
closeParenOffset: close,
|
|
272
|
+
titlePresent,
|
|
273
|
+
angleBrackets,
|
|
274
|
+
};
|
|
275
|
+
}
|