@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.
Files changed (71) hide show
  1. package/README.md +6 -5
  2. package/assets/skill/SKILL.md +25 -0
  3. package/assets/skill/mcp-reference.md +6 -0
  4. package/browser-extension/artifacts/{gno-browser-clipper-v1.30.7.zip → gno-browser-clipper-v1.32.0.zip} +0 -0
  5. package/browser-extension/artifacts/gno-browser-clipper-v1.32.0.zip.sha256 +1 -0
  6. package/browser-extension/dist/manifest.json +1 -1
  7. package/package.json +1 -1
  8. package/spec/cli.md +19 -0
  9. package/spec/db/schema.sql +55 -0
  10. package/spec/mcp.md +176 -11
  11. package/spec/output-schemas/file-refactor-apply-result.schema.json +305 -0
  12. package/spec/output-schemas/file-refactor-preview.schema.json +393 -0
  13. package/spec/output-schemas/section-target-create-result.schema.json +20 -0
  14. package/spec/output-schemas/section-target-resolve-result.schema.json +194 -0
  15. package/spec/output-schemas/section-target.schema.json +118 -0
  16. package/spec/output-schemas/section.schema.json +113 -0
  17. package/src/core/document-capabilities.ts +13 -0
  18. package/src/core/file-ops.ts +129 -1
  19. package/src/core/file-refactor-adapter.ts +329 -0
  20. package/src/core/file-refactor-apply-edits.ts +61 -0
  21. package/src/core/file-refactor-apply-fs.ts +512 -0
  22. package/src/core/file-refactor-apply-safety.ts +340 -0
  23. package/src/core/file-refactor-apply-validate.ts +401 -0
  24. package/src/core/file-refactor-contract.ts +486 -0
  25. package/src/core/file-refactor-destination.ts +123 -0
  26. package/src/core/file-refactor-from-snapshot.ts +148 -0
  27. package/src/core/file-refactor-journal-port.ts +150 -0
  28. package/src/core/file-refactor-journal.ts +347 -0
  29. package/src/core/file-refactor-paths.ts +60 -0
  30. package/src/core/file-refactor-plan-classify.ts +208 -0
  31. package/src/core/file-refactor-plan-validate.ts +169 -0
  32. package/src/core/file-refactor-planner-types.ts +62 -0
  33. package/src/core/file-refactor-planner.ts +423 -0
  34. package/src/core/file-refactor-resolve.ts +280 -0
  35. package/src/core/file-refactor-service.ts +468 -0
  36. package/src/core/file-refactors.ts +84 -56
  37. package/src/core/link-destination-parse.ts +275 -0
  38. package/src/core/link-inventory-markdown.ts +454 -0
  39. package/src/core/link-inventory-opaque.ts +244 -0
  40. package/src/core/link-inventory-types.ts +47 -0
  41. package/src/core/link-inventory.ts +182 -0
  42. package/src/core/link-relevance.ts +150 -0
  43. package/src/core/section-parse.ts +187 -0
  44. package/src/core/section-target-link.ts +154 -0
  45. package/src/core/section-target-resolve.ts +351 -0
  46. package/src/core/section-target-transport.ts +519 -0
  47. package/src/core/section-target.ts +263 -0
  48. package/src/core/sections.ts +60 -115
  49. package/src/mcp/AGENTS.md +1 -0
  50. package/src/mcp/CLAUDE.md +1 -0
  51. package/src/mcp/http-egress.ts +1 -0
  52. package/src/mcp/tools/index.ts +37 -17
  53. package/src/mcp/tools/sections.ts +512 -0
  54. package/src/mcp/tools/workspace-write.ts +215 -97
  55. package/src/sdk/client.ts +238 -116
  56. package/src/sdk/index.ts +12 -0
  57. package/src/sdk/types.ts +61 -3
  58. package/src/serve/file-refactor-http.ts +239 -0
  59. package/src/serve/public/components/RefactorImpactPreview.tsx +227 -0
  60. package/src/serve/public/globals.built.css +1 -1
  61. package/src/serve/public/lib/section-links.ts +189 -0
  62. package/src/serve/public/pages/DocView.tsx +395 -77
  63. package/src/serve/routes/api.ts +191 -104
  64. package/src/serve/routes/section-targets.ts +221 -0
  65. package/src/serve/server.ts +34 -0
  66. package/src/store/migrations/026-file-refactor-recovery-journal.ts +72 -0
  67. package/src/store/migrations/index.ts +2 -0
  68. package/src/store/sqlite/adapter.ts +452 -0
  69. package/src/store/sqlite/file-refactor-journal-store.ts +275 -0
  70. package/src/store/types.ts +84 -0
  71. 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
+ }