@gmickel/gno 1.24.0 → 1.26.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 (96) hide show
  1. package/README.md +18 -4
  2. package/assets/skill/SKILL.md +40 -17
  3. package/assets/skill/recipes/capture-and-file.md +20 -5
  4. package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.zip +0 -0
  5. package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.zip.sha256 +1 -0
  6. package/browser-extension/dist/PRIVACY.md +55 -0
  7. package/browser-extension/dist/chunk-vn5f663b.js +50 -0
  8. package/browser-extension/dist/chunk-ydfx5d7p.css +1 -0
  9. package/browser-extension/dist/content.js +1 -0
  10. package/browser-extension/dist/manifest.json +25 -0
  11. package/browser-extension/dist/preview.html +13 -0
  12. package/browser-extension/dist/service-worker.js +40 -0
  13. package/package.json +13 -3
  14. package/spec/cli.md +196 -4
  15. package/spec/db/schema.sql +102 -1
  16. package/spec/mcp.md +10 -0
  17. package/spec/output-schemas/browser-clip-preview.schema.json +83 -0
  18. package/spec/output-schemas/browser-clip.schema.json +586 -0
  19. package/spec/output-schemas/capture-receipt.schema.json +22 -1
  20. package/spec/output-schemas/clipper-csrf.schema.json +12 -0
  21. package/spec/output-schemas/clipper-error.schema.json +46 -0
  22. package/spec/output-schemas/clipper-pair-approval.schema.json +17 -0
  23. package/spec/output-schemas/clipper-pair-start.schema.json +26 -0
  24. package/spec/output-schemas/clipper-pair-status.schema.json +46 -0
  25. package/spec/output-schemas/clipper-revoke.schema.json +28 -0
  26. package/spec/output-schemas/mcp-capture-result.schema.json +12 -1
  27. package/spec/output-schemas/project-profile-apply.schema.json +209 -0
  28. package/spec/output-schemas/project-profile-command.schema.json +160 -0
  29. package/spec/output-schemas/query-diagnose.schema.json +7 -1
  30. package/spec/output-schemas/setup-profile-result.schema.json +87 -0
  31. package/spec/project-profile.schema.json +301 -0
  32. package/src/cli/commands/collection/add.ts +39 -45
  33. package/src/cli/commands/collection/remove.ts +28 -28
  34. package/src/cli/commands/collection/rename.ts +55 -73
  35. package/src/cli/commands/context/add.ts +37 -26
  36. package/src/cli/commands/context/rm.ts +47 -20
  37. package/src/cli/commands/init.ts +55 -125
  38. package/src/cli/commands/models/use.ts +43 -38
  39. package/src/cli/commands/profile-apply.ts +334 -0
  40. package/src/cli/commands/profile.ts +409 -0
  41. package/src/cli/commands/setup-activation.ts +205 -54
  42. package/src/cli/commands/setup-profile.ts +223 -0
  43. package/src/cli/commands/setup.ts +3 -0
  44. package/src/cli/program.ts +110 -9
  45. package/src/config/index.ts +3 -0
  46. package/src/config/project-profile.ts +367 -0
  47. package/src/config/saver.ts +16 -7
  48. package/src/config/types.ts +42 -0
  49. package/src/core/browser-clip-provenance.ts +139 -0
  50. package/src/core/browser-clip.ts +473 -0
  51. package/src/core/capture-write.ts +5 -0
  52. package/src/core/capture.ts +75 -18
  53. package/src/core/config-mutation.ts +138 -76
  54. package/src/core/config-write-lock.ts +89 -0
  55. package/src/core/context-identity.ts +16 -0
  56. package/src/core/context-resolver.ts +2 -12
  57. package/src/core/file-lock.ts +20 -6
  58. package/src/core/folder-setup-planning.ts +6 -21
  59. package/src/core/folder-setup.ts +30 -2
  60. package/src/core/path-rules.ts +53 -0
  61. package/src/core/project-affinity-surface.ts +102 -7
  62. package/src/core/project-profile-apply-state.ts +268 -0
  63. package/src/core/project-profile-apply-validation.ts +95 -0
  64. package/src/core/project-profile-apply.ts +408 -0
  65. package/src/core/project-profile-canonical.ts +71 -0
  66. package/src/core/project-profile-diff.ts +302 -0
  67. package/src/core/project-profile-discovery.ts +519 -0
  68. package/src/core/project-profile-file.ts +37 -0
  69. package/src/core/project-profile-parser.ts +98 -0
  70. package/src/core/project-profile.ts +490 -0
  71. package/src/ingestion/walker.ts +85 -44
  72. package/src/llm/cache.ts +21 -0
  73. package/src/serve/capture-service.ts +420 -0
  74. package/src/serve/clipper-body.ts +62 -0
  75. package/src/serve/clipper-capture.ts +248 -0
  76. package/src/serve/clipper-contract.ts +57 -0
  77. package/src/serve/clipper-idempotency.ts +35 -0
  78. package/src/serve/clipper-pairing.ts +297 -0
  79. package/src/serve/clipper-security-errors.ts +23 -0
  80. package/src/serve/clipper-security.ts +449 -0
  81. package/src/serve/config-sync.ts +2 -2
  82. package/src/serve/public/app.tsx +8 -1
  83. package/src/serve/public/globals.built.css +1 -1
  84. package/src/serve/public/index.html +1 -0
  85. package/src/serve/public/lib/clipper-approval.ts +206 -0
  86. package/src/serve/public/pages/ClipperPairing.tsx +210 -0
  87. package/src/serve/resident-runtime.ts +1 -0
  88. package/src/serve/routes/api.ts +20 -115
  89. package/src/serve/routes/clipper.ts +394 -0
  90. package/src/serve/server.ts +22 -0
  91. package/src/store/migrations/020-browser-clipper-security.ts +128 -0
  92. package/src/store/migrations/021-multi-context-identity.ts +37 -0
  93. package/src/store/migrations/index.ts +4 -0
  94. package/src/store/sqlite/adapter.ts +83 -0
  95. package/src/store/sqlite/clipper-store-types.ts +104 -0
  96. package/src/store/sqlite/clipper-store.ts +496 -0
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Mutating project-profile CLI composition.
3
+ *
4
+ * @module src/cli/commands/profile-apply
5
+ */
6
+
7
+ // node:fs/promises provides recursive directory creation; Bun has no equivalent structural API.
8
+ import { mkdir } from "node:fs/promises";
9
+
10
+ import type { ProjectProfileApplyReceipt } from "../../core/project-profile-apply";
11
+ import type {
12
+ ProjectProfileDiscoveryDependencies,
13
+ ProjectProfileDiscoverySummary,
14
+ } from "../../core/project-profile-discovery";
15
+ import type { SqliteAdapter as SqliteAdapterType } from "../../store/sqlite/adapter";
16
+ import type { ProjectProfileCommandDiagnostic } from "./profile";
17
+
18
+ import { getIndexDbPath } from "../../app/constants";
19
+ import {
20
+ createDefaultConfig,
21
+ getConfigPaths,
22
+ loadConfig,
23
+ toAbsolutePath,
24
+ } from "../../config";
25
+ import {
26
+ applyProjectProfile,
27
+ validateProjectProfileRuntimePaths,
28
+ } from "../../core/project-profile-apply";
29
+ import { discoverProjectProfile } from "../../core/project-profile-discovery";
30
+ import {
31
+ ProjectProfileFileError,
32
+ readProjectProfileFile,
33
+ } from "../../core/project-profile-file";
34
+ import { SqliteAdapter } from "../../store/sqlite/adapter";
35
+ import {
36
+ compilerDiagnostic,
37
+ discoveryDiagnostic,
38
+ PROJECT_PROFILE_COMMAND_SCHEMA_VERSION,
39
+ sortDiagnostics,
40
+ } from "./profile";
41
+
42
+ export interface ProjectProfileApplyCommandResult {
43
+ schemaVersion: typeof PROJECT_PROFILE_COMMAND_SCHEMA_VERSION;
44
+ command: "apply";
45
+ status:
46
+ | "applied"
47
+ | "unchanged"
48
+ | "invalid"
49
+ | "not_found"
50
+ | "disabled"
51
+ | "failed";
52
+ applied: boolean;
53
+ discovery: ProjectProfileDiscoverySummary;
54
+ receipt: ProjectProfileApplyReceipt | null;
55
+ diagnostics: ProjectProfileCommandDiagnostic[];
56
+ }
57
+
58
+ export interface ProjectProfileApplyCommandOutcome {
59
+ result: ProjectProfileApplyCommandResult;
60
+ exitCode: 0 | 1 | 2;
61
+ }
62
+
63
+ export interface ProjectProfileApplyCommandOptions {
64
+ path?: string;
65
+ cwd?: string;
66
+ channel?: "local" | "remote";
67
+ configPath?: string;
68
+ dataDir?: string;
69
+ indexName?: string;
70
+ discoveryDependencies?: Partial<ProjectProfileDiscoveryDependencies>;
71
+ readProfileFn?: (path: string) => Promise<string>;
72
+ createStore?: () => SqliteAdapterType;
73
+ }
74
+
75
+ const failedApplyResult = (
76
+ status: "disabled" | "failed" | "invalid" | "not_found",
77
+ discovery: ProjectProfileDiscoverySummary,
78
+ diagnostics: ProjectProfileCommandDiagnostic[]
79
+ ): ProjectProfileApplyCommandResult => ({
80
+ schemaVersion: PROJECT_PROFILE_COMMAND_SCHEMA_VERSION,
81
+ command: "apply",
82
+ status,
83
+ applied: false,
84
+ discovery,
85
+ receipt: null,
86
+ diagnostics: sortDiagnostics(diagnostics),
87
+ });
88
+
89
+ /**
90
+ * Apply one trusted local profile through the same discovery/compiler/diff
91
+ * pipeline used by the read commands.
92
+ */
93
+ export async function runProjectProfileApplyCommand(
94
+ options: ProjectProfileApplyCommandOptions
95
+ ): Promise<ProjectProfileApplyCommandOutcome> {
96
+ const discovery = await discoverProjectProfile(
97
+ {
98
+ channel: options.channel ?? "local",
99
+ cwd: options.cwd,
100
+ rootOverride: options.path,
101
+ },
102
+ options.discoveryDependencies
103
+ );
104
+ const discoveryDiagnostics = discovery.diagnostics.map(discoveryDiagnostic);
105
+ if (discovery.summary.status !== "found") {
106
+ const status =
107
+ discovery.summary.status === "not_found"
108
+ ? "not_found"
109
+ : discovery.summary.status === "disabled"
110
+ ? "disabled"
111
+ : "invalid";
112
+ return {
113
+ result: failedApplyResult(
114
+ status,
115
+ discovery.summary,
116
+ discoveryDiagnostics
117
+ ),
118
+ exitCode: discoveryDiagnostics.some(
119
+ (diagnostic) => diagnostic.code === "PROFILE_DISCOVERY_FAILED"
120
+ )
121
+ ? 2
122
+ : 1,
123
+ };
124
+ }
125
+ if (!(discovery.profilePath && discovery.profileRoot)) {
126
+ return {
127
+ result: failedApplyResult("failed", discovery.summary, [
128
+ ...discoveryDiagnostics,
129
+ {
130
+ code: "PROFILE_DISCOVERY_FAILED",
131
+ severity: "error",
132
+ path: ".gno/index.yml",
133
+ message: "Discovery returned no trusted local profile identity.",
134
+ remediation: "Rerun with an exact readable project root.",
135
+ },
136
+ ]),
137
+ exitCode: 2,
138
+ };
139
+ }
140
+
141
+ let profileYaml: string;
142
+ try {
143
+ profileYaml = await (options.readProfileFn ?? readProjectProfileFile)(
144
+ discovery.profilePath
145
+ );
146
+ } catch (error) {
147
+ const invalidFile = error instanceof ProjectProfileFileError ? error : null;
148
+ return {
149
+ result: failedApplyResult("failed", discovery.summary, [
150
+ ...discoveryDiagnostics,
151
+ {
152
+ code: invalidFile?.code ?? "PROFILE_READ_FAILED",
153
+ severity: "error",
154
+ path: ".gno/index.yml",
155
+ message:
156
+ invalidFile?.message ??
157
+ "The selected project profile could not be read.",
158
+ remediation: invalidFile
159
+ ? "Reduce or repair .gno/index.yml and retry."
160
+ : "Repair local file permissions and retry.",
161
+ },
162
+ ]),
163
+ exitCode: invalidFile ? 1 : 2,
164
+ };
165
+ }
166
+
167
+ const paths = getConfigPaths();
168
+ const configPath = toAbsolutePath(options.configPath ?? paths.configFile);
169
+ const dataDir = toAbsolutePath(options.dataDir ?? paths.dataDir);
170
+ const indexName = options.indexName ?? "default";
171
+ const indexPath = getIndexDbPath(indexName, {
172
+ config: paths.configDir,
173
+ data: dataDir,
174
+ cache: paths.cacheDir,
175
+ });
176
+ const overlap = await validateProjectProfileRuntimePaths(
177
+ discovery.profileRoot,
178
+ [
179
+ { label: "Config", path: configPath },
180
+ { label: "User data directory", path: dataDir },
181
+ { label: "Model cache directory", path: paths.cacheDir },
182
+ { label: "Index database", path: indexPath },
183
+ ]
184
+ );
185
+ if (overlap) {
186
+ return {
187
+ result: failedApplyResult("invalid", discovery.summary, [
188
+ ...discoveryDiagnostics,
189
+ {
190
+ code: overlap.code,
191
+ severity: "error",
192
+ path: "runtime",
193
+ message: overlap.message,
194
+ remediation: overlap.remediation,
195
+ },
196
+ ]),
197
+ exitCode: 1,
198
+ };
199
+ }
200
+ const loaded = await loadConfig(configPath);
201
+ if (!loaded.ok && loaded.error.code !== "NOT_FOUND") {
202
+ return {
203
+ result: failedApplyResult("invalid", discovery.summary, [
204
+ ...discoveryDiagnostics,
205
+ {
206
+ code: "CONFIG_INVALID",
207
+ severity: "error",
208
+ path: "config",
209
+ message: "The selected local GNO config could not be loaded safely.",
210
+ remediation: "Repair the local config and rerun gno profile apply.",
211
+ },
212
+ ]),
213
+ exitCode: 1,
214
+ };
215
+ }
216
+ const startingConfig = loaded.ok ? loaded.value : createDefaultConfig();
217
+ const store = options.createStore?.() ?? new SqliteAdapter();
218
+ try {
219
+ await mkdir(dataDir, { recursive: true });
220
+ store.setConfigPath(configPath);
221
+ const opened = await store.open(indexPath, startingConfig.ftsTokenizer);
222
+ if (!opened.ok) {
223
+ return {
224
+ result: failedApplyResult("failed", discovery.summary, [
225
+ ...discoveryDiagnostics,
226
+ {
227
+ code: "INDEX_OPEN_FAILED",
228
+ severity: "error",
229
+ path: "index",
230
+ message: "The selected local index could not be opened.",
231
+ remediation: "Repair the user data directory and rerun apply.",
232
+ },
233
+ ]),
234
+ exitCode: 2,
235
+ };
236
+ }
237
+ const applied = await applyProjectProfile({
238
+ profileYaml,
239
+ profileRoot: discovery.profileRoot,
240
+ profilePath: discovery.profilePath,
241
+ configPath,
242
+ dataDir,
243
+ store,
244
+ runtimePaths: [
245
+ { label: "Model cache directory", path: paths.cacheDir },
246
+ { label: "Index database", path: indexPath },
247
+ ],
248
+ });
249
+ if (!applied.ok) {
250
+ const diagnostics = [
251
+ ...discoveryDiagnostics,
252
+ ...(applied.error.diagnostics ?? []).map(compilerDiagnostic),
253
+ {
254
+ code: applied.error.code,
255
+ severity: "error" as const,
256
+ path:
257
+ applied.error.code === "PROFILE_INVALID"
258
+ ? ".gno/index.yml"
259
+ : "runtime",
260
+ message: applied.error.message,
261
+ remediation: applied.error.remediation,
262
+ },
263
+ ];
264
+ const validationFailure =
265
+ applied.error.code === "PROFILE_INVALID" ||
266
+ applied.error.code === "RUNTIME_PATH_OVERLAP";
267
+ return {
268
+ result: failedApplyResult(
269
+ validationFailure ? "invalid" : "failed",
270
+ discovery.summary,
271
+ diagnostics
272
+ ),
273
+ exitCode: validationFailure ? 1 : 2,
274
+ };
275
+ }
276
+ return {
277
+ result: {
278
+ schemaVersion: PROJECT_PROFILE_COMMAND_SCHEMA_VERSION,
279
+ command: "apply",
280
+ status: applied.receipt.status,
281
+ applied: applied.receipt.status === "applied",
282
+ discovery: discovery.summary,
283
+ receipt: applied.receipt,
284
+ diagnostics: sortDiagnostics([
285
+ ...discoveryDiagnostics,
286
+ ...applied.receipt.diagnostics,
287
+ ]),
288
+ },
289
+ exitCode: 0,
290
+ };
291
+ } catch {
292
+ return {
293
+ result: failedApplyResult("failed", discovery.summary, [
294
+ ...discoveryDiagnostics,
295
+ {
296
+ code: "RUNTIME_IO_FAILED",
297
+ severity: "error",
298
+ path: "runtime",
299
+ message: "The selected runtime directories could not be prepared.",
300
+ remediation:
301
+ "Repair user config/data-directory permissions and rerun apply.",
302
+ },
303
+ ]),
304
+ exitCode: 2,
305
+ };
306
+ } finally {
307
+ await store.close();
308
+ }
309
+ }
310
+
311
+ export function formatProjectProfileApplyResult(
312
+ result: ProjectProfileApplyCommandResult,
313
+ options: { json: boolean }
314
+ ): string {
315
+ if (options.json) return JSON.stringify(result, null, 2);
316
+ const lines = [
317
+ `Profile apply ${result.status}: ${result.discovery.profile ?? "(not found)"}`,
318
+ ];
319
+ if (result.receipt) {
320
+ lines.push(`Fingerprint: ${result.receipt.profile.fingerprint}`);
321
+ lines.push(`Diff: ${result.receipt.diff.status}`);
322
+ for (const resource of result.receipt.resources) {
323
+ lines.push(
324
+ ` ${resource.disposition} ${resource.kind} ${resource.id}${resource.pendingIndexing ? " (indexing pending)" : ""}`
325
+ );
326
+ }
327
+ }
328
+ for (const issue of result.diagnostics) {
329
+ lines.push(
330
+ `${issue.severity === "error" ? "Error" : "Warning"} ${issue.code}: ${issue.message} ${issue.remediation}`
331
+ );
332
+ }
333
+ return lines.join("\n");
334
+ }