@gmickel/gno 2.5.1 → 2.6.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 (105) hide show
  1. package/README.md +60 -5
  2. package/assets/skill/README.md +5 -1
  3. package/assets/skill/SKILL.md +75 -1
  4. package/assets/skill/cli-reference.md +123 -0
  5. package/assets/skill/examples.md +30 -0
  6. package/assets/skill/mcp-reference.md +52 -0
  7. package/assets/skill/recipes/capture-and-file.md +6 -0
  8. package/assets/skill/recipes/memory-file-decision.md +6 -0
  9. package/assets/skill/recipes/memory-supersede-fact.md +5 -0
  10. package/assets/skill/recipes/session-evidence-lookup.md +98 -0
  11. package/assets/spa-production.json.gz +0 -0
  12. package/browser-extension/artifacts/{gno-browser-clipper-v2.5.1.zip → gno-browser-clipper-v2.6.0.zip} +0 -0
  13. package/browser-extension/artifacts/gno-browser-clipper-v2.6.0.zip.sha256 +1 -0
  14. package/browser-extension/dist/manifest.json +1 -1
  15. package/package.json +2 -1
  16. package/spec/cli.md +347 -24
  17. package/spec/mcp.md +198 -2
  18. package/spec/output-schemas/capture-receipt.schema.json +3 -0
  19. package/spec/output-schemas/mcp-capture-result.schema.json +3 -0
  20. package/spec/output-schemas/memory-remember.schema.json +8 -2
  21. package/spec/output-schemas/request-status.schema.json +113 -0
  22. package/spec/output-schemas/sessions-automation-run.schema.json +46 -0
  23. package/spec/output-schemas/sessions-discovery.schema.json +38 -0
  24. package/spec/output-schemas/sessions-import-receipt.schema.json +156 -0
  25. package/spec/output-schemas/sessions-status.schema.json +432 -0
  26. package/src/cli/commands/ask.ts +14 -2
  27. package/src/cli/commands/capture.ts +55 -96
  28. package/src/cli/commands/daemon.ts +41 -0
  29. package/src/cli/commands/ls.ts +3 -0
  30. package/src/cli/commands/memory.ts +12 -3
  31. package/src/cli/commands/request-status.ts +59 -0
  32. package/src/cli/commands/reset.ts +39 -5
  33. package/src/cli/commands/sessions.ts +713 -0
  34. package/src/cli/commands/shared.ts +14 -1
  35. package/src/cli/program.ts +388 -1
  36. package/src/cli/session-binding.ts +49 -0
  37. package/src/config/types.ts +8 -0
  38. package/src/core/capture-publish.ts +239 -0
  39. package/src/core/capture-sync.ts +3 -0
  40. package/src/core/memory-remember.ts +233 -122
  41. package/src/core/memory-types.ts +11 -0
  42. package/src/core/network-boundary-inventory.ts +8 -0
  43. package/src/core/request-receipts.ts +671 -0
  44. package/src/index.ts +9 -0
  45. package/src/mcp/context.ts +8 -0
  46. package/src/mcp/http-egress.ts +4 -0
  47. package/src/mcp/http-transport.ts +2 -0
  48. package/src/mcp/tools/capture.ts +87 -83
  49. package/src/mcp/tools/index.ts +66 -0
  50. package/src/mcp/tools/memory-remember.ts +7 -0
  51. package/src/mcp/tools/memory-shared.ts +7 -1
  52. package/src/mcp/tools/request-status.ts +73 -0
  53. package/src/mcp/tools/sessions.ts +208 -0
  54. package/src/sdk/client.ts +180 -84
  55. package/src/sdk/index.ts +6 -0
  56. package/src/sdk/types.ts +54 -2
  57. package/src/serve/capture-service.ts +98 -32
  58. package/src/serve/config-sync.ts +3 -2
  59. package/src/serve/public/app.tsx +4 -1
  60. package/src/serve/public/components/CaptureModal.tsx +26 -8
  61. package/src/serve/public/components/sessions/AutomationPanel.tsx +800 -0
  62. package/src/serve/public/components/sessions/ImportReceipt.tsx +238 -0
  63. package/src/serve/public/components/sessions/SessionSearch.tsx +286 -0
  64. package/src/serve/public/components/sessions/SourcesPanel.tsx +541 -0
  65. package/src/serve/public/components/sessions/api.ts +40 -0
  66. package/src/serve/public/components/sessions/snippet.tsx +53 -0
  67. package/src/serve/public/globals.built.css +1 -1
  68. package/src/serve/public/hooks/use-api.ts +10 -2
  69. package/src/serve/public/lib/request-intent.ts +69 -0
  70. package/src/serve/public/lib/workspace-actions.ts +12 -1
  71. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  72. package/src/serve/public/pages/Dashboard.tsx +10 -0
  73. package/src/serve/public/pages/DocView.tsx +15 -1
  74. package/src/serve/public/pages/DocumentEditor.tsx +139 -96
  75. package/src/serve/public/pages/Sessions.tsx +350 -0
  76. package/src/serve/resident-runtime.ts +43 -3
  77. package/src/serve/routes/api.ts +476 -147
  78. package/src/serve/routes/sessions.ts +766 -0
  79. package/src/serve/security.ts +9 -0
  80. package/src/serve/server.ts +205 -1
  81. package/src/serve/session-automation.ts +146 -0
  82. package/src/sessions/archive.ts +348 -0
  83. package/src/sessions/automation-state.ts +444 -0
  84. package/src/sessions/automation-status.ts +239 -0
  85. package/src/sessions/automation.ts +1169 -0
  86. package/src/sessions/binding.ts +105 -0
  87. package/src/sessions/claude-hook.ts +240 -0
  88. package/src/sessions/config.ts +176 -0
  89. package/src/sessions/format.ts +191 -0
  90. package/src/sessions/import-child-env.ts +8 -0
  91. package/src/sessions/import-child.ts +152 -0
  92. package/src/sessions/parsers/claude-code.ts +259 -0
  93. package/src/sessions/parsers/codex.ts +303 -0
  94. package/src/sessions/parsers/hermes.ts +248 -0
  95. package/src/sessions/parsers/openclaw.ts +496 -0
  96. package/src/sessions/parsers/shared.ts +184 -0
  97. package/src/sessions/sanitize.ts +222 -0
  98. package/src/sessions/service.ts +1533 -0
  99. package/src/sessions/setup.ts +477 -0
  100. package/src/sessions/sources.ts +518 -0
  101. package/src/sessions/state.ts +118 -0
  102. package/src/sessions/types.ts +457 -0
  103. package/src/store/sqlite/adapter.ts +54 -15
  104. package/src/store/sqlite/scoped-index.ts +9 -0
  105. package/browser-extension/artifacts/gno-browser-clipper-v2.5.1.zip.sha256 +0 -1
@@ -0,0 +1,432 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gno://schemas/sessions-status@1.0",
4
+ "title": "GNO Session Archive Status",
5
+ "description": "Shared status of a session archive on every surface (CLI `gno sessions status --json`, MCP gno_sessions_status, REST GET /api/sessions/status, SDK client.sessionsStatus()). Derived from SessionsStatus in src/sessions/types.ts. Includes opt-in automation state. Contains no host paths and no session content.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schemaVersion",
10
+ "configured",
11
+ "index",
12
+ "collections",
13
+ "sources",
14
+ "automation",
15
+ "warnings"
16
+ ],
17
+ "properties": {
18
+ "schemaVersion": {
19
+ "const": "1"
20
+ },
21
+ "configured": {
22
+ "const": true
23
+ },
24
+ "index": {
25
+ "type": "string",
26
+ "minLength": 1
27
+ },
28
+ "collections": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["name", "threads"],
34
+ "properties": {
35
+ "name": {
36
+ "type": "string"
37
+ },
38
+ "threads": {
39
+ "type": "integer",
40
+ "minimum": 0
41
+ }
42
+ }
43
+ }
44
+ },
45
+ "sources": {
46
+ "type": "array",
47
+ "items": {
48
+ "type": "object",
49
+ "additionalProperties": false,
50
+ "required": [
51
+ "id",
52
+ "harness",
53
+ "collection",
54
+ "available",
55
+ "units",
56
+ "archivedThreads",
57
+ "staleParser",
58
+ "sourceUnavailable",
59
+ "lastImportAt"
60
+ ],
61
+ "properties": {
62
+ "id": { "type": "string" },
63
+ "harness": { "enum": ["codex", "claude-code", "openclaw", "hermes"] },
64
+ "collection": { "type": "string" },
65
+ "available": {
66
+ "type": "boolean",
67
+ "description": "False when the source root is missing or cannot be listed."
68
+ },
69
+ "units": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": [
73
+ "total",
74
+ "complete",
75
+ "incomplete",
76
+ "failed",
77
+ "pending"
78
+ ],
79
+ "properties": {
80
+ "total": {
81
+ "type": "integer",
82
+ "minimum": 0
83
+ },
84
+ "complete": {
85
+ "type": "integer",
86
+ "minimum": 0
87
+ },
88
+ "incomplete": {
89
+ "type": "integer",
90
+ "minimum": 0
91
+ },
92
+ "failed": {
93
+ "type": "integer",
94
+ "minimum": 0
95
+ },
96
+ "pending": {
97
+ "type": "integer",
98
+ "minimum": 0
99
+ }
100
+ }
101
+ },
102
+ "archivedThreads": {
103
+ "type": "integer",
104
+ "minimum": 0
105
+ },
106
+ "staleParser": {
107
+ "type": "integer",
108
+ "minimum": 0,
109
+ "description": "Archived units produced by an older parser whose source is no longer available to reparse."
110
+ },
111
+ "sourceUnavailable": {
112
+ "type": "integer",
113
+ "minimum": 0,
114
+ "description": "Archived units whose source no longer exists; their archive is retained until an explicit prune."
115
+ },
116
+ "lastImportAt": {
117
+ "type": ["string", "null"],
118
+ "format": "date-time"
119
+ }
120
+ }
121
+ }
122
+ },
123
+ "automation": {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "required": ["daemon", "timezone", "profiles"],
127
+ "properties": {
128
+ "daemon": {
129
+ "type": "object",
130
+ "additionalProperties": false,
131
+ "required": ["state", "heartbeatAt"],
132
+ "properties": {
133
+ "state": {
134
+ "enum": ["running", "not_running", "stale"],
135
+ "description": "running only with a fresh heartbeat from a live daemon on this archive; serve never counts."
136
+ },
137
+ "heartbeatAt": {
138
+ "type": ["string", "null"],
139
+ "format": "date-time"
140
+ }
141
+ }
142
+ },
143
+ "timezone": {
144
+ "type": "string",
145
+ "description": "IANA timezone for human-readable times; stored instants are UTC."
146
+ },
147
+ "profiles": {
148
+ "type": "array",
149
+ "items": {
150
+ "type": "object",
151
+ "additionalProperties": false,
152
+ "required": [
153
+ "id",
154
+ "sources",
155
+ "collections",
156
+ "state",
157
+ "hook",
158
+ "schedule",
159
+ "limit",
160
+ "retries",
161
+ "pending",
162
+ "running",
163
+ "lastTrigger",
164
+ "lastRun",
165
+ "lastSuccessAt",
166
+ "retryAt",
167
+ "recovery"
168
+ ],
169
+ "properties": {
170
+ "id": {
171
+ "type": "string"
172
+ },
173
+ "sources": {
174
+ "type": "array",
175
+ "items": {
176
+ "type": "string"
177
+ }
178
+ },
179
+ "collections": {
180
+ "type": "array",
181
+ "items": {
182
+ "type": "string"
183
+ }
184
+ },
185
+ "state": {
186
+ "enum": [
187
+ "off",
188
+ "idle",
189
+ "pending",
190
+ "running",
191
+ "retrying",
192
+ "partial",
193
+ "failed"
194
+ ],
195
+ "description": "off: no trigger enabled and nothing pending. partial/failed describe the last run."
196
+ },
197
+ "hook": {
198
+ "oneOf": [
199
+ {
200
+ "type": "null"
201
+ },
202
+ {
203
+ "type": "object",
204
+ "additionalProperties": false,
205
+ "required": ["harness", "enabled", "installed"],
206
+ "properties": {
207
+ "harness": {
208
+ "enum": ["claude-code"]
209
+ },
210
+ "enabled": {
211
+ "type": "boolean"
212
+ },
213
+ "installed": {
214
+ "type": ["boolean", "null"],
215
+ "description": "Owned entry present in the host settings file; null when the file cannot be read."
216
+ }
217
+ }
218
+ }
219
+ ]
220
+ },
221
+ "schedule": {
222
+ "oneOf": [
223
+ {
224
+ "type": "null"
225
+ },
226
+ {
227
+ "type": "object",
228
+ "additionalProperties": false,
229
+ "required": ["enabled", "cadence", "nextDueAt"],
230
+ "properties": {
231
+ "enabled": {
232
+ "type": "boolean"
233
+ },
234
+ "cadence": {
235
+ "type": "string"
236
+ },
237
+ "nextDueAt": {
238
+ "type": ["string", "null"],
239
+ "format": "date-time",
240
+ "description": "Null unless enabled and a daemon is running on this archive."
241
+ }
242
+ }
243
+ }
244
+ ]
245
+ },
246
+ "limit": {
247
+ "type": "integer",
248
+ "minimum": 1
249
+ },
250
+ "retries": {
251
+ "type": "integer",
252
+ "minimum": 0
253
+ },
254
+ "pending": {
255
+ "oneOf": [
256
+ {
257
+ "type": "null"
258
+ },
259
+ {
260
+ "type": "object",
261
+ "additionalProperties": false,
262
+ "required": ["since", "triggers"],
263
+ "properties": {
264
+ "since": {
265
+ "type": "string",
266
+ "format": "date-time"
267
+ },
268
+ "triggers": {
269
+ "type": "array",
270
+ "items": {
271
+ "enum": ["hook", "schedule", "manual"]
272
+ }
273
+ }
274
+ }
275
+ }
276
+ ]
277
+ },
278
+ "running": {
279
+ "oneOf": [
280
+ {
281
+ "type": "null"
282
+ },
283
+ {
284
+ "type": "object",
285
+ "additionalProperties": false,
286
+ "required": ["startedAt", "triggers"],
287
+ "properties": {
288
+ "startedAt": {
289
+ "type": "string",
290
+ "format": "date-time"
291
+ },
292
+ "triggers": {
293
+ "type": "array",
294
+ "items": {
295
+ "enum": ["hook", "schedule", "manual"]
296
+ }
297
+ }
298
+ }
299
+ }
300
+ ]
301
+ },
302
+ "lastTrigger": {
303
+ "oneOf": [
304
+ {
305
+ "type": "null"
306
+ },
307
+ {
308
+ "type": "object",
309
+ "additionalProperties": false,
310
+ "required": ["kind", "at"],
311
+ "properties": {
312
+ "kind": {
313
+ "enum": ["hook", "schedule", "manual"]
314
+ },
315
+ "at": {
316
+ "type": "string",
317
+ "format": "date-time"
318
+ }
319
+ }
320
+ }
321
+ ]
322
+ },
323
+ "lastRun": {
324
+ "oneOf": [
325
+ {
326
+ "type": "null"
327
+ },
328
+ {
329
+ "$ref": "#/definitions/runRecord"
330
+ }
331
+ ]
332
+ },
333
+ "lastSuccessAt": {
334
+ "type": ["string", "null"],
335
+ "format": "date-time"
336
+ },
337
+ "retryAt": {
338
+ "type": ["string", "null"],
339
+ "format": "date-time"
340
+ },
341
+ "recovery": {
342
+ "type": ["string", "null"]
343
+ }
344
+ }
345
+ }
346
+ }
347
+ }
348
+ },
349
+ "warnings": {
350
+ "type": "array",
351
+ "items": {
352
+ "type": "string"
353
+ }
354
+ }
355
+ },
356
+ "definitions": {
357
+ "runRecord": {
358
+ "type": "object",
359
+ "additionalProperties": false,
360
+ "required": [
361
+ "triggers",
362
+ "startedAt",
363
+ "finishedAt",
364
+ "outcome",
365
+ "reason",
366
+ "threads",
367
+ "units"
368
+ ],
369
+ "properties": {
370
+ "triggers": {
371
+ "type": "array",
372
+ "items": {
373
+ "enum": ["hook", "schedule", "manual"]
374
+ }
375
+ },
376
+ "startedAt": {
377
+ "type": "string",
378
+ "format": "date-time"
379
+ },
380
+ "finishedAt": {
381
+ "type": "string",
382
+ "format": "date-time"
383
+ },
384
+ "outcome": {
385
+ "enum": ["complete", "up_to_date", "partial", "failed"]
386
+ },
387
+ "reason": {
388
+ "type": ["string", "null"],
389
+ "description": "Stable reason code for partial or failed runs, e.g. busy, runtime_error, source_revoked, source_unavailable, invalid_configuration, import_failed, interrupted, deferred, incomplete_units, index_sync_failed, unit_failures."
390
+ },
391
+ "threads": {
392
+ "type": "object",
393
+ "additionalProperties": false,
394
+ "required": ["imported", "updated", "unchanged"],
395
+ "properties": {
396
+ "imported": {
397
+ "type": "integer",
398
+ "minimum": 0
399
+ },
400
+ "updated": {
401
+ "type": "integer",
402
+ "minimum": 0
403
+ },
404
+ "unchanged": {
405
+ "type": "integer",
406
+ "minimum": 0
407
+ }
408
+ }
409
+ },
410
+ "units": {
411
+ "type": "object",
412
+ "additionalProperties": false,
413
+ "required": ["incomplete", "failed", "deferred"],
414
+ "properties": {
415
+ "incomplete": {
416
+ "type": "integer",
417
+ "minimum": 0
418
+ },
419
+ "failed": {
420
+ "type": "integer",
421
+ "minimum": 0
422
+ },
423
+ "deferred": {
424
+ "type": "integer",
425
+ "minimum": 0
426
+ }
427
+ }
428
+ }
429
+ }
430
+ }
431
+ }
432
+ }
@@ -15,6 +15,7 @@ import type {
15
15
  } from "../../llm/types";
16
16
  import type { AskOptions, AskResult, Citation } from "../../pipeline/types";
17
17
 
18
+ import { decorateUriForIndex } from "../../app/constants";
18
19
  import { buildVerifiedAsk } from "../../app/verified-ask";
19
20
  import { resolveCliProjectAffinity } from "../../core/project-affinity-surface";
20
21
  import {
@@ -96,7 +97,7 @@ export async function ask(
96
97
  const globals = getGlobals();
97
98
 
98
99
  const initResult = await initStore({
99
- configPath: options.configPath,
100
+ configPath: options.configPath ?? globals.config,
100
101
  indexName: globals.index,
101
102
  collection: options.collection,
102
103
  syncConfig: false,
@@ -434,9 +435,20 @@ export async function ask(
434
435
  return { success: false, error: finalized.error.message };
435
436
  }
436
437
  }
438
+ // URIs carry `?index=` like search and Capsule results on a named index.
437
439
  return {
438
440
  success: true,
439
- data: askResult,
441
+ data: {
442
+ ...askResult,
443
+ results: askResult.results.map((result) => ({
444
+ ...result,
445
+ uri: decorateUriForIndex(result.uri, globals.index),
446
+ })),
447
+ citations: askResult.citations?.map((citation) => ({
448
+ ...citation,
449
+ uri: decorateUriForIndex(citation.uri, globals.index),
450
+ })),
451
+ },
440
452
  metadata: traceSession?.metadata(),
441
453
  };
442
454
  } catch (cause) {
@@ -4,30 +4,28 @@
4
4
  * @module src/cli/commands/capture
5
5
  */
6
6
 
7
- // node:fs/promises for mkdir (no Bun equivalent for recursive dir creation)
8
- import { mkdir } from "node:fs/promises";
9
- // node:path has no Bun path utilities
10
- import { dirname, join } from "node:path";
11
-
12
7
  import type {
13
- CapturePlan,
8
+ CaptureInput,
14
9
  CaptureReceipt,
15
10
  CaptureSourceKind,
16
11
  } from "../../core/capture";
17
12
  import type { NoteCollisionPolicy } from "../../core/note-creation";
18
13
  import type { NotePresetId } from "../../core/note-presets";
14
+ import type { RequestReceiptInfo } from "../../core/request-receipts";
19
15
 
20
16
  import { getIndexDbPath } from "../../app/constants";
21
17
  import {
22
- buildCaptureReceipt,
23
18
  listCaptureDiskRelPaths,
24
19
  planCapture,
25
20
  serializeCaptureReceipt,
26
21
  } from "../../core/capture";
27
- import { writeCapturePlanFile } from "../../core/capture-write";
28
- import { withWriteLock } from "../../core/file-lock";
29
- import { defaultSyncService, withContentTypeRules } from "../../ingestion";
22
+ import { publishCapture } from "../../core/capture-publish";
23
+ import {
24
+ formatRequestReceiptLine,
25
+ localRequestLedger,
26
+ } from "../../core/request-receipts";
30
27
  import { CliError } from "../errors";
28
+ import { requestErrorToCli } from "./request-status";
31
29
  import { initStore } from "./shared";
32
30
 
33
31
  export interface CaptureCliOptions {
@@ -49,6 +47,7 @@ export interface CaptureCliOptions {
49
47
  sourceAuthor?: string;
50
48
  sourceDate?: string;
51
49
  sourceId?: string;
50
+ requestId?: string;
52
51
  }
53
52
 
54
53
  const SOURCE_KINDS = new Set<CaptureSourceKind>([
@@ -130,7 +129,7 @@ function validateCollisionPolicy(
130
129
 
131
130
  export async function capture(
132
131
  options: CaptureCliOptions
133
- ): Promise<CaptureReceipt> {
132
+ ): Promise<CaptureReceipt & { request?: RequestReceiptInfo }> {
134
133
  const storeInit = await initStore({
135
134
  configPath: options.configPath,
136
135
  indexName: options.indexName,
@@ -160,102 +159,61 @@ export async function capture(
160
159
 
161
160
  validateCollisionPolicy(options.collisionPolicy);
162
161
  const content = await readContent(options);
163
- const existingDocs = await store.listDocuments(collection.name);
164
- if (!existingDocs.ok) {
165
- throw new Error(existingDocs.error.message);
166
- }
167
- const diskRelPaths = await listCaptureDiskRelPaths(collection.path);
168
- let plan: CapturePlan;
162
+ const input: CaptureInput = {
163
+ collection: collection.name,
164
+ content,
165
+ title: options.title,
166
+ relPath: options.path,
167
+ folderPath: options.folder,
168
+ collisionPolicy: options.collisionPolicy,
169
+ presetId: options.preset,
170
+ tags: parseTags(options.tags),
171
+ source: buildSource(options),
172
+ };
173
+ const ledger = localRequestLedger(getIndexDbPath(options.indexName));
169
174
  try {
170
- plan = planCapture({
171
- input: {
172
- collection: collection.name,
173
- content,
174
- title: options.title,
175
- relPath: options.path,
176
- folderPath: options.folder,
177
- collisionPolicy: options.collisionPolicy,
178
- presetId: options.preset,
179
- tags: parseTags(options.tags),
180
- source: buildSource(options),
175
+ const published = await publishCapture({
176
+ collection,
177
+ store,
178
+ lockPath: ledger.lockPath,
179
+ reportSyncFailure: true,
180
+ config,
181
+ plan: async () => {
182
+ const existingDocs = await store.listDocuments(collection.name);
183
+ if (!existingDocs.ok) {
184
+ throw new Error(existingDocs.error.message);
185
+ }
186
+ try {
187
+ return planCapture({
188
+ input,
189
+ existingRelPaths: existingDocs.value.map((doc) => doc.relPath),
190
+ diskRelPaths: await listCaptureDiskRelPaths(collection.path),
191
+ });
192
+ } catch (error) {
193
+ throw new CliError(
194
+ "VALIDATION",
195
+ error instanceof Error ? error.message : String(error)
196
+ );
197
+ }
181
198
  },
182
- existingRelPaths: existingDocs.value.map((doc) => doc.relPath),
183
- diskRelPaths,
199
+ request:
200
+ options.requestId === undefined
201
+ ? undefined
202
+ : { ...ledger, requestId: options.requestId, input },
184
203
  });
204
+ return published.request
205
+ ? { ...published.receipt, request: published.request }
206
+ : published.receipt;
185
207
  } catch (error) {
186
- throw new CliError(
187
- "VALIDATION",
188
- error instanceof Error ? error.message : String(error)
189
- );
208
+ throw requestErrorToCli(error);
190
209
  }
191
- const absPath = join(collection.path, plan.relPath);
192
-
193
- const lockPath = join(
194
- dirname(getIndexDbPath(options.indexName)),
195
- ".mcp-write.lock"
196
- );
197
- return await withWriteLock(lockPath, async () => {
198
- if (plan.openedExisting) {
199
- const existingDoc = await store.getDocument(
200
- collection.name,
201
- plan.relPath
202
- );
203
- if (!existingDoc.ok) {
204
- throw new Error(existingDoc.error.message);
205
- }
206
- return buildCaptureReceipt({
207
- plan,
208
- absPath,
209
- docid: existingDoc.value?.docid,
210
- sync: existingDoc.value
211
- ? { status: "completed" }
212
- : {
213
- status: "skipped",
214
- reason: "Existing file is not indexed yet.",
215
- },
216
- });
217
- }
218
-
219
- await mkdir(dirname(absPath), { recursive: true });
220
- await writeCapturePlanFile(plan, absPath);
221
- const syncResults = await defaultSyncService.syncFiles(
222
- collection,
223
- store,
224
- [plan.relPath],
225
- withContentTypeRules(
226
- {
227
- runUpdateCmd: false,
228
- gitPull: false,
229
- },
230
- config
231
- )
232
- );
233
- const syncResult = syncResults[0];
234
- const docResult = await store.getDocument(collection.name, plan.relPath);
235
- const docid = docResult.ok ? docResult.value?.docid : undefined;
236
- return buildCaptureReceipt({
237
- plan,
238
- absPath,
239
- docid: syncResult?.docid ?? docid,
240
- sync:
241
- syncResult?.status === "error"
242
- ? {
243
- status: "failed",
244
- error:
245
- syncResult.errorMessage ??
246
- syncResult.errorCode ??
247
- "Unknown sync error",
248
- }
249
- : { status: "completed" },
250
- });
251
- });
252
210
  } finally {
253
211
  await store.close();
254
212
  }
255
213
  }
256
214
 
257
215
  export function formatCaptureReceipt(
258
- receipt: CaptureReceipt,
216
+ receipt: CaptureReceipt & { request?: RequestReceiptInfo },
259
217
  options: { json?: boolean; quiet?: boolean } = {}
260
218
  ): string {
261
219
  if (options.json) {
@@ -278,5 +236,6 @@ export function formatCaptureReceipt(
278
236
  if (receipt.source.url) {
279
237
  lines.push(`Source: ${receipt.source.url}`);
280
238
  }
239
+ if (receipt.request) lines.push(formatRequestReceiptLine(receipt.request));
281
240
  return lines.join("\n");
282
241
  }