@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
package/src/sdk/client.ts CHANGED
@@ -34,10 +34,12 @@ import type {
34
34
  GnoIndexResult,
35
35
  GnoIndexStatus,
36
36
  GnoListOptions,
37
+ GnoMoveNoteApplyOptions,
37
38
  GnoMoveNoteOptions,
38
39
  GnoMultiGetOptions,
39
40
  GnoQueryOptions,
40
41
  GnoRefactorNoteResult,
42
+ GnoRenameNoteApplyOptions,
41
43
  GnoRenameNoteOptions,
42
44
  GnoSearchOptions,
43
45
  GnoUpdateOptions,
@@ -47,6 +49,10 @@ import type {
47
49
  KnowledgeImpactInput,
48
50
  KnowledgeImpactResult,
49
51
  ListKnowledgeChangesInput,
52
+ SectionTargetCreateResult,
53
+ SectionTargetCreateSelector,
54
+ SectionTargetResolveResult,
55
+ SectionTargetV1,
50
56
  } from "./types";
51
57
 
52
58
  import {
@@ -82,20 +88,23 @@ import { writeCapturePlanFile } from "../core/capture-write";
82
88
  import { projectCollectionEgressPolicy } from "../core/collection-egress-policy-projection";
83
89
  import { CollectionEgressPolicyService } from "../core/collection-egress-policy-service";
84
90
  import { applyConfigChange } from "../core/config-mutation";
91
+ import { getDocumentCapabilities } from "../core/document-capabilities";
85
92
  import { EgressAuditService } from "../core/egress-audit";
86
93
  import { authorizeCurrentEgress } from "../core/egress-authorization";
94
+ import { atomicWrite, copyFilePath, createFolderPath } from "../core/file-ops";
87
95
  import {
88
- atomicWrite,
89
- copyFilePath,
90
- createFolderPath,
91
- renameFilePath,
92
- } from "../core/file-ops";
93
- import {
96
+ applyCanonicalFileRefactor,
97
+ assertFileRefactorSyncConverged,
98
+ buildCanonicalRefactorPlan,
99
+ buildDurableFileRefactorApplyDeps,
94
100
  buildRefactorWarnings,
101
+ parseRefactorApplyConfirmation,
95
102
  planCreateFolder,
96
103
  planDuplicateRefactor,
97
- planMoveRefactor,
98
- planRenameRefactor,
104
+ resolveMoveTarget,
105
+ resolveRenameTarget,
106
+ type FileRefactorApplyResult,
107
+ type FileRefactorPreviewPlan,
99
108
  } from "../core/file-refactors";
100
109
  import { resolveEffectiveIndex } from "../core/indexed-reference";
101
110
  import {
@@ -122,7 +131,17 @@ import {
122
131
  RETRIEVAL_TRACE_METADATA,
123
132
  type RetrievalTraceSession,
124
133
  } from "../core/retrieval-trace-session";
125
- import { extractSections } from "../core/sections";
134
+ import {
135
+ CANONICAL_URI_EXCEEDS_TRANSPORT_BOUNDS,
136
+ createSectionTarget as createSectionTargetCore,
137
+ extractSections,
138
+ isTransportBoundedCanonicalUri,
139
+ parseSectionTargetCreateSelector,
140
+ parseSectionTargetV1,
141
+ projectSectionTargetCreateResult,
142
+ projectSectionTargetResolveResult,
143
+ resolveSectionTarget as resolveSectionTargetCore,
144
+ } from "../core/sections";
126
145
  import { normalizeStructuredQueryInput } from "../core/structured-query";
127
146
  import { parseAndValidateTagFilter } from "../core/tags";
128
147
  import {
@@ -320,6 +339,52 @@ class GnoClientImpl implements GnoClient {
320
339
  return filtered;
321
340
  }
322
341
 
342
+ private requireRefactorApplyConfirmation(options: {
343
+ schemaVersion?: unknown;
344
+ planDigest?: unknown;
345
+ confirmation?: unknown;
346
+ }) {
347
+ const parsed = parseRefactorApplyConfirmation(options);
348
+ if ("error" in parsed) {
349
+ throw sdkError("VALIDATION", parsed.message);
350
+ }
351
+ return parsed;
352
+ }
353
+
354
+ private async prepareRefactorSource(ref: string) {
355
+ const doc = await getDocumentByRef(this.store, this.config, ref, {});
356
+ const stored = await this.store.getDocumentByUri(doc.uri);
357
+ if (!stored.ok || !stored.value) {
358
+ throw sdkError("NOT_FOUND", "Document not found");
359
+ }
360
+ const storedDoc = stored.value;
361
+ const collection = this.getCollections(storedDoc.collection)[0];
362
+ if (!collection) {
363
+ throw sdkError(
364
+ "VALIDATION",
365
+ `Collection not found: ${storedDoc.collection}`
366
+ );
367
+ }
368
+ const capabilities = getDocumentCapabilities({
369
+ sourceExt: storedDoc.sourceExt,
370
+ sourceMime: storedDoc.sourceMime,
371
+ contentAvailable: storedDoc.mirrorHash !== null,
372
+ recordKey: storedDoc.recordKey,
373
+ });
374
+ if (!capabilities.editable) {
375
+ throw sdkError(
376
+ "VALIDATION",
377
+ capabilities.reason ?? "Document is read-only in place."
378
+ );
379
+ }
380
+ return {
381
+ storedDoc,
382
+ collection,
383
+ sourceFullPath: `${collection.path}/${storedDoc.relPath}`,
384
+ editable: capabilities.editable,
385
+ };
386
+ }
387
+
323
388
  private async createRuntimePorts(options: {
324
389
  embed?: boolean;
325
390
  expand?: boolean;
@@ -1643,125 +1708,126 @@ class GnoClientImpl implements GnoClient {
1643
1708
  };
1644
1709
  }
1645
1710
 
1646
- async renameNote(
1711
+ async previewRenameNote(
1647
1712
  options: GnoRenameNoteOptions
1648
- ): Promise<GnoRefactorNoteResult> {
1713
+ ): Promise<FileRefactorPreviewPlan> {
1649
1714
  this.assertOpen();
1650
- const doc = await getDocumentByRef(
1651
- this.store,
1652
- this.config,
1653
- options.ref,
1654
- {}
1655
- );
1656
- const stored = await this.store.getDocumentByUri(doc.uri);
1657
- if (!stored.ok || !stored.value) {
1658
- throw sdkError("NOT_FOUND", "Document not found");
1659
- }
1660
- const storedDoc = stored.value;
1661
- const collection = this.getCollections(storedDoc.collection)[0];
1662
- if (!collection) {
1663
- throw sdkError(
1664
- "VALIDATION",
1665
- `Collection not found: ${storedDoc.collection}`
1666
- );
1667
- }
1668
- const plan = planRenameRefactor({
1669
- collection: collection.name,
1670
- currentRelPath: storedDoc.relPath,
1715
+ const prepared = await this.prepareRefactorSource(options.ref);
1716
+ const target = resolveRenameTarget({
1717
+ collection: prepared.collection.name,
1718
+ currentRelPath: prepared.storedDoc.relPath,
1671
1719
  nextName: options.name,
1672
1720
  });
1673
- const currentPath = `${collection.path}/${storedDoc.relPath}`;
1674
- const nextPath = `${collection.path}/${plan.nextRelPath}`;
1675
- await renameFilePath(currentPath, nextPath);
1676
- await defaultSyncService.syncCollection(
1677
- collection,
1678
- this.store,
1679
- withContentTypeRules({ runUpdateCmd: false }, this.config)
1680
- );
1681
- const linksResult = await this.store.getLinksForDoc(storedDoc.id);
1682
- const backlinksResult = await this.store.getBacklinksForDoc(storedDoc.id);
1683
- if (!linksResult.ok || !backlinksResult.ok) {
1684
- throw sdkError("STORE", "Failed to compute refactor warnings");
1685
- }
1686
- return {
1687
- uri: plan.nextUri,
1688
- path: nextPath,
1689
- relPath: plan.nextRelPath,
1690
- warnings: buildRefactorWarnings(
1691
- {
1692
- backlinks: backlinksResult.value.length,
1693
- wikiLinks: linksResult.value.filter(
1694
- (entry) => entry.linkType === "wiki"
1695
- ).length,
1696
- markdownLinks: linksResult.value.filter(
1697
- (entry) => entry.linkType === "markdown"
1698
- ).length,
1721
+ return buildCanonicalRefactorPlan({
1722
+ operation: "rename",
1723
+ doc: prepared.storedDoc,
1724
+ collection: prepared.collection,
1725
+ sourceFullPath: prepared.sourceFullPath,
1726
+ target,
1727
+ store: this.store,
1728
+ sourceEditable: prepared.editable,
1729
+ });
1730
+ }
1731
+
1732
+ async previewMoveNote(
1733
+ options: GnoMoveNoteOptions
1734
+ ): Promise<FileRefactorPreviewPlan> {
1735
+ this.assertOpen();
1736
+ const prepared = await this.prepareRefactorSource(options.ref);
1737
+ const target = resolveMoveTarget({
1738
+ collection: prepared.collection.name,
1739
+ currentRelPath: prepared.storedDoc.relPath,
1740
+ folderPath: options.folderPath,
1741
+ nextName: options.name,
1742
+ });
1743
+ return buildCanonicalRefactorPlan({
1744
+ operation: "move",
1745
+ doc: prepared.storedDoc,
1746
+ collection: prepared.collection,
1747
+ sourceFullPath: prepared.sourceFullPath,
1748
+ target,
1749
+ store: this.store,
1750
+ sourceEditable: prepared.editable,
1751
+ });
1752
+ }
1753
+
1754
+ async renameNote(
1755
+ options: GnoRenameNoteApplyOptions
1756
+ ): Promise<FileRefactorApplyResult> {
1757
+ this.assertOpen();
1758
+ const confirmation = this.requireRefactorApplyConfirmation(options);
1759
+ const prepared = await this.prepareRefactorSource(options.ref);
1760
+ const target = resolveRenameTarget({
1761
+ collection: prepared.collection.name,
1762
+ currentRelPath: prepared.storedDoc.relPath,
1763
+ nextName: options.name,
1764
+ });
1765
+ const plan = await buildCanonicalRefactorPlan({
1766
+ operation: "rename",
1767
+ doc: prepared.storedDoc,
1768
+ collection: prepared.collection,
1769
+ sourceFullPath: prepared.sourceFullPath,
1770
+ target,
1771
+ store: this.store,
1772
+ sourceEditable: prepared.editable,
1773
+ });
1774
+ const apply = await applyCanonicalFileRefactor({
1775
+ plan,
1776
+ confirmation,
1777
+ deps: buildDurableFileRefactorApplyDeps({
1778
+ collection: prepared.collection,
1779
+ store: this.store,
1780
+ syncAfterCommit: async () => {
1781
+ const syncResult = await defaultSyncService.syncCollection(
1782
+ prepared.collection,
1783
+ this.store,
1784
+ withContentTypeRules({ runUpdateCmd: false }, this.config)
1785
+ );
1786
+ assertFileRefactorSyncConverged(syncResult);
1699
1787
  },
1700
- { filenameChanged: true }
1701
- ).warnings,
1702
- };
1788
+ }),
1789
+ });
1790
+ return apply;
1703
1791
  }
1704
1792
 
1705
- async moveNote(options: GnoMoveNoteOptions): Promise<GnoRefactorNoteResult> {
1793
+ async moveNote(
1794
+ options: GnoMoveNoteApplyOptions
1795
+ ): Promise<FileRefactorApplyResult> {
1706
1796
  this.assertOpen();
1707
- const doc = await getDocumentByRef(
1708
- this.store,
1709
- this.config,
1710
- options.ref,
1711
- {}
1712
- );
1713
- const stored = await this.store.getDocumentByUri(doc.uri);
1714
- if (!stored.ok || !stored.value) {
1715
- throw sdkError("NOT_FOUND", "Document not found");
1716
- }
1717
- const storedDoc = stored.value;
1718
- const collection = this.getCollections(storedDoc.collection)[0];
1719
- if (!collection) {
1720
- throw sdkError(
1721
- "VALIDATION",
1722
- `Collection not found: ${storedDoc.collection}`
1723
- );
1724
- }
1725
- const plan = planMoveRefactor({
1726
- collection: collection.name,
1727
- currentRelPath: storedDoc.relPath,
1797
+ const confirmation = this.requireRefactorApplyConfirmation(options);
1798
+ const prepared = await this.prepareRefactorSource(options.ref);
1799
+ const target = resolveMoveTarget({
1800
+ collection: prepared.collection.name,
1801
+ currentRelPath: prepared.storedDoc.relPath,
1728
1802
  folderPath: options.folderPath,
1729
1803
  nextName: options.name,
1730
1804
  });
1731
- const currentPath = `${collection.path}/${storedDoc.relPath}`;
1732
- const nextPath = `${collection.path}/${plan.nextRelPath}`;
1733
- await mkdir(dirname(nextPath), { recursive: true });
1734
- await renameFilePath(currentPath, nextPath);
1735
- await defaultSyncService.syncCollection(
1736
- collection,
1737
- this.store,
1738
- withContentTypeRules({ runUpdateCmd: false }, this.config)
1739
- );
1740
- const linksResult = await this.store.getLinksForDoc(storedDoc.id);
1741
- const backlinksResult = await this.store.getBacklinksForDoc(storedDoc.id);
1742
- if (!linksResult.ok || !backlinksResult.ok) {
1743
- throw sdkError("STORE", "Failed to compute refactor warnings");
1744
- }
1745
- return {
1746
- uri: plan.nextUri,
1747
- path: nextPath,
1748
- relPath: plan.nextRelPath,
1749
- warnings: buildRefactorWarnings(
1750
- {
1751
- backlinks: backlinksResult.value.length,
1752
- wikiLinks: linksResult.value.filter(
1753
- (entry) => entry.linkType === "wiki"
1754
- ).length,
1755
- markdownLinks: linksResult.value.filter(
1756
- (entry) => entry.linkType === "markdown"
1757
- ).length,
1805
+ const plan = await buildCanonicalRefactorPlan({
1806
+ operation: "move",
1807
+ doc: prepared.storedDoc,
1808
+ collection: prepared.collection,
1809
+ sourceFullPath: prepared.sourceFullPath,
1810
+ target,
1811
+ store: this.store,
1812
+ sourceEditable: prepared.editable,
1813
+ });
1814
+ const apply = await applyCanonicalFileRefactor({
1815
+ plan,
1816
+ confirmation,
1817
+ deps: buildDurableFileRefactorApplyDeps({
1818
+ collection: prepared.collection,
1819
+ store: this.store,
1820
+ syncAfterCommit: async () => {
1821
+ const syncResult = await defaultSyncService.syncCollection(
1822
+ prepared.collection,
1823
+ this.store,
1824
+ withContentTypeRules({ runUpdateCmd: false }, this.config)
1825
+ );
1826
+ assertFileRefactorSyncConverged(syncResult);
1758
1827
  },
1759
- {
1760
- folderChanged: true,
1761
- filenameChanged: Boolean(options.name),
1762
- }
1763
- ).warnings,
1764
- };
1828
+ }),
1829
+ });
1830
+ return apply;
1765
1831
  }
1766
1832
 
1767
1833
  async duplicateNote(
@@ -1835,6 +1901,62 @@ class GnoClientImpl implements GnoClient {
1835
1901
  return extractSections(document.content);
1836
1902
  }
1837
1903
 
1904
+ async createSectionTarget(
1905
+ ref: string,
1906
+ selector: SectionTargetCreateSelector
1907
+ ): Promise<SectionTargetCreateResult> {
1908
+ this.assertOpen();
1909
+ const parsed = parseSectionTargetCreateSelector(selector);
1910
+ if (!parsed.ok) {
1911
+ throw sdkError("VALIDATION", parsed.error);
1912
+ }
1913
+ const document = await getDocumentByRef(this.store, this.config, ref, {});
1914
+ // Top-level response uri shares schema maxLength — reject before create.
1915
+ if (!isTransportBoundedCanonicalUri(document.uri)) {
1916
+ throw sdkError("VALIDATION", CANONICAL_URI_EXCEEDS_TRANSPORT_BOUNDS);
1917
+ }
1918
+ // Canonical identity from stored document — never from caller.
1919
+ const target = await createSectionTargetCore({
1920
+ content: document.content,
1921
+ uri: document.uri,
1922
+ ...parsed.value,
1923
+ });
1924
+ if (!target) {
1925
+ const sections = extractSections(document.content);
1926
+ const matched =
1927
+ parsed.value.anchor !== undefined
1928
+ ? sections.some((section) => section.anchor === parsed.value.anchor)
1929
+ : sections.some((section) => section.line === parsed.value.line);
1930
+ if (!matched) {
1931
+ throw sdkError("NOT_FOUND", "Section not found");
1932
+ }
1933
+ throw sdkError("VALIDATION", "Section target exceeds size bounds");
1934
+ }
1935
+ return projectSectionTargetCreateResult(document.uri, target);
1936
+ }
1937
+
1938
+ async resolveSectionTarget(
1939
+ ref: string,
1940
+ target: SectionTargetV1
1941
+ ): Promise<SectionTargetResolveResult> {
1942
+ this.assertOpen();
1943
+ const parsed = parseSectionTargetV1(target);
1944
+ if (!parsed.ok) {
1945
+ throw sdkError("VALIDATION", parsed.error);
1946
+ }
1947
+ const document = await getDocumentByRef(this.store, this.config, ref, {});
1948
+ // Top-level uri must fit schema — citation fail-closed does not repair it.
1949
+ if (!isTransportBoundedCanonicalUri(document.uri)) {
1950
+ throw sdkError("VALIDATION", CANONICAL_URI_EXCEEDS_TRANSPORT_BOUNDS);
1951
+ }
1952
+ const resolution = await resolveSectionTargetCore({
1953
+ content: document.content,
1954
+ target: parsed.value,
1955
+ uri: document.uri,
1956
+ });
1957
+ return projectSectionTargetResolveResult(document.uri, resolution);
1958
+ }
1959
+
1838
1960
  async close(): Promise<void> {
1839
1961
  if (this.closed) {
1840
1962
  return;
package/src/sdk/index.ts CHANGED
@@ -28,6 +28,7 @@ export type { IndexStatus } from "../store/types";
28
28
  export { GnoSdkError, sdkError } from "./errors";
29
29
  export { createGnoClient } from "./client";
30
30
  export type {
31
+ DocumentSection,
31
32
  GnoAskOptions,
32
33
  GnoCaptureOptions,
33
34
  GnoCaptureResult,
@@ -39,6 +40,9 @@ export type {
39
40
  GnoClientInitOptions,
40
41
  GnoEmbedOptions,
41
42
  GnoEmbedResult,
43
+ GnoFileRefactorApplyConfirmation,
44
+ GnoFileRefactorApplyResult,
45
+ GnoFileRefactorPreviewPlan,
42
46
  GnoGetOptions,
43
47
  GnoGetResult,
44
48
  GnoIndexOptions,
@@ -48,10 +52,14 @@ export type {
48
52
  GnoListOptions,
49
53
  GnoListResult,
50
54
  GnoModelOverrides,
55
+ GnoMoveNoteApplyOptions,
56
+ GnoMoveNoteOptions,
51
57
  GnoMultiGetDocument,
52
58
  GnoMultiGetOptions,
53
59
  GnoMultiGetResult,
54
60
  GnoQueryOptions,
61
+ GnoRenameNoteApplyOptions,
62
+ GnoRenameNoteOptions,
55
63
  GnoProjectHintOptions,
56
64
  GnoSearchOptions,
57
65
  GnoSkippedDocument,
@@ -64,6 +72,10 @@ export type {
64
72
  KnowledgeImpactInput,
65
73
  KnowledgeImpactResult,
66
74
  ListKnowledgeChangesInput,
75
+ SectionTargetCreateResult,
76
+ SectionTargetCreateSelector,
77
+ SectionTargetResolveResult,
78
+ SectionTargetV1,
67
79
  } from "./types";
68
80
  export {
69
81
  ContextCapsuleContractError,
package/src/sdk/types.ts CHANGED
@@ -32,6 +32,10 @@ import type {
32
32
  EgressAuditShowResult,
33
33
  EgressAuditStatusResult,
34
34
  } from "../core/egress-audit";
35
+ import type {
36
+ FileRefactorApplyResult,
37
+ FileRefactorPreviewPlan,
38
+ } from "../core/file-refactor-contract";
35
39
  import type {
36
40
  KnowledgeChangesResult,
37
41
  KnowledgeDiffResult,
@@ -52,7 +56,13 @@ import type {
52
56
  RetrievalTraceListResult,
53
57
  RetrievalTracePurgeResult as RetrievalTraceManagementPurgeResult,
54
58
  } from "../core/retrieval-trace-management";
55
- import type { DocumentSection } from "../core/sections";
59
+ import type {
60
+ DocumentSection,
61
+ SectionTargetCreateResult,
62
+ SectionTargetCreateSelector,
63
+ SectionTargetResolveResult,
64
+ SectionTargetV1,
65
+ } from "../core/sections";
56
66
  import type { SyncResult } from "../ingestion";
57
67
  import type { DownloadPolicy } from "../llm/policy";
58
68
  import type {
@@ -67,10 +77,15 @@ import type { IndexStatus } from "../store/types";
67
77
  export type {
68
78
  AskResult,
69
79
  Config,
80
+ DocumentSection,
70
81
  DownloadPolicy,
71
82
  IndexStatus,
72
83
  SearchOptions,
73
84
  SearchResults,
85
+ SectionTargetCreateResult,
86
+ SectionTargetCreateSelector,
87
+ SectionTargetResolveResult,
88
+ SectionTargetV1,
74
89
  SyncResult,
75
90
  };
76
91
  export type { AskOptions, HybridSearchOptions } from "../pipeline/types";
@@ -251,6 +266,22 @@ export interface GnoMoveNoteOptions {
251
266
  name?: string;
252
267
  }
253
268
 
269
+ /** Canonical apply confirmation — preview first, then pass exact digest. */
270
+ export interface GnoFileRefactorApplyConfirmation {
271
+ schemaVersion: "1.0";
272
+ planDigest: string;
273
+ confirmation: "apply";
274
+ }
275
+
276
+ export interface GnoRenameNoteApplyOptions
277
+ extends GnoRenameNoteOptions, GnoFileRefactorApplyConfirmation {}
278
+
279
+ export interface GnoMoveNoteApplyOptions
280
+ extends GnoMoveNoteOptions, GnoFileRefactorApplyConfirmation {}
281
+
282
+ export type GnoFileRefactorPreviewPlan = FileRefactorPreviewPlan;
283
+ export type GnoFileRefactorApplyResult = FileRefactorApplyResult;
284
+
254
285
  export interface GnoDuplicateNoteOptions {
255
286
  ref: string;
256
287
  folderPath?: string;
@@ -339,11 +370,38 @@ export interface GnoClient {
339
370
  capture(options: GnoCaptureOptions): Promise<GnoCaptureResult>;
340
371
  createNote(options: GnoCreateNoteOptions): Promise<GnoCreateNoteResult>;
341
372
  createFolder(options: GnoCreateFolderOptions): Promise<GnoCreateFolderResult>;
342
- renameNote(options: GnoRenameNoteOptions): Promise<GnoRefactorNoteResult>;
343
- moveNote(options: GnoMoveNoteOptions): Promise<GnoRefactorNoteResult>;
373
+ previewRenameNote(
374
+ options: GnoRenameNoteOptions
375
+ ): Promise<GnoFileRefactorPreviewPlan>;
376
+ previewMoveNote(
377
+ options: GnoMoveNoteOptions
378
+ ): Promise<GnoFileRefactorPreviewPlan>;
379
+ renameNote(
380
+ options: GnoRenameNoteApplyOptions
381
+ ): Promise<GnoFileRefactorApplyResult>;
382
+ moveNote(
383
+ options: GnoMoveNoteApplyOptions
384
+ ): Promise<GnoFileRefactorApplyResult>;
344
385
  duplicateNote(
345
386
  options: GnoDuplicateNoteOptions
346
387
  ): Promise<GnoRefactorNoteResult>;
347
388
  getSections(ref: string): Promise<DocumentSection[]>;
389
+ /**
390
+ * Create a durable SectionTargetV1 for a heading in a stored document.
391
+ * Provide exactly one of `anchor` or `line`. Canonical URI comes from the
392
+ * resolved stored document, never from the caller.
393
+ */
394
+ createSectionTarget(
395
+ ref: string,
396
+ selector: SectionTargetCreateSelector
397
+ ): Promise<SectionTargetCreateResult>;
398
+ /**
399
+ * Conservatively resolve a SectionTargetV1 against a stored document.
400
+ * Navigable results include citation evidence; ambiguous/stale/missing do not.
401
+ */
402
+ resolveSectionTarget(
403
+ ref: string,
404
+ target: SectionTargetV1
405
+ ): Promise<SectionTargetResolveResult>;
348
406
  close(): Promise<void>;
349
407
  }