unique-sdk 2026.26.0.dev5__tar.gz → 2026.26.0.dev7__tar.gz

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 (82) hide show
  1. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/PKG-INFO +1 -1
  2. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/pyproject.toml +1 -1
  3. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/cli.py +30 -0
  4. unique_sdk-2026.26.0.dev7/unique_sdk/cli/commands/read.py +93 -0
  5. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/shell.py +23 -0
  6. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +42 -1
  7. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/README.md +0 -0
  8. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/__init__.py +0 -0
  9. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_api_requestor.py +0 -0
  10. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_api_resource.py +0 -0
  11. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_api_version.py +0 -0
  12. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_error.py +0 -0
  13. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_http_client.py +0 -0
  14. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_list_object.py +0 -0
  15. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_object_classes.py +0 -0
  16. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_request_options.py +0 -0
  17. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_unique_object.py +0 -0
  18. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_unique_ql.py +0 -0
  19. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_unique_response.py +0 -0
  20. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_util.py +0 -0
  21. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_version.py +0 -0
  22. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/_webhook.py +0 -0
  23. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/__init__.py +0 -0
  24. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_acronyms.py +0 -0
  25. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_agentic_table.py +0 -0
  26. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_analytics_order.py +0 -0
  27. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_benchmarking.py +0 -0
  28. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_briefing.py +0 -0
  29. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_chat_completion.py +0 -0
  30. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_content.py +0 -0
  31. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_elicitation.py +0 -0
  32. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_embedding.py +0 -0
  33. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_event.py +0 -0
  34. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_folder.py +0 -0
  35. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_group.py +0 -0
  36. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_integrated.py +0 -0
  37. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_llm_models.py +0 -0
  38. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_mcp.py +0 -0
  39. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_message.py +0 -0
  40. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_message_assessment.py +0 -0
  41. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_message_execution.py +0 -0
  42. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_message_log.py +0 -0
  43. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_message_tool.py +0 -0
  44. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_module.py +0 -0
  45. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  46. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_search.py +0 -0
  47. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_search_string.py +0 -0
  48. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  49. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_space.py +0 -0
  50. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_user.py +0 -0
  51. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/api_resources/_web_search.py +0 -0
  52. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/__init__.py +0 -0
  53. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/__main__.py +0 -0
  54. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/__init__.py +0 -0
  55. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  56. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/cite_file.py +0 -0
  57. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/elicitation.py +0 -0
  58. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/files.py +0 -0
  59. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/folders.py +0 -0
  60. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/mcp.py +0 -0
  61. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/navigation.py +0 -0
  62. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  63. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/search.py +0 -0
  64. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/subagent.py +0 -0
  65. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/web_search.py +0 -0
  66. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/commands/web_search_config.py +0 -0
  67. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/config.py +0 -0
  68. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/formatting.py +0 -0
  69. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  70. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  71. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  72. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  73. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  74. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  75. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/cli/state.py +0 -0
  76. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/utils/analytics_order_run.py +0 -0
  77. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/utils/benchmarking_run.py +0 -0
  78. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/utils/chat_history.py +0 -0
  79. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/utils/chat_in_space.py +0 -0
  80. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/utils/file_io.py +0 -0
  81. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/utils/sources.py +0 -0
  82. {unique_sdk-2026.26.0.dev5 → unique_sdk-2026.26.0.dev7}/unique_sdk/utils/token.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-sdk
3
- Version: 2026.26.0.dev5
3
+ Version: 2026.26.0.dev7
4
4
  Summary:
5
5
  Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
6
6
  Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_sdk"
3
- version = "2026.26.0.dev5"
3
+ version = "2026.26.0.dev7"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -20,6 +20,10 @@ from unique_sdk.cli.commands.files import cmd_download, cmd_mv_file, cmd_rm, cmd
20
20
  from unique_sdk.cli.commands.folders import cmd_mkdir, cmd_mvdir, cmd_rmdir
21
21
  from unique_sdk.cli.commands.mcp import cmd_mcp
22
22
  from unique_sdk.cli.commands.navigation import cmd_cd, cmd_ls, cmd_pwd
23
+ from unique_sdk.cli.commands.read import cmd_read
24
+ from unique_sdk.cli.commands.read import (
25
+ is_error_output as _is_read_error_output,
26
+ )
23
27
  from unique_sdk.cli.commands.scheduled_tasks import (
24
28
  cmd_schedule_create,
25
29
  cmd_schedule_delete,
@@ -329,6 +333,32 @@ def cite(
329
333
  click.echo(cmd_cite_file(LazyState.get(ctx), name_or_id, pages))
330
334
 
331
335
 
336
+ @main.command(name="read")
337
+ @click.argument("cont_id")
338
+ @click.pass_context
339
+ def read_cmd(ctx: click.Context, cont_id: str) -> None:
340
+ """Read all indexed text chunks for a known content ID.
341
+
342
+ \b
343
+ CONT_ID must be a content ID (cont_...) obtained from a prior `ls` or
344
+ `search` result. Retrieves every indexed chunk directly from the database
345
+ — no vector search, no query string needed.
346
+
347
+ \b
348
+ Use `search` when you need to find documents by topic or keyword.
349
+ Use `read` when you already know the content ID and want the full text.
350
+
351
+ \b
352
+ Examples:
353
+ unique-cli read cont_abc123
354
+ """
355
+ output = cmd_read(LazyState.get(ctx), cont_id)
356
+ if _is_read_error_output(output):
357
+ click.echo(output, err=True)
358
+ raise SystemExit(1)
359
+ click.echo(output)
360
+
361
+
332
362
  @main.command()
333
363
  @click.argument("name_or_id")
334
364
  @click.pass_context
@@ -0,0 +1,93 @@
1
+ """Read command: retrieve all indexed text chunks for a known content ID.
2
+
3
+ Calls ``Content.search(where={"id": {"equals": cont_id}})`` — a direct
4
+ Postgres lookup that returns every indexed chunk for the document in one
5
+ request, no vector search involved.
6
+
7
+ Use this when you already know the ``cont_*`` ID (e.g. from a prior ``ls``
8
+ or ``unique-cli search`` result) and want to read the full document text.
9
+ For discovery or query-based retrieval use ``unique-cli search`` instead.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import unique_sdk
15
+ from unique_sdk.cli.state import ShellState
16
+
17
+ READ_ERROR_PREFIX = "read:"
18
+
19
+
20
+ def cmd_read(state: ShellState, cont_id: str) -> str:
21
+ """Return all indexed text chunks for *cont_id* as plain text.
22
+
23
+ Args:
24
+ state: Shell state carrying user/company credentials.
25
+ cont_id: A content ID (``cont_...``) to retrieve.
26
+
27
+ Returns:
28
+ A formatted string of chunks, or an error message prefixed with
29
+ ``read:``.
30
+ """
31
+ if not cont_id.startswith("cont_"):
32
+ return f"{READ_ERROR_PREFIX} expected a content ID starting with 'cont_', got: {cont_id!r}"
33
+
34
+ # Enforce the same .unique-search.json workspace boundary as search/ls/rm.
35
+ # Content.search has no scopeIds param, so we guard by owner scope before
36
+ # the point-lookup — matching rm/mv, not search's API-level scopeIds filter.
37
+ if not state.is_content_within_workspace(cont_id):
38
+ return f"{READ_ERROR_PREFIX} permission denied (outside workspace scope)"
39
+
40
+ try:
41
+ results = unique_sdk.Content.search(
42
+ user_id=state.config.user_id,
43
+ company_id=state.config.company_id,
44
+ where={"id": {"equals": cont_id}},
45
+ )
46
+ except unique_sdk.APIError as e:
47
+ return f"{READ_ERROR_PREFIX} {e}"
48
+
49
+ if not results:
50
+ return f"{READ_ERROR_PREFIX} no content found for ID: {cont_id}"
51
+
52
+ content = results[0]
53
+ title = getattr(content, "title", None) or getattr(content, "key", None) or cont_id
54
+ chunks = getattr(content, "chunks", None) or []
55
+
56
+ if not chunks:
57
+ return (
58
+ f"Content: {title} ({cont_id})\n"
59
+ "No indexed chunks found — the document may still be ingesting or ingestion failed."
60
+ )
61
+
62
+ sorted_chunks = sorted(chunks, key=lambda c: c.get("order") or 0)
63
+
64
+ lines: list[str] = [
65
+ f"Content: {title} ({cont_id}) — {len(sorted_chunks)} chunk(s)\n"
66
+ ]
67
+ for chunk in sorted_chunks:
68
+ text = (chunk.get("text") or "").strip()
69
+ if not text:
70
+ continue
71
+ start = chunk.get("startPage")
72
+ end = chunk.get("endPage")
73
+ if start is not None or end is not None:
74
+ page_start = start if start is not None else end
75
+ page_end = end if end is not None else start
76
+ if page_start is not None and page_end is not None:
77
+ page_ref = (
78
+ f"[p.{page_start}]"
79
+ if page_start == page_end
80
+ else f"[p.{page_start}-{page_end}]"
81
+ )
82
+ lines.append(f"{page_ref} {text}")
83
+ else:
84
+ lines.append(text)
85
+ else:
86
+ lines.append(text)
87
+
88
+ return "\n\n".join(lines)
89
+
90
+
91
+ def is_error_output(output: str) -> bool:
92
+ """Return ``True`` when *output* is an error message from ``cmd_read``."""
93
+ return output.startswith(READ_ERROR_PREFIX)
@@ -59,6 +59,7 @@ OVERVIEW_HELP = textwrap.dedent("""\
59
59
  --folder <path|id> Restrict to a folder
60
60
  --metadata <key=value> Filter by metadata (repeatable)
61
61
  --limit <N> Max results (default: 200)
62
+ read <cont_id> Read all indexed text chunks for a content ID
62
63
 
63
64
  MCP:
64
65
  mcp [options] <json> Call an MCP server tool
@@ -394,6 +395,28 @@ class UniqueShell(cmd.Cmd):
394
395
  return
395
396
  self._print(cmd_cite_file(self.state, positional[0], pages))
396
397
 
398
+ def do_read(self, arg: str) -> None:
399
+ """Read all indexed text chunks for a known content ID.
400
+
401
+ Usage: read <cont_id>
402
+
403
+ Retrieves every indexed chunk for the document directly from the
404
+ database — no vector search, no query string needed.
405
+
406
+ Use `search` to find documents by topic; use `read` once you have
407
+ the content ID and want the full text.
408
+
409
+ Examples:
410
+ /Reports> read cont_abc123
411
+ """
412
+ from unique_sdk.cli.commands.read import cmd_read
413
+
414
+ parts = shlex.split(arg)
415
+ if not parts:
416
+ self._print("Usage: read <cont_id>")
417
+ return
418
+ self._print(cmd_read(self.state, parts[0]))
419
+
397
420
  def do_rm(self, arg: str) -> None:
398
421
  """Delete a file.
399
422
 
@@ -6,6 +6,8 @@ description: >-
6
6
  as clickable reference chips and `<sup>N</sup>` footnotes on the Unique
7
7
  platform. Use whenever the user asks to find, search, or query documents
8
8
  or content on Unique, including filtering by folder or metadata.
9
+ Also covers `unique-cli read <cont_id>` for reading the full indexed text
10
+ of a document when its content ID is already known.
9
11
  NOTE: This search uses combined vector + full-text indexing. Excel
10
12
  (.xlsx/.xls), CSV (.csv), and image files are NOT full-text indexed,
11
13
  so they will not appear in search results. To locate these file types,
@@ -13,7 +15,20 @@ description: >-
13
15
  `unique-cli ls` to find them by name).
14
16
  ---
15
17
 
16
- # Unique CLI -- Knowledge Base Search
18
+ # Unique CLI -- Knowledge Base Search & Document Read
19
+
20
+ ## `search` vs `read` — which command to use
21
+
22
+ | Situation | Command |
23
+ |-----------|---------|
24
+ | You have a **query or topic** and want to find relevant chunks across documents | `unique-cli search "<query>"` |
25
+ | You already have a **`cont_*` ID** and want the **full indexed text** of that document | `unique-cli read <cont_id>` |
26
+
27
+ **What "full indexed text" means:** the platform has already ingested the document — OCR for scanned pages, extracted text from PDFs and Office files, and image descriptions from figures/charts. `read` returns that pre-processed text directly; you do **not** need to download the file or run OCR yourself. If `read` returns no chunks after ingestion should be complete, download the file with `unique-cli download` and inspect it directly — ingestion occasionally fails and the raw file must be checked.
28
+
29
+ Use `read` after a `ls` or `search` surfaces a content ID and you need to go deeper into that specific document — it retrieves every chunk directly from the database with no query needed. Use `search` for discovery.
30
+
31
+ ---
17
32
 
18
33
  Search the Unique knowledge base using combined vector + full-text search via the `unique-cli search` command. Every invocation wraps each result in a `<sourceN>...</sourceN>` block and records a per-turn citation manifest at `.unique/kb-search-refs.jsonl`, so any fact you cite as `[sourceN]` is rendered with a footnote and a clickable reference chip in the chat UI.
19
34
 
@@ -131,6 +146,32 @@ unique-cli search "Q4 earnings" \
131
146
  --limit 100
132
147
  ```
133
148
 
149
+ ## Reading a Document by ID (`read`)
150
+
151
+ When you already know a `cont_*` ID, use `read` to retrieve every indexed chunk in one call:
152
+
153
+ ```bash
154
+ unique-cli read cont_abc123
155
+ ```
156
+
157
+ Output:
158
+
159
+ ```
160
+ Content: annual-report.pdf (cont_abc123) — 42 chunk(s)
161
+
162
+ [p.1] The company was founded in 1998 with a focus on...
163
+
164
+ [p.2-3] Revenue grew 15% year over year, driven by...
165
+
166
+ [p.4] Key risks include supply chain disruptions...
167
+ ```
168
+
169
+ Each paragraph is one ingested chunk (OCR, extracted text, image descriptions) prefixed with its page range when available. No further file parsing or OCR is required on your side.
170
+
171
+ **When chunks are empty:** if the document was just uploaded and ingestion hasn't finished, `read` returns a message saying so — retry after a short wait. If chunks stay empty after a reasonable wait, download the file (`unique-cli download <cont_id>`) and inspect it yourself; some files are not ingested correctly and must be read directly.
172
+
173
+ **`read` does not produce `[sourceN]` citations.** It is for reading and understanding document content. If you need to cite specific facts in your answer, run `unique-cli search "<relevant query>"` against the same document's folder to generate proper `[sourceN]` references.
174
+
134
175
  ## Prerequisites
135
176
 
136
177
  Requires these environment variables: