@sift-wiki/cli 0.1.4 → 0.1.6

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 (3) hide show
  1. package/README.md +21 -0
  2. package/dist/bin/sift.js +889 -305
  3. package/package.json +1 -1
package/dist/bin/sift.js CHANGED
@@ -149,20 +149,20 @@ function parseProfileQuery(input) {
149
149
  };
150
150
  }
151
151
  function parseToolsSearch(input) {
152
- const result2 = {
152
+ const result = {
153
153
  intent: requireString(input, "intent")
154
154
  };
155
155
  if (input.toolsetNames !== void 0) {
156
- result2.toolsetNames = requireStringArray(input, "toolsetNames");
156
+ result.toolsetNames = requireStringArray(input, "toolsetNames");
157
157
  }
158
158
  if (input.limit !== void 0) {
159
159
  const limit = optionalInteger(input, "limit");
160
160
  if (limit === void 0 || limit < 1 || limit > 20) {
161
161
  throw new Error("limit must be an integer between 1 and 20.");
162
162
  }
163
- result2.limit = limit;
163
+ result.limit = limit;
164
164
  }
165
- return result2;
165
+ return result;
166
166
  }
167
167
  function parseDecision(input) {
168
168
  return {
@@ -373,6 +373,7 @@ function writeTool(name, summary, properties, cliExample, options) {
373
373
  capability: "record:write",
374
374
  mutability: "write",
375
375
  transports: writeTransports,
376
+ idempotency: options?.idempotency,
376
377
  cliExample,
377
378
  hostedAgent: options?.hostedAgent
378
379
  });
@@ -428,7 +429,7 @@ function defineTool(input) {
428
429
  mutability: input.mutability,
429
430
  auditCategory: input.mutability === "admin" ? "admin" : input.mutability,
430
431
  transports: input.transports,
431
- idempotency: input.mutability === "write" ? "recommended" : "none",
432
+ idempotency: input.idempotency ?? (input.mutability === "write" ? "recommended" : "none"),
432
433
  resultSize: "compact",
433
434
  cliExample: input.cliExample,
434
435
  hostedAgent: hostedAgentMetadata(input, required)
@@ -610,6 +611,32 @@ var init_registry = __esm({
610
611
  severity: { type: "string" },
611
612
  visibility: { type: "array", items: { type: "string" } }
612
613
  }, "sift skill teach <skill-id> --lesson 'when X, do Y'", { required: ["skillId", "lesson", "visibility"] }),
614
+ writeTool("skill.feedback", "Report structured feedback for a pinned skill version; external-agent reports remain reported until trusted Sift evidence verifies them.", {
615
+ skillId: { type: "string" },
616
+ skillVersionId: { type: "string" },
617
+ exerciseRef: {
618
+ type: "object",
619
+ properties: { exerciseId: { type: "string" } },
620
+ required: ["exerciseId"]
621
+ },
622
+ subjectRef: { type: "object", properties: {} },
623
+ signalKind: { type: "string" },
624
+ polarity: { type: "string", enum: ["positive", "negative", "mixed", "unknown"] },
625
+ strength: { type: "string", enum: ["weak", "medium", "strong"] },
626
+ payload: { type: "object", properties: {} },
627
+ idempotencyKey: { type: "string" }
628
+ }, "sift skill feedback <skill-id> <skill-version-id>", {
629
+ required: [
630
+ "skillId",
631
+ "skillVersionId",
632
+ "signalKind",
633
+ "polarity",
634
+ "strength",
635
+ "payload",
636
+ "idempotencyKey"
637
+ ],
638
+ idempotency: "required"
639
+ }),
613
640
  readTool("search.query", "Search authorized brain context and return raw cited candidate results for exploration.", {
614
641
  query: { type: "string" },
615
642
  limit: { type: "integer", minimum: 1, maximum: 20 }
@@ -813,24 +840,24 @@ var init_discovery = __esm({
813
840
  });
814
841
 
815
842
  // ../tools/dist/results.js
816
- function captureResult(result2) {
843
+ function captureResult(result) {
817
844
  return {
818
- status: result2.job?.status ?? result2.status ?? "captured",
819
- jobId: result2.job?.id,
820
- sourceId: result2.sourceId,
821
- sourceItemId: result2.sourceItemId,
822
- recordId: result2.recordId,
823
- versionId: result2.versionId,
824
- versionNumber: result2.versionNumber
845
+ status: result.job?.status ?? result.status ?? "captured",
846
+ jobId: result.job?.id,
847
+ sourceId: result.sourceId,
848
+ sourceItemId: result.sourceItemId,
849
+ recordId: result.recordId,
850
+ versionId: result.versionId,
851
+ versionNumber: result.versionNumber
825
852
  };
826
853
  }
827
- function workRecordResult(result2) {
854
+ function workRecordResult(result) {
828
855
  return {
829
- status: result2.job.status,
830
- jobId: result2.job.id,
831
- recordId: result2.recordId,
832
- versionId: result2.versionId,
833
- versionNumber: result2.versionNumber
856
+ status: result.job.status,
857
+ jobId: result.job.id,
858
+ recordId: result.recordId,
859
+ versionId: result.versionId,
860
+ versionNumber: result.versionNumber
834
861
  };
835
862
  }
836
863
  var init_results = __esm({
@@ -842,16 +869,16 @@ var init_results = __esm({
842
869
  // ../tools/dist/captureTools.js
843
870
  async function executeCaptureText(input, toolInput) {
844
871
  const capture = parseCaptureText(toolInput);
845
- const result2 = await input.service.ingestText({ auth: input.auth, ...capture });
846
- return captureResult(result2);
872
+ const result = await input.service.ingestText({ auth: input.auth, ...capture });
873
+ return captureResult(result);
847
874
  }
848
875
  async function executeCaptureFile(input, toolInput) {
849
876
  if (input.service.ingestFile === void 0) {
850
877
  throw new Error("Tool 'capture.file' requires a file ingestion service contract.");
851
878
  }
852
879
  const file = parseCaptureFile(toolInput);
853
- const result2 = await input.service.ingestFile({ auth: input.auth, ...file });
854
- return captureResult(result2);
880
+ const result = await input.service.ingestFile({ auth: input.auth, ...file });
881
+ return captureResult(result);
855
882
  }
856
883
  async function executeCaptureBatch(input, toolInput) {
857
884
  const batch = parseCaptureBatch(toolInput);
@@ -859,16 +886,16 @@ async function executeCaptureBatch(input, toolInput) {
859
886
  for (const item of batch.items) {
860
887
  if (item.kind === "text") {
861
888
  const { kind: _kind2, ...capture } = item;
862
- const result3 = await input.service.ingestText({ auth: input.auth, ...capture });
863
- results.push(captureResult(result3));
889
+ const result2 = await input.service.ingestText({ auth: input.auth, ...capture });
890
+ results.push(captureResult(result2));
864
891
  continue;
865
892
  }
866
893
  if (input.service.ingestFile === void 0) {
867
894
  throw new Error("Tool 'capture.batch' requires a file ingestion service contract.");
868
895
  }
869
896
  const { kind: _kind, ...file } = item;
870
- const result2 = await input.service.ingestFile({ auth: input.auth, ...file });
871
- results.push(captureResult(result2));
897
+ const result = await input.service.ingestFile({ auth: input.auth, ...file });
898
+ results.push(captureResult(result));
872
899
  }
873
900
  return results;
874
901
  }
@@ -941,6 +968,15 @@ function skillToolHandlers(input, toolInput) {
941
968
  if (input.service.teachSkill === void 0)
942
969
  throw missingSkillService("skill.teach");
943
970
  return input.service.teachSkill({ auth: input.auth, ...parseSkillTeach(toolInput) });
971
+ },
972
+ "skill.feedback": () => {
973
+ if (input.service.reportFeedbackSignal === void 0) {
974
+ throw missingSkillService("skill.feedback");
975
+ }
976
+ return input.service.reportFeedbackSignal({
977
+ auth: input.auth,
978
+ ...parseSkillFeedback(toolInput)
979
+ });
944
980
  }
945
981
  };
946
982
  }
@@ -950,7 +986,8 @@ function skillToolAvailability(service) {
950
986
  [service.getSkill !== void 0, ["skill.get"]],
951
987
  [service.getSkillFile !== void 0, ["skill.file"]],
952
988
  [service.recordSkillExercise !== void 0, ["skill.exercise"]],
953
- [service.teachSkill !== void 0, ["skill.teach"]]
989
+ [service.teachSkill !== void 0, ["skill.teach"]],
990
+ [service.reportFeedbackSignal !== void 0, ["skill.feedback"]]
954
991
  ];
955
992
  }
956
993
  function parseSkillExercise(input) {
@@ -983,6 +1020,29 @@ function parseSkillTeach(input) {
983
1020
  visibility
984
1021
  };
985
1022
  }
1023
+ function parseSkillFeedback(input) {
1024
+ const exerciseRef = optionalObject(input, "exerciseRef");
1025
+ const subjectRef = optionalObject(input, "subjectRef");
1026
+ const polarity = requireString(input, "polarity");
1027
+ const strength = requireString(input, "strength");
1028
+ if (!isSkillFeedbackPolarity(polarity)) {
1029
+ throw new Error("polarity must be positive, negative, mixed, or unknown.");
1030
+ }
1031
+ if (!isSkillFeedbackStrength(strength)) {
1032
+ throw new Error("strength must be weak, medium, or strong.");
1033
+ }
1034
+ return {
1035
+ skillId: requireString(input, "skillId"),
1036
+ skillVersionId: requireString(input, "skillVersionId"),
1037
+ exerciseRef: exerciseRef === void 0 ? void 0 : { exerciseId: requireString(exerciseRef, "exerciseId") },
1038
+ subjectRef,
1039
+ signalKind: requireString(input, "signalKind"),
1040
+ polarity,
1041
+ strength,
1042
+ payload: optionalObject(input, "payload") ?? {},
1043
+ idempotencyKey: requireString(input, "idempotencyKey")
1044
+ };
1045
+ }
986
1046
  function requireSkillOutputRef(input, key) {
987
1047
  const value = input[key];
988
1048
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -1001,6 +1061,22 @@ function requireSkillOutputRef(input, key) {
1001
1061
  }
1002
1062
  throw new Error(`${key}.kind must be record or external.`);
1003
1063
  }
1064
+ function optionalObject(input, key) {
1065
+ const value = input[key];
1066
+ if (value === void 0) {
1067
+ return void 0;
1068
+ }
1069
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1070
+ throw new Error(`${key} must be an object.`);
1071
+ }
1072
+ return value;
1073
+ }
1074
+ function isSkillFeedbackPolarity(value) {
1075
+ return value === "positive" || value === "negative" || value === "mixed" || value === "unknown";
1076
+ }
1077
+ function isSkillFeedbackStrength(value) {
1078
+ return value === "weak" || value === "medium" || value === "strong";
1079
+ }
1004
1080
  function missingSkillService(toolName) {
1005
1081
  return new Error(`Tool '${toolName}' requires a runtime service contract.`);
1006
1082
  }
@@ -1267,7 +1343,7 @@ function createRuntimeToolExecutor(input) {
1267
1343
  throw error;
1268
1344
  }
1269
1345
  try {
1270
- const result2 = await handler();
1346
+ const result = await handler();
1271
1347
  logToolCall({
1272
1348
  auth: input.auth,
1273
1349
  onToolLog: input.onToolLog,
@@ -1275,9 +1351,9 @@ function createRuntimeToolExecutor(input) {
1275
1351
  toolInput,
1276
1352
  status: "success",
1277
1353
  startedAt,
1278
- result: result2
1354
+ result
1279
1355
  });
1280
- return result2;
1356
+ return result;
1281
1357
  } catch (error) {
1282
1358
  logToolCall({
1283
1359
  auth: input.auth,
@@ -1411,16 +1487,16 @@ async function executeDecisionCreate(input, toolInput) {
1411
1487
  throw new Error("Tool 'decision.create' requires a work-record service contract.");
1412
1488
  }
1413
1489
  const decision = parseDecision(toolInput);
1414
- const result2 = await input.service.createDecision({ auth: input.auth, ...decision });
1415
- return workRecordResult(result2);
1490
+ const result = await input.service.createDecision({ auth: input.auth, ...decision });
1491
+ return workRecordResult(result);
1416
1492
  }
1417
1493
  async function executeTaskCreate(input, toolInput) {
1418
1494
  if (input.service.createTask === void 0) {
1419
1495
  throw new Error("Tool 'task.create' requires a work-record service contract.");
1420
1496
  }
1421
1497
  const task = parseTask(toolInput);
1422
- const result2 = await input.service.createTask({ auth: input.auth, ...task });
1423
- return workRecordResult(result2);
1498
+ const result = await input.service.createTask({ auth: input.auth, ...task });
1499
+ return workRecordResult(result);
1424
1500
  }
1425
1501
  function executeSourceList(input) {
1426
1502
  if (input.service.listSources === void 0) {
@@ -1471,16 +1547,16 @@ async function executeRecordCreateMarkdown(input, toolInput) {
1471
1547
  throw new Error("Tool 'record.create_markdown' requires a record write service contract.");
1472
1548
  }
1473
1549
  const record = parseMarkdownRecord(toolInput);
1474
- const result2 = await input.service.createMarkdownRecord({ auth: input.auth, ...record });
1475
- return workRecordResult(result2);
1550
+ const result = await input.service.createMarkdownRecord({ auth: input.auth, ...record });
1551
+ return workRecordResult(result);
1476
1552
  }
1477
1553
  async function executeRecordPatchSection(input, toolInput) {
1478
1554
  if (input.service.patchRecordSection === void 0) {
1479
1555
  throw new Error("Tool 'record.patch_section' requires a record write service contract.");
1480
1556
  }
1481
1557
  const patch = parseRecordSectionPatch(toolInput);
1482
- const result2 = await input.service.patchRecordSection({ auth: input.auth, ...patch });
1483
- return workRecordResult(result2);
1558
+ const result = await input.service.patchRecordSection({ auth: input.auth, ...patch });
1559
+ return workRecordResult(result);
1484
1560
  }
1485
1561
  function executeRecordVersions(input, toolInput) {
1486
1562
  if (input.service.listRecordVersions === void 0) {
@@ -1563,11 +1639,11 @@ function createMcpAdapter(input) {
1563
1639
  return errorResult2("tool_unavailable", `Tool '${call.name}' is unavailable for this MCP transport or scope.`);
1564
1640
  }
1565
1641
  try {
1566
- const result2 = await input.executor.execute(call.name, call.arguments);
1642
+ const result = await input.executor.execute(call.name, call.arguments);
1567
1643
  return {
1568
1644
  isError: false,
1569
- structuredContent: result2,
1570
- content: [{ type: "text", text: renderToolResult(result2) }]
1645
+ structuredContent: result,
1646
+ content: [{ type: "text", text: renderToolResult(result) }]
1571
1647
  };
1572
1648
  } catch (error) {
1573
1649
  return errorResult2(classifyToolError(error), messageForError(error));
@@ -1575,14 +1651,14 @@ function createMcpAdapter(input) {
1575
1651
  }
1576
1652
  };
1577
1653
  }
1578
- function renderToolResult(result2) {
1579
- if (typeof result2 === "object" && result2 !== null && "contextMarkdown" in result2) {
1580
- const context = result2;
1654
+ function renderToolResult(result) {
1655
+ if (typeof result === "object" && result !== null && "contextMarkdown" in result) {
1656
+ const context = result;
1581
1657
  if (typeof context.contextMarkdown === "string") {
1582
1658
  return context.contextMarkdown;
1583
1659
  }
1584
1660
  }
1585
- return JSON.stringify(result2);
1661
+ return JSON.stringify(result);
1586
1662
  }
1587
1663
  function classifyToolError(error) {
1588
1664
  if (error instanceof Error) {
@@ -1703,7 +1779,7 @@ var init_hostedMcpEntrypoint = __esm({
1703
1779
 
1704
1780
  // ../tools/dist/mcpJsonRpcCore.js
1705
1781
  function createMcpJsonRpcCore(input) {
1706
- const { adapter, config: config2 } = input;
1782
+ const { adapter, config } = input;
1707
1783
  return {
1708
1784
  async handleMessage(message) {
1709
1785
  if (message.id === void 0) {
@@ -1717,7 +1793,7 @@ function createMcpJsonRpcCore(input) {
1717
1793
  return {
1718
1794
  jsonrpc: "2.0",
1719
1795
  id,
1720
- result: await dispatchRequest(message.method, message.params, adapter, config2)
1796
+ result: await dispatchRequest(message.method, message.params, adapter, config)
1721
1797
  };
1722
1798
  } catch (err) {
1723
1799
  return errorResponse(id, -32601, err instanceof Error ? err.message : "Method not found");
@@ -1728,14 +1804,14 @@ function createMcpJsonRpcCore(input) {
1728
1804
  function parseErrorResponse() {
1729
1805
  return errorResponse(null, -32700, "Parse error");
1730
1806
  }
1731
- async function dispatchRequest(method, params, adapter, config2) {
1807
+ async function dispatchRequest(method, params, adapter, config) {
1732
1808
  if (method === "initialize") {
1733
1809
  const requested = readProtocolVersion(params);
1734
1810
  return {
1735
1811
  protocolVersion: requested ?? MCP_PROTOCOL_VERSION,
1736
1812
  capabilities: { tools: { listChanged: false } },
1737
- serverInfo: { name: config2.serverName, version: config2.version },
1738
- instructions: config2.instructions
1813
+ serverInfo: { name: config.serverName, version: config.version },
1814
+ instructions: config.instructions
1739
1815
  };
1740
1816
  }
1741
1817
  if (method === "ping")
@@ -1749,23 +1825,23 @@ async function dispatchRequest(method, params, adapter, config2) {
1749
1825
  throw new Error(`Method '${method}' is not supported by Sift MCP.`);
1750
1826
  }
1751
1827
  function readProtocolVersion(params) {
1752
- if (!isRecord(params))
1828
+ if (!isRecord2(params))
1753
1829
  return void 0;
1754
1830
  return typeof params.protocolVersion === "string" ? params.protocolVersion : void 0;
1755
1831
  }
1756
1832
  function parseToolCall(params) {
1757
- if (!isRecord(params) || typeof params.name !== "string") {
1833
+ if (!isRecord2(params) || typeof params.name !== "string") {
1758
1834
  throw new Error("tools/call requires a tool name.");
1759
1835
  }
1760
1836
  return {
1761
1837
  name: params.name,
1762
- arguments: isRecord(params.arguments) ? params.arguments : {}
1838
+ arguments: isRecord2(params.arguments) ? params.arguments : {}
1763
1839
  };
1764
1840
  }
1765
1841
  function errorResponse(id, code, message) {
1766
1842
  return { jsonrpc: "2.0", id, error: { code, message } };
1767
1843
  }
1768
- function isRecord(value) {
1844
+ function isRecord2(value) {
1769
1845
  return typeof value === "object" && value !== null && !Array.isArray(value);
1770
1846
  }
1771
1847
  function normalizeId(value) {
@@ -1900,19 +1976,19 @@ function renderScope(scope) {
1900
1976
  ""
1901
1977
  ].join("\n");
1902
1978
  }
1903
- function validateAuthenticatedScope(config2, now) {
1979
+ function validateAuthenticatedScope(config, now) {
1904
1980
  const requiredScope = [
1905
- ["apiBaseUrl", config2.apiBaseUrl],
1906
- ["workspaceId", config2.workspaceId],
1907
- ["brainId", config2.brainId],
1908
- ["principalId", config2.principalId]
1981
+ ["apiBaseUrl", config.apiBaseUrl],
1982
+ ["workspaceId", config.workspaceId],
1983
+ ["brainId", config.brainId],
1984
+ ["principalId", config.principalId]
1909
1985
  ];
1910
1986
  const missing = requiredScope.find(([, value]) => value.trim().length === 0);
1911
1987
  if (missing !== void 0) {
1912
1988
  throw new Error(`Missing authenticated CLI scope: ${missing[0]}.`);
1913
1989
  }
1914
- if (config2.tokenExpiresAt !== void 0) {
1915
- const expiresAt = Date.parse(config2.tokenExpiresAt);
1990
+ if (config.tokenExpiresAt !== void 0) {
1991
+ const expiresAt = Date.parse(config.tokenExpiresAt);
1916
1992
  if (!Number.isFinite(expiresAt)) {
1917
1993
  throw new Error("Invalid CLI auth expiry timestamp.");
1918
1994
  }
@@ -1921,49 +1997,49 @@ function validateAuthenticatedScope(config2, now) {
1921
1997
  }
1922
1998
  }
1923
1999
  }
1924
- function renderSearchResult(result2) {
1925
- if (typeof result2 === "object" && result2 !== null && "contextMarkdown" in result2) {
1926
- const context = result2;
2000
+ function renderSearchResult(result) {
2001
+ if (typeof result === "object" && result !== null && "contextMarkdown" in result) {
2002
+ const context = result;
1927
2003
  return `${context.contextMarkdown}
1928
2004
  `;
1929
2005
  }
1930
- return `${JSON.stringify(result2)}
2006
+ return `${JSON.stringify(result)}
1931
2007
  `;
1932
2008
  }
1933
- function renderRecordResult(result2) {
1934
- if (typeof result2 === "object" && result2 !== null && "markdown" in result2) {
1935
- const record = result2;
2009
+ function renderRecordResult(result) {
2010
+ if (typeof result === "object" && result !== null && "markdown" in result) {
2011
+ const record = result;
1936
2012
  return `${record.markdown}
1937
2013
  `;
1938
2014
  }
1939
- return `${JSON.stringify(result2)}
2015
+ return `${JSON.stringify(result)}
1940
2016
  `;
1941
2017
  }
1942
- function renderProfileResult(result2) {
1943
- if (typeof result2 === "object" && result2 !== null && "profileMarkdown" in result2) {
1944
- const profile = result2;
2018
+ function renderProfileResult(result) {
2019
+ if (typeof result === "object" && result !== null && "profileMarkdown" in result) {
2020
+ const profile = result;
1945
2021
  return `${profile.profileMarkdown}
1946
2022
  `;
1947
2023
  }
1948
- return `${JSON.stringify(result2)}
2024
+ return `${JSON.stringify(result)}
1949
2025
  `;
1950
2026
  }
1951
2027
  function renderTools(tools) {
1952
2028
  return `${tools.map((tool) => `${tool.name}: ${tool.summary}`).join("\n")}
1953
2029
  `;
1954
2030
  }
1955
- function renderSiftFound(result2) {
2031
+ function renderSiftFound(result) {
1956
2032
  return `Sift found:
1957
2033
 
1958
- ${renderSearchResult(result2)}`;
2034
+ ${renderSearchResult(result)}`;
1959
2035
  }
1960
- function renderWriteReceipt(action, result2) {
1961
- if (typeof result2 !== "object" || result2 === null) {
2036
+ function renderWriteReceipt(action, result) {
2037
+ if (typeof result !== "object" || result === null) {
1962
2038
  return `${action} complete.
1963
- ${JSON.stringify(result2)}
2039
+ ${JSON.stringify(result)}
1964
2040
  `;
1965
2041
  }
1966
- const record = result2;
2042
+ const record = result;
1967
2043
  const lines = [`${action} complete.`];
1968
2044
  addReceiptLine(lines, "Record", record.recordId);
1969
2045
  addReceiptLine(lines, "Version", record.versionId);
@@ -1973,20 +2049,20 @@ ${JSON.stringify(result2)}
1973
2049
  addReceiptLine(lines, "Job", record.job.id);
1974
2050
  }
1975
2051
  if (lines.length === 1) {
1976
- lines.push(JSON.stringify(result2));
2052
+ lines.push(JSON.stringify(result));
1977
2053
  }
1978
2054
  lines.push("");
1979
2055
  return lines.join("\n");
1980
2056
  }
1981
- function renderDoctorResult(result2) {
1982
- return `${result2.checks.map((check) => {
2057
+ function renderDoctorResult(result) {
2058
+ return `${result.checks.map((check) => {
1983
2059
  const fix = check.fix === void 0 ? "" : ` Fix: ${check.fix}`;
1984
2060
  return `[${check.status}] ${check.label}: ${check.detail}${fix}`;
1985
2061
  }).join("\n")}
1986
2062
  `;
1987
2063
  }
1988
- function aliasJson(command, tool, result2) {
1989
- return `${JSON.stringify({ command, tool, result: result2 })}
2064
+ function aliasJson(command, tool, result) {
2065
+ return `${JSON.stringify({ command, tool, result })}
1990
2066
  `;
1991
2067
  }
1992
2068
  function positionalArgs(args) {
@@ -2184,16 +2260,16 @@ async function agentRegister(executor, assertedAgentName, rest, json) {
2184
2260
  if (kind !== void 0) {
2185
2261
  input.kind = kind;
2186
2262
  }
2187
- const result2 = await executor.execute("agent.register", input);
2188
- return ok(json ? `${JSON.stringify(result2)}
2189
- ` : renderAgentRegisterResult(result2));
2263
+ const result = await executor.execute("agent.register", input);
2264
+ return ok(json ? `${JSON.stringify(result)}
2265
+ ` : renderAgentRegisterResult(result));
2190
2266
  }
2191
- function renderAgentRegisterResult(result2) {
2192
- if (typeof result2 !== "object" || result2 === null || !("agent" in result2)) {
2193
- return `${JSON.stringify(result2)}
2267
+ function renderAgentRegisterResult(result) {
2268
+ if (typeof result !== "object" || result === null || !("agent" in result)) {
2269
+ return `${JSON.stringify(result)}
2194
2270
  `;
2195
2271
  }
2196
- const { agent, created, reactivated } = result2;
2272
+ const { agent, created, reactivated } = result;
2197
2273
  const verb = created === true ? "Registered" : reactivated === true ? "Reactivated" : "Already registered";
2198
2274
  const actsFor = agent.actsForDisplayName === void 0 ? "" : ` (acting for ${agent.actsForDisplayName})`;
2199
2275
  return `${verb} agent worker '${agent.name ?? "unknown"}'${actsFor}.
@@ -2262,7 +2338,8 @@ var commandCapabilities = {
2262
2338
  "evidence:get": "record:read",
2263
2339
  "graph:neighbors": "record:read",
2264
2340
  "event:list": "record:read",
2265
- "audit:events": "event:audit:read"
2341
+ "audit:events": "event:audit:read",
2342
+ "roam:import": "source:manage"
2266
2343
  };
2267
2344
  function validateCommandCapability(input) {
2268
2345
  const capability = commandCapabilities[input.commandKey];
@@ -2271,6 +2348,17 @@ function validateCommandCapability(input) {
2271
2348
  }
2272
2349
  }
2273
2350
 
2351
+ // src/commandErrors.ts
2352
+ var ROAM_IMPORT_SCOPE_FIX = "Roam import needs additional Sift access. Run 'npx -y @sift-wiki/cli@latest login --capability record:read,source:manage', then retry 'sift roam import'.";
2353
+ function toCommandError(error, commandKey) {
2354
+ if (commandKey === "roam:import" && error instanceof Error && error.message === "missing capability source:manage") {
2355
+ const permissionError = new Error(ROAM_IMPORT_SCOPE_FIX);
2356
+ permissionError.name = "RuntimePermissionDeniedError";
2357
+ return permissionError;
2358
+ }
2359
+ return error;
2360
+ }
2361
+
2274
2362
  // src/contractOption.ts
2275
2363
  function extractContractVersion(argv2) {
2276
2364
  const index = argv2.indexOf("--contract");
@@ -2300,6 +2388,190 @@ function withContractVersion(executor, contractVersion) {
2300
2388
  };
2301
2389
  }
2302
2390
 
2391
+ // src/roamImport.ts
2392
+ async function runRoamImportCommand(input) {
2393
+ try {
2394
+ const parsed = parseOptions(input.rest);
2395
+ const scope = parseCliRoamScope(optionalOption(parsed, "scope") ?? "sift-tag");
2396
+ const mode = parseCliRoamMode(optionalOption(parsed, "mode") ?? "personal");
2397
+ const limit = parseIntegerOption(parsed, "limit", 100);
2398
+ if (limit < 1 || limit > 100) {
2399
+ throw new Error("Option --limit must be between 1 and 100.");
2400
+ }
2401
+ const wholeGraphConfirmed = input.rest.includes("--confirm-whole-graph") || input.rest.includes("--yes");
2402
+ if (scope === "whole_graph" && !wholeGraphConfirmed) {
2403
+ throw new Error("Option --confirm-whole-graph is required.");
2404
+ }
2405
+ const workspaceAttestation = input.rest.includes("--workspace-attestation") || input.rest.includes("--confirm-workspace");
2406
+ if (mode === "workspace" && !workspaceAttestation) {
2407
+ throw new Error("Option --workspace-attestation is required.");
2408
+ }
2409
+ if (input.reader === void 0) {
2410
+ throw new Error(
2411
+ "Roam import needs the local Roam helper. Run this command through the Sift CLI package."
2412
+ );
2413
+ }
2414
+ if (input.importer === void 0) {
2415
+ throw new Error("Not signed in. Run 'sift login', then retry 'sift roam import'.");
2416
+ }
2417
+ const records = await input.reader.exportPages({
2418
+ scope,
2419
+ graph: optionalOption(parsed, "graph"),
2420
+ limit,
2421
+ now: input.now
2422
+ });
2423
+ if (records.length === 0) {
2424
+ throw new Error(
2425
+ scope === "sift_tag" ? "No Roam pages marked [[Sift]] were found." : "No Roam pages were found for import."
2426
+ );
2427
+ }
2428
+ const result = await input.importer.importRecords({
2429
+ mode,
2430
+ scope,
2431
+ records,
2432
+ defaultVisibility: visibilityOption(parsed),
2433
+ workspaceAttestation,
2434
+ wholeGraphConfirmed
2435
+ });
2436
+ return ok(input.json ? `${JSON.stringify(result)}
2437
+ ` : renderRoamImportResult(result));
2438
+ } catch (error) {
2439
+ return errorResult(error, input.json);
2440
+ }
2441
+ }
2442
+ function createSiftRoamImportClient(input) {
2443
+ const fetchImpl = input.fetch ?? globalThis.fetch;
2444
+ return {
2445
+ async importRecords(request) {
2446
+ const response = await fetchImpl(roamImportUrl(input.apiBaseUrl, input.workspaceId), {
2447
+ method: "POST",
2448
+ headers: {
2449
+ Authorization: `Bearer ${input.token}`,
2450
+ "Content-Type": "application/json"
2451
+ },
2452
+ body: JSON.stringify(request)
2453
+ });
2454
+ const body = await response.text();
2455
+ const parsed = body.length > 0 ? parseJson(body) : {};
2456
+ if (!response.ok) {
2457
+ throw new Error(responseError(parsed, response.status));
2458
+ }
2459
+ return parseRoamImportResult(parsed);
2460
+ }
2461
+ };
2462
+ }
2463
+ function parseCliRoamScope(value) {
2464
+ if (value === "sift-tag" || value === "sift_tag" || value === "marked" || value === "sift") {
2465
+ return "sift_tag";
2466
+ }
2467
+ if (value === "whole-graph" || value === "whole_graph" || value === "everything") {
2468
+ return "whole_graph";
2469
+ }
2470
+ throw new Error("Roam scope must be sift-tag or whole-graph.");
2471
+ }
2472
+ function parseCliRoamMode(value) {
2473
+ if (value === "personal" || value === "workspace") return value;
2474
+ throw new Error("Roam import mode must be personal or workspace.");
2475
+ }
2476
+ function visibilityOption(parsed) {
2477
+ const visibility = optionalOption(parsed, "visibility");
2478
+ if (visibility === void 0) return void 0;
2479
+ const values = visibility.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
2480
+ if (values.length === 0) {
2481
+ throw new Error("Option --visibility must include at least one visibility segment.");
2482
+ }
2483
+ return values;
2484
+ }
2485
+ function renderRoamImportResult(result) {
2486
+ return [
2487
+ `Imported ${result.importedCount} Roam pages.`,
2488
+ `Stored: ${result.storedCount}`,
2489
+ `Deduped: ${result.dedupedCount}`,
2490
+ `Rejected: ${result.rejectedCount}`,
2491
+ ""
2492
+ ].join("\n");
2493
+ }
2494
+ function roamImportUrl(apiBaseUrl, workspaceId) {
2495
+ const base = `${apiBaseUrl.replace(/\/+$/u, "")}/integrations/roam/import`;
2496
+ return workspaceId === void 0 ? base : `${base}?workspaceId=${encodeURIComponent(workspaceId)}`;
2497
+ }
2498
+ function parseJson(body) {
2499
+ try {
2500
+ return JSON.parse(body);
2501
+ } catch {
2502
+ throw new Error("Sift Roam import API returned invalid JSON.");
2503
+ }
2504
+ }
2505
+ function responseError(parsed, status2) {
2506
+ if (typeof parsed === "object" && parsed !== null) {
2507
+ const record = parsed;
2508
+ const message = record.message;
2509
+ if (typeof message === "string" && message.trim().length > 0) return message;
2510
+ const error = record.error;
2511
+ if (typeof error === "object" && error !== null && "message" in error) {
2512
+ const nested = error.message;
2513
+ if (typeof nested === "string" && nested.trim().length > 0) return nested;
2514
+ }
2515
+ }
2516
+ return `Sift Roam import API failed with status ${status2}.`;
2517
+ }
2518
+ function parseRoamImportResult(value) {
2519
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2520
+ throw new Error("Sift Roam import API returned an invalid result.");
2521
+ }
2522
+ const record = value;
2523
+ if (record.providerKind !== "roam") {
2524
+ throw new Error("Sift Roam import API returned an unexpected provider kind.");
2525
+ }
2526
+ return {
2527
+ providerKind: "roam",
2528
+ importedCount: integerField(record, "importedCount"),
2529
+ storedCount: integerField(record, "storedCount"),
2530
+ dedupedCount: integerField(record, "dedupedCount"),
2531
+ rejectedCount: integerField(record, "rejectedCount")
2532
+ };
2533
+ }
2534
+ function integerField(record, key) {
2535
+ const value = record[key];
2536
+ if (typeof value !== "number" || !Number.isInteger(value)) {
2537
+ throw new Error(`Sift Roam import API result field ${key} must be an integer.`);
2538
+ }
2539
+ return value;
2540
+ }
2541
+
2542
+ // src/mcpServeCommand.ts
2543
+ async function mcpServe(input) {
2544
+ if (input.mcpServer === void 0) {
2545
+ return fail("No local MCP server is configured for mcp.serve.");
2546
+ }
2547
+ if (input.executor === void 0) {
2548
+ return fail("Not signed in. Run 'sift login', then 'sift mcp serve'.");
2549
+ }
2550
+ const result = await input.mcpServer.serve({
2551
+ config: input.config,
2552
+ executor: input.executor,
2553
+ transport: "local_mcp"
2554
+ });
2555
+ if (result === void 0) return ok("");
2556
+ return ok(`${JSON.stringify(result)}
2557
+ `);
2558
+ }
2559
+
2560
+ // src/scopeCurrentCommand.ts
2561
+ function scopeCurrent(config, json) {
2562
+ const scope = {
2563
+ apiBaseUrl: config.apiBaseUrl,
2564
+ tokenLabel: config.tokenLabel,
2565
+ tokenExpiresAt: config.tokenExpiresAt,
2566
+ principalId: config.principalId,
2567
+ workspaceId: config.workspaceId,
2568
+ brainId: config.brainId,
2569
+ capabilities: config.capabilities
2570
+ };
2571
+ return ok(json ? `${JSON.stringify(scope)}
2572
+ ` : renderScope(scope));
2573
+ }
2574
+
2303
2575
  // src/specialCommands.ts
2304
2576
  import { readFile } from "fs/promises";
2305
2577
 
@@ -2317,24 +2589,24 @@ async function doctor(input) {
2317
2589
  checks.push(readToolsCheck(availableTools3));
2318
2590
  checks.push(writeToolsCheck(input.config, availableTools3));
2319
2591
  checks.push(recordGetCheck(availableTools3));
2320
- const result2 = {
2592
+ const result = {
2321
2593
  ok: !checks.some((check) => check.status === "failed"),
2322
2594
  apiBaseUrl: input.config.apiBaseUrl.trim().length > 0 ? input.config.apiBaseUrl : void 0,
2323
2595
  scope: scopeResult(input.config),
2324
2596
  checks
2325
2597
  };
2326
2598
  return {
2327
- exitCode: result2.ok ? 0 : 1,
2328
- stdout: input.json ? `${JSON.stringify(result2)}
2329
- ` : renderDoctorResult(result2),
2599
+ exitCode: result.ok ? 0 : 1,
2600
+ stdout: input.json ? `${JSON.stringify(result)}
2601
+ ` : renderDoctorResult(result),
2330
2602
  stderr: ""
2331
2603
  };
2332
2604
  }
2333
2605
  async function discoverToolNames(executor) {
2334
2606
  if (executor === void 0) return void 0;
2335
2607
  if (executor.listAvailableToolNames !== void 0) return executor.listAvailableToolNames();
2336
- const result2 = await executor.execute("tools.list", {});
2337
- return toolNamesFromResult(result2);
2608
+ const result = await executor.execute("tools.list", {});
2609
+ return toolNamesFromResult(result);
2338
2610
  }
2339
2611
  async function apiReachability(executor) {
2340
2612
  if (executor === void 0) return { reachable: false, detail: "No API executor is configured." };
@@ -2348,15 +2620,15 @@ async function apiReachability(executor) {
2348
2620
  };
2349
2621
  }
2350
2622
  }
2351
- function scopeResult(config2) {
2352
- if (config2.workspaceId.trim().length === 0 || config2.brainId.trim().length === 0 || config2.principalId.trim().length === 0) {
2623
+ function scopeResult(config) {
2624
+ if (config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0 || config.principalId.trim().length === 0) {
2353
2625
  return void 0;
2354
2626
  }
2355
2627
  return {
2356
- workspaceId: config2.workspaceId,
2357
- brainId: config2.brainId,
2358
- principalId: config2.principalId,
2359
- capabilities: [...config2.capabilities]
2628
+ workspaceId: config.workspaceId,
2629
+ brainId: config.brainId,
2630
+ principalId: config.principalId,
2631
+ capabilities: [...config.capabilities]
2360
2632
  };
2361
2633
  }
2362
2634
  async function checkApi(executor) {
@@ -2372,8 +2644,8 @@ async function checkApi(executor) {
2372
2644
  const api = await apiReachability(executor);
2373
2645
  return api.reachable ? { id: "api", status: "ok", label: "API", detail: "whoami succeeded." } : { id: "api", status: "failed", label: "API", detail: api.detail };
2374
2646
  }
2375
- function authCheck(config2, now) {
2376
- if (config2.apiBaseUrl.trim().length === 0 || config2.workspaceId.trim().length === 0 || config2.brainId.trim().length === 0 || config2.principalId.trim().length === 0) {
2647
+ function authCheck(config, now) {
2648
+ if (config.apiBaseUrl.trim().length === 0 || config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0 || config.principalId.trim().length === 0) {
2377
2649
  return {
2378
2650
  id: "auth",
2379
2651
  status: "failed",
@@ -2382,7 +2654,7 @@ function authCheck(config2, now) {
2382
2654
  fix: "Run sift login."
2383
2655
  };
2384
2656
  }
2385
- if (config2.tokenExpiresAt !== void 0 && Date.parse(config2.tokenExpiresAt) <= now.getTime()) {
2657
+ if (config.tokenExpiresAt !== void 0 && Date.parse(config.tokenExpiresAt) <= now.getTime()) {
2386
2658
  return {
2387
2659
  id: "auth",
2388
2660
  status: "failed",
@@ -2393,8 +2665,8 @@ function authCheck(config2, now) {
2393
2665
  }
2394
2666
  return { id: "auth", status: "ok", label: "Auth", detail: "authenticated profile loaded." };
2395
2667
  }
2396
- function scopeCheck(config2) {
2397
- if (config2.workspaceId.trim().length === 0 || config2.brainId.trim().length === 0) {
2668
+ function scopeCheck(config) {
2669
+ if (config.workspaceId.trim().length === 0 || config.brainId.trim().length === 0) {
2398
2670
  return {
2399
2671
  id: "scope",
2400
2672
  status: "failed",
@@ -2407,14 +2679,14 @@ function scopeCheck(config2) {
2407
2679
  id: "scope",
2408
2680
  status: "ok",
2409
2681
  label: "Scope",
2410
- detail: `${config2.workspaceId}/${config2.brainId}`
2682
+ detail: `${config.workspaceId}/${config.brainId}`
2411
2683
  };
2412
2684
  }
2413
2685
  function readToolsCheck(names) {
2414
2686
  return toolSetCheck("read-tools", "Read tools", ["context.assemble", "search.query"], names);
2415
2687
  }
2416
- function writeToolsCheck(config2, names) {
2417
- const hasWrite = config2.capabilities.includes("record:write") || config2.capabilities.includes("source:write");
2688
+ function writeToolsCheck(config, names) {
2689
+ const hasWrite = config.capabilities.includes("record:write") || config.capabilities.includes("source:write");
2418
2690
  if (!hasWrite) {
2419
2691
  return {
2420
2692
  id: "write-tools",
@@ -2453,9 +2725,9 @@ function toolSetCheck(id, label, required, names) {
2453
2725
  const missing = required.filter((name) => !names.includes(name));
2454
2726
  return missing.length === 0 ? { id, status: "ok", label, detail: "required tools are available." } : { id, status: "failed", label, detail: `missing ${missing.join(", ")}.` };
2455
2727
  }
2456
- function toolNamesFromResult(result2) {
2457
- if (!Array.isArray(result2)) return [];
2458
- return result2.flatMap((item) => {
2728
+ function toolNamesFromResult(result) {
2729
+ if (!Array.isArray(result)) return [];
2730
+ return result.flatMap((item) => {
2459
2731
  if (typeof item === "object" && item !== null && "name" in item) {
2460
2732
  const name = item.name;
2461
2733
  return typeof name === "string" ? [name] : [];
@@ -2488,6 +2760,7 @@ var knownTopLevelCommands = /* @__PURE__ */ new Set([
2488
2760
  "mcp",
2489
2761
  "record",
2490
2762
  "remember",
2763
+ "roam",
2491
2764
  "scope",
2492
2765
  "search",
2493
2766
  "show",
@@ -2580,11 +2853,11 @@ async function ask(executor, rest, json) {
2580
2853
  }
2581
2854
  const parsed = parseOptions(rest);
2582
2855
  const query = argsWithoutOptions(rest).join(" ").trim();
2583
- const result2 = await executor.execute("context.assemble", {
2856
+ const result = await executor.execute("context.assemble", {
2584
2857
  query,
2585
2858
  maxChars: parseIntegerOption(parsed, "max-chars", 8e3)
2586
2859
  });
2587
- return ok(json ? aliasJson("ask", "context.assemble", result2) : renderSiftFound(result2));
2860
+ return ok(json ? aliasJson("ask", "context.assemble", result) : renderSiftFound(result));
2588
2861
  }
2589
2862
  async function simpleSearch(executor, rest, json) {
2590
2863
  if (executor === void 0) {
@@ -2592,11 +2865,11 @@ async function simpleSearch(executor, rest, json) {
2592
2865
  }
2593
2866
  const parsed = parseOptions(rest);
2594
2867
  const query = argsWithoutOptions(rest).join(" ").trim();
2595
- const result2 = await executor.execute("search.query", {
2868
+ const result = await executor.execute("search.query", {
2596
2869
  query,
2597
2870
  limit: parseIntegerOption(parsed, "limit", 10)
2598
2871
  });
2599
- return ok(json ? aliasJson("search", "search.query", result2) : renderSearchResult(result2));
2872
+ return ok(json ? aliasJson("search", "search.query", result) : renderSearchResult(result));
2600
2873
  }
2601
2874
  async function remember(executor, rest, json, readStdin2, now) {
2602
2875
  if (executor === void 0) {
@@ -2615,8 +2888,8 @@ async function remember(executor, rest, json, readStdin2, now) {
2615
2888
  visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY],
2616
2889
  markdown
2617
2890
  };
2618
- const result2 = await executor.execute("capture.text", input);
2619
- return ok(json ? aliasJson("remember", "capture.text", result2) : renderWriteReceipt("Remember", result2));
2891
+ const result = await executor.execute("capture.text", input);
2892
+ return ok(json ? aliasJson("remember", "capture.text", result) : renderWriteReceipt("Remember", result));
2620
2893
  }
2621
2894
  async function addFile(executor, fileReader, rest, json) {
2622
2895
  if (executor === void 0) {
@@ -2638,8 +2911,8 @@ async function addFile(executor, fileReader, rest, json) {
2638
2911
  bytes,
2639
2912
  visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY]
2640
2913
  };
2641
- const result2 = await executor.execute("capture.file", input);
2642
- return ok(json ? aliasJson("add", "capture.file", result2) : renderWriteReceipt("Add", result2));
2914
+ const result = await executor.execute("capture.file", input);
2915
+ return ok(json ? aliasJson("add", "capture.file", result) : renderWriteReceipt("Add", result));
2643
2916
  }
2644
2917
  async function edit(executor, rest, json) {
2645
2918
  if (executor === void 0) {
@@ -2664,8 +2937,8 @@ async function edit(executor, rest, json) {
2664
2937
  if (expectedMarkdown !== void 0) {
2665
2938
  input.expectedMarkdown = expectedMarkdown;
2666
2939
  }
2667
- const result2 = await executor.execute("record.patch_section", input);
2668
- return ok(json ? aliasJson("edit", "record.patch_section", result2) : renderWriteReceipt("Edit", result2));
2940
+ const result = await executor.execute("record.patch_section", input);
2941
+ return ok(json ? aliasJson("edit", "record.patch_section", result) : renderWriteReceipt("Edit", result));
2669
2942
  }
2670
2943
  async function decide(executor, rest, json) {
2671
2944
  if (executor === void 0) {
@@ -2678,8 +2951,8 @@ async function decide(executor, rest, json) {
2678
2951
  visibility: [optionalOption(parsed, "visibility") ?? DEFAULT_CLI_VISIBILITY]
2679
2952
  };
2680
2953
  addOptionalWorkAliasMetadata(input, parsed);
2681
- const result2 = await executor.execute("decision.create", input);
2682
- return ok(json ? aliasJson("decide", "decision.create", result2) : renderWriteReceipt("Decision", result2));
2954
+ const result = await executor.execute("decision.create", input);
2955
+ return ok(json ? aliasJson("decide", "decision.create", result) : renderWriteReceipt("Decision", result));
2683
2956
  }
2684
2957
  async function todo(executor, rest, json) {
2685
2958
  if (executor === void 0) {
@@ -2696,8 +2969,8 @@ async function todo(executor, rest, json) {
2696
2969
  const dueDate = optionalOption(parsed, "due-date");
2697
2970
  if (dueDate !== void 0) input.dueDate = dueDate;
2698
2971
  addOptionalWorkAliasMetadata(input, parsed);
2699
- const result2 = await executor.execute("task.create", input);
2700
- return ok(json ? aliasJson("todo", "task.create", result2) : renderWriteReceipt("Task", result2));
2972
+ const result = await executor.execute("task.create", input);
2973
+ return ok(json ? aliasJson("todo", "task.create", result) : renderWriteReceipt("Task", result));
2701
2974
  }
2702
2975
  async function show(executor, rest, json) {
2703
2976
  if (executor === void 0) {
@@ -2720,21 +2993,21 @@ async function show(executor, rest, json) {
2720
2993
  if (sectionAnchor !== void 0) {
2721
2994
  input.sectionAnchor = sectionAnchor;
2722
2995
  }
2723
- const result2 = await executor.execute("record.get", input);
2724
- return ok(json ? aliasJson("show", "record.get", result2) : renderRecordResult(result2));
2996
+ const result = await executor.execute("record.get", input);
2997
+ return ok(json ? aliasJson("show", "record.get", result) : renderRecordResult(result));
2725
2998
  }
2726
- async function status(config2, executor, json) {
2999
+ async function status(config, executor, json) {
2727
3000
  const scope = {
2728
- apiBaseUrl: config2.apiBaseUrl,
2729
- principalId: config2.principalId,
2730
- workspaceId: config2.workspaceId,
2731
- brainId: config2.brainId,
2732
- capabilities: [...config2.capabilities]
3001
+ apiBaseUrl: config.apiBaseUrl,
3002
+ principalId: config.principalId,
3003
+ workspaceId: config.workspaceId,
3004
+ brainId: config.brainId,
3005
+ capabilities: [...config.capabilities]
2733
3006
  };
2734
3007
  const api = await apiReachability(executor);
2735
- const result2 = { scope, api };
3008
+ const result = { scope, api };
2736
3009
  if (json) {
2737
- return ok(`${JSON.stringify({ command: "status", result: result2 })}
3010
+ return ok(`${JSON.stringify({ command: "status", result })}
2738
3011
  `);
2739
3012
  }
2740
3013
  return ok(`${renderScope({ ...scope, tokenLabel: "configured" })}API reachable: ${api.reachable}
@@ -3005,9 +3278,9 @@ function toolsHelp(input) {
3005
3278
  return executeSimple(input.executor, "tools.help", { name }, input.json);
3006
3279
  }
3007
3280
  async function executeSimple(executor, name, toolInput, json) {
3008
- const result2 = await executor.execute(name, toolInput);
3009
- return ok(json ? `${JSON.stringify(result2)}
3010
- ` : `${JSON.stringify(result2)}
3281
+ const result = await executor.execute(name, toolInput);
3282
+ return ok(json ? `${JSON.stringify(result)}
3283
+ ` : `${JSON.stringify(result)}
3011
3284
  `);
3012
3285
  }
3013
3286
 
@@ -3028,7 +3301,7 @@ function createHostedApiExecutor(input) {
3028
3301
  body: JSON.stringify({ input: toolInput }, serializeJsonValue)
3029
3302
  });
3030
3303
  const body = await response.text();
3031
- const parsed = body.length > 0 ? parseJson(body) : {};
3304
+ const parsed = body.length > 0 ? parseJson2(body) : {};
3032
3305
  if (!response.ok) {
3033
3306
  throw new Error(errorMessage(parsed, response.status));
3034
3307
  }
@@ -3045,7 +3318,7 @@ function serializeJsonValue(_key, value) {
3045
3318
  function toolUrl(apiBaseUrl, name) {
3046
3319
  return `${apiBaseUrl.replace(/\/+$/u, "")}/agent-tools/${encodeURIComponent(name)}`;
3047
3320
  }
3048
- function parseJson(body) {
3321
+ function parseJson2(body) {
3049
3322
  try {
3050
3323
  return JSON.parse(body);
3051
3324
  } catch {
@@ -3148,7 +3421,19 @@ async function runSiftCli(rawInput) {
3148
3421
  "task:create": () => createTask(input.executor, rest, json),
3149
3422
  "agent:register": () => agentRegister(input.executor, input.agentName, rest, json),
3150
3423
  "agent:status": () => executeSimple2(input.executor, "agent.status", {}, json),
3151
- "mcp:serve": () => mcpServe(input.mcpServer, input.config, rawInput.executor, json),
3424
+ "mcp:serve": () => mcpServe({
3425
+ mcpServer: input.mcpServer,
3426
+ config: input.config,
3427
+ executor: rawInput.executor
3428
+ }),
3429
+ "roam:import": () => runRoamImportCommand({
3430
+ rest,
3431
+ json,
3432
+ config: input.config,
3433
+ reader: input.roamReader,
3434
+ importer: input.roamImporter,
3435
+ now: input.now ?? /* @__PURE__ */ new Date()
3436
+ }),
3152
3437
  "login:": () => authCommand(input.authCommands, "login", { rest: commandRest, json }),
3153
3438
  "auth:status": () => authCommand(input.authCommands, "status", { json }),
3154
3439
  "logout:": () => authCommand(input.authCommands, "logout", { json })
@@ -3169,42 +3454,17 @@ async function runSiftCli(rawInput) {
3169
3454
  validateCommandCapability({ commandKey, config: input.config });
3170
3455
  return await handler();
3171
3456
  } catch (error) {
3172
- return errorResult(error, json);
3457
+ return errorResult(toCommandError(error, commandKey), json);
3173
3458
  }
3174
3459
  }
3175
- async function mcpServe(mcpServer, config2, executor, _json) {
3176
- if (mcpServer === void 0) {
3177
- return fail("No local MCP server is configured for mcp.serve.");
3178
- }
3179
- if (executor === void 0) {
3180
- return fail("Not signed in. Run 'sift login', then 'sift mcp serve'.");
3181
- }
3182
- const result2 = await mcpServer.serve({ config: config2, executor, transport: "local_mcp" });
3183
- if (result2 === void 0) return ok("");
3184
- return ok(`${JSON.stringify(result2)}
3185
- `);
3186
- }
3187
- function scopeCurrent(config2, json) {
3188
- const scope = {
3189
- apiBaseUrl: config2.apiBaseUrl,
3190
- tokenLabel: config2.tokenLabel,
3191
- tokenExpiresAt: config2.tokenExpiresAt,
3192
- principalId: config2.principalId,
3193
- workspaceId: config2.workspaceId,
3194
- brainId: config2.brainId,
3195
- capabilities: config2.capabilities
3196
- };
3197
- return ok(json ? `${JSON.stringify(scope)}
3198
- ` : renderScope(scope));
3199
- }
3200
3460
  async function searchQuery(executor, rest, json) {
3201
3461
  if (executor === void 0) {
3202
3462
  return fail("No Sift API executor is configured for search.query.");
3203
3463
  }
3204
3464
  const query = rest.join(" ").trim();
3205
- const result2 = await executor.execute("search.query", { query, limit: 10 });
3206
- return ok(json ? `${JSON.stringify(result2)}
3207
- ` : renderSearchResult(result2));
3465
+ const result = await executor.execute("search.query", { query, limit: 10 });
3466
+ return ok(json ? `${JSON.stringify(result)}
3467
+ ` : renderSearchResult(result));
3208
3468
  }
3209
3469
  async function contextAssemble(executor, rest, json) {
3210
3470
  if (executor === void 0) {
@@ -3212,12 +3472,12 @@ async function contextAssemble(executor, rest, json) {
3212
3472
  }
3213
3473
  const parsed = parseOptions(rest);
3214
3474
  const query = positionalArgs(rest).join(" ").trim();
3215
- const result2 = await executor.execute("context.assemble", {
3475
+ const result = await executor.execute("context.assemble", {
3216
3476
  query,
3217
3477
  maxChars: parseIntegerOption(parsed, "max-chars", 4e3)
3218
3478
  });
3219
- return ok(json ? `${JSON.stringify(result2)}
3220
- ` : renderSearchResult(result2));
3479
+ return ok(json ? `${JSON.stringify(result)}
3480
+ ` : renderSearchResult(result));
3221
3481
  }
3222
3482
  async function contextProfile(executor, rest, json) {
3223
3483
  if (executor === void 0) {
@@ -3231,17 +3491,17 @@ async function contextProfile(executor, rest, json) {
3231
3491
  if (query.length > 0) {
3232
3492
  input.query = query;
3233
3493
  }
3234
- const result2 = await executor.execute("context.profile", input);
3235
- return ok(json ? `${JSON.stringify(result2)}
3236
- ` : renderProfileResult(result2));
3494
+ const result = await executor.execute("context.profile", input);
3495
+ return ok(json ? `${JSON.stringify(result)}
3496
+ ` : renderProfileResult(result));
3237
3497
  }
3238
3498
  async function executeSimple2(executor, name, toolInput, json) {
3239
3499
  if (executor === void 0) {
3240
3500
  return fail(`No Sift API executor is configured for ${name}.`);
3241
3501
  }
3242
- const result2 = await executor.execute(name, toolInput);
3243
- return ok(json ? `${JSON.stringify(result2)}
3244
- ` : `${JSON.stringify(result2)}
3502
+ const result = await executor.execute(name, toolInput);
3503
+ return ok(json ? `${JSON.stringify(result)}
3504
+ ` : `${JSON.stringify(result)}
3245
3505
  `);
3246
3506
  }
3247
3507
  async function captureText(executor, rest, json) {
@@ -3249,15 +3509,15 @@ async function captureText(executor, rest, json) {
3249
3509
  return fail("No Sift API executor is configured for capture.text.");
3250
3510
  }
3251
3511
  const parsed = parseOptions(rest);
3252
- const result2 = await executor.execute("capture.text", {
3512
+ const result = await executor.execute("capture.text", {
3253
3513
  sourceName: requireOption(parsed, "source"),
3254
3514
  externalId: requireOption(parsed, "external-id"),
3255
3515
  title: requireOption(parsed, "title"),
3256
3516
  visibility: [requireOption(parsed, "visibility")],
3257
3517
  markdown: requireOption(parsed, "markdown")
3258
3518
  });
3259
- return ok(json ? `${JSON.stringify(result2)}
3260
- ` : `${JSON.stringify(result2)}
3519
+ return ok(json ? `${JSON.stringify(result)}
3520
+ ` : `${JSON.stringify(result)}
3261
3521
  `);
3262
3522
  }
3263
3523
  async function captureFile(executor, fileReader, rest, json) {
@@ -3270,7 +3530,7 @@ async function captureFile(executor, fileReader, rest, json) {
3270
3530
  }
3271
3531
  const parsed = parseOptions(optionArgs);
3272
3532
  const bytes = await fileReader(path);
3273
- const result2 = await executor.execute("capture.file", {
3533
+ const result = await executor.execute("capture.file", {
3274
3534
  sourceName: requireOption(parsed, "source"),
3275
3535
  externalId: requireOption(parsed, "external-id"),
3276
3536
  title: requireOption(parsed, "title"),
@@ -3279,8 +3539,8 @@ async function captureFile(executor, fileReader, rest, json) {
3279
3539
  bytes,
3280
3540
  visibility: [requireOption(parsed, "visibility")]
3281
3541
  });
3282
- return ok(json ? `${JSON.stringify(result2)}
3283
- ` : `${JSON.stringify(result2)}
3542
+ return ok(json ? `${JSON.stringify(result)}
3543
+ ` : `${JSON.stringify(result)}
3284
3544
  `);
3285
3545
  }
3286
3546
  async function captureBatch(executor, fileReader, rest, json) {
@@ -3292,9 +3552,9 @@ async function captureBatch(executor, fileReader, rest, json) {
3292
3552
  return fail("Missing required manifest path for capture.batch.");
3293
3553
  }
3294
3554
  const manifest = parseBatchManifest(await fileReader(manifestPath));
3295
- const result2 = await executor.execute("capture.batch", { items: manifest });
3296
- return ok(json ? `${JSON.stringify(result2)}
3297
- ` : `${JSON.stringify(result2)}
3555
+ const result = await executor.execute("capture.batch", { items: manifest });
3556
+ return ok(json ? `${JSON.stringify(result)}
3557
+ ` : `${JSON.stringify(result)}
3298
3558
  `);
3299
3559
  }
3300
3560
  function parseBatchManifest(bytes) {
@@ -3319,18 +3579,18 @@ async function createDecision(executor, rest, json) {
3319
3579
  input.rationale = rationale;
3320
3580
  }
3321
3581
  addOptionalWorkMetadata(input, parsed);
3322
- const result2 = await executor.execute("decision.create", input);
3323
- return ok(json ? `${JSON.stringify(result2)}
3324
- ` : `${JSON.stringify(result2)}
3582
+ const result = await executor.execute("decision.create", input);
3583
+ return ok(json ? `${JSON.stringify(result)}
3584
+ ` : `${JSON.stringify(result)}
3325
3585
  `);
3326
3586
  }
3327
3587
  async function sourceList(executor, json) {
3328
3588
  if (executor === void 0) {
3329
3589
  return fail("No Sift API executor is configured for source.list.");
3330
3590
  }
3331
- const result2 = await executor.execute("source.list", {});
3332
- return ok(json ? `${JSON.stringify(result2)}
3333
- ` : `${JSON.stringify(result2)}
3591
+ const result = await executor.execute("source.list", {});
3592
+ return ok(json ? `${JSON.stringify(result)}
3593
+ ` : `${JSON.stringify(result)}
3334
3594
  `);
3335
3595
  }
3336
3596
  async function sourceCreate(executor, rest, json) {
@@ -3338,12 +3598,12 @@ async function sourceCreate(executor, rest, json) {
3338
3598
  return fail("No Sift API executor is configured for source.create.");
3339
3599
  }
3340
3600
  const parsed = parseOptions(rest);
3341
- const result2 = await executor.execute("source.create", {
3601
+ const result = await executor.execute("source.create", {
3342
3602
  name: requireOption(parsed, "name"),
3343
3603
  visibility: [requireOption(parsed, "visibility")]
3344
3604
  });
3345
- return ok(json ? `${JSON.stringify(result2)}
3346
- ` : `${JSON.stringify(result2)}
3605
+ return ok(json ? `${JSON.stringify(result)}
3606
+ ` : `${JSON.stringify(result)}
3347
3607
  `);
3348
3608
  }
3349
3609
  async function sourceRead(executor, command, rest, json) {
@@ -3355,9 +3615,9 @@ async function sourceRead(executor, command, rest, json) {
3355
3615
  if (sourceId === void 0 || sourceId.trim().length === 0) {
3356
3616
  return fail(`Missing required source ID for ${toolName}.`);
3357
3617
  }
3358
- const result2 = await executor.execute(toolName, { sourceId });
3359
- return ok(json ? `${JSON.stringify(result2)}
3360
- ` : `${JSON.stringify(result2)}
3618
+ const result = await executor.execute(toolName, { sourceId });
3619
+ return ok(json ? `${JSON.stringify(result)}
3620
+ ` : `${JSON.stringify(result)}
3361
3621
  `);
3362
3622
  }
3363
3623
  async function ingestionStatus(executor, rest, json) {
@@ -3368,18 +3628,18 @@ async function ingestionStatus(executor, rest, json) {
3368
3628
  if (jobId === void 0 || jobId.trim().length === 0) {
3369
3629
  return fail("Missing required job ID for ingestion.status.");
3370
3630
  }
3371
- const result2 = await executor.execute("ingestion.status", { jobId });
3372
- return ok(json ? `${JSON.stringify(result2)}
3373
- ` : `${JSON.stringify(result2)}
3631
+ const result = await executor.execute("ingestion.status", { jobId });
3632
+ return ok(json ? `${JSON.stringify(result)}
3633
+ ` : `${JSON.stringify(result)}
3374
3634
  `);
3375
3635
  }
3376
3636
  async function recordList(executor, json) {
3377
3637
  if (executor === void 0) {
3378
3638
  return fail("No Sift API executor is configured for record.list.");
3379
3639
  }
3380
- const result2 = await executor.execute("record.list", {});
3381
- return ok(json ? `${JSON.stringify(result2)}
3382
- ` : `${JSON.stringify(result2)}
3640
+ const result = await executor.execute("record.list", {});
3641
+ return ok(json ? `${JSON.stringify(result)}
3642
+ ` : `${JSON.stringify(result)}
3383
3643
  `);
3384
3644
  }
3385
3645
  async function recordRead(executor, toolName, rest, json) {
@@ -3397,23 +3657,23 @@ async function recordRead(executor, toolName, rest, json) {
3397
3657
  input.sectionAnchor = sectionAnchor;
3398
3658
  }
3399
3659
  }
3400
- const result2 = await executor.execute(toolName, input);
3401
- return ok(json ? `${JSON.stringify(result2)}
3402
- ` : renderRecordResult(result2));
3660
+ const result = await executor.execute(toolName, input);
3661
+ return ok(json ? `${JSON.stringify(result)}
3662
+ ` : renderRecordResult(result));
3403
3663
  }
3404
3664
  async function createMarkdownRecord(executor, rest, json) {
3405
3665
  if (executor === void 0) {
3406
3666
  return fail("No Sift API executor is configured for record.create_markdown.");
3407
3667
  }
3408
3668
  const parsed = parseOptions(rest);
3409
- const result2 = await executor.execute("record.create_markdown", {
3669
+ const result = await executor.execute("record.create_markdown", {
3410
3670
  recordType: requireOption(parsed, "type"),
3411
3671
  title: requireOption(parsed, "title"),
3412
3672
  markdown: requireOption(parsed, "markdown"),
3413
3673
  visibility: [requireOption(parsed, "visibility")]
3414
3674
  });
3415
- return ok(json ? `${JSON.stringify(result2)}
3416
- ` : `${JSON.stringify(result2)}
3675
+ return ok(json ? `${JSON.stringify(result)}
3676
+ ` : `${JSON.stringify(result)}
3417
3677
  `);
3418
3678
  }
3419
3679
  async function patchRecordSection(executor, rest, json) {
@@ -3437,9 +3697,9 @@ async function patchRecordSection(executor, rest, json) {
3437
3697
  if (expectedMarkdown !== void 0) {
3438
3698
  input.expectedMarkdown = expectedMarkdown;
3439
3699
  }
3440
- const result2 = await executor.execute("record.patch_section", input);
3441
- return ok(json ? `${JSON.stringify(result2)}
3442
- ` : `${JSON.stringify(result2)}
3700
+ const result = await executor.execute("record.patch_section", input);
3701
+ return ok(json ? `${JSON.stringify(result)}
3702
+ ` : `${JSON.stringify(result)}
3443
3703
  `);
3444
3704
  }
3445
3705
  async function createTask(executor, rest, json) {
@@ -3468,9 +3728,9 @@ async function createTask(executor, rest, json) {
3468
3728
  input.rationale = rationale;
3469
3729
  }
3470
3730
  addOptionalWorkMetadata(input, parsed);
3471
- const result2 = await executor.execute("task.create", input);
3472
- return ok(json ? `${JSON.stringify(result2)}
3473
- ` : `${JSON.stringify(result2)}
3731
+ const result = await executor.execute("task.create", input);
3732
+ return ok(json ? `${JSON.stringify(result)}
3733
+ ` : `${JSON.stringify(result)}
3474
3734
  `);
3475
3735
  }
3476
3736
  async function auditEvents(executor, rest, json) {
@@ -3482,9 +3742,9 @@ async function auditEvents(executor, rest, json) {
3482
3742
  if (targetId !== void 0 && targetId.trim().length > 0) {
3483
3743
  input.targetId = targetId;
3484
3744
  }
3485
- const result2 = await executor.execute("audit.events", input);
3486
- return ok(json ? `${JSON.stringify(result2)}
3487
- ` : `${JSON.stringify(result2)}
3745
+ const result = await executor.execute("audit.events", input);
3746
+ return ok(json ? `${JSON.stringify(result)}
3747
+ ` : `${JSON.stringify(result)}
3488
3748
  `);
3489
3749
  }
3490
3750
  async function idTool(input) {
@@ -3495,9 +3755,9 @@ async function idTool(input) {
3495
3755
  if (id === void 0 || id.trim().length === 0) {
3496
3756
  return fail(`Missing required ${input.idLabel} for ${input.toolName}.`);
3497
3757
  }
3498
- const result2 = await input.executor.execute(input.toolName, { [input.inputKey]: id });
3499
- return ok(input.json ? `${JSON.stringify(result2)}
3500
- ` : `${JSON.stringify(result2)}
3758
+ const result = await input.executor.execute(input.toolName, { [input.inputKey]: id });
3759
+ return ok(input.json ? `${JSON.stringify(result)}
3760
+ ` : `${JSON.stringify(result)}
3501
3761
  `);
3502
3762
  }
3503
3763
 
@@ -3523,10 +3783,10 @@ async function readStoredSiftConfig(input) {
3523
3783
  return parseStoredSiftConfig(JSON.parse(raw));
3524
3784
  }
3525
3785
  async function writeStoredSiftConfig(input) {
3526
- const config2 = parseStoredSiftConfig(input.config);
3786
+ const config = parseStoredSiftConfig(input.config);
3527
3787
  const path = resolveSiftConfigPath(input);
3528
3788
  await mkdir(dirname(path), { recursive: true, mode: 448 });
3529
- await writeFile(path, `${JSON.stringify(config2, null, 2)}
3789
+ await writeFile(path, `${JSON.stringify(config, null, 2)}
3530
3790
  `, { mode: 384 });
3531
3791
  await chmod(path, 384);
3532
3792
  }
@@ -3747,14 +4007,14 @@ function createMacOSKeychainStore(input = {}) {
3747
4007
  return {
3748
4008
  async assertAvailable() {
3749
4009
  await requireSupported();
3750
- const result2 = await runCommand(securityPath, ["list-keychains"]);
3751
- if (result2.exitCode !== 0) {
4010
+ const result = await runCommand(securityPath, ["list-keychains"]);
4011
+ if (result.exitCode !== 0) {
3752
4012
  throw new UnsupportedCredentialStoreError();
3753
4013
  }
3754
4014
  },
3755
4015
  async read(readInput) {
3756
4016
  await requireSupported();
3757
- const result2 = await runCommand(securityPath, [
4017
+ const result = await runCommand(securityPath, [
3758
4018
  "find-generic-password",
3759
4019
  "-s",
3760
4020
  serviceName,
@@ -3762,15 +4022,15 @@ function createMacOSKeychainStore(input = {}) {
3762
4022
  account(readInput),
3763
4023
  "-w"
3764
4024
  ]);
3765
- if (result2.exitCode !== 0) {
4025
+ if (result.exitCode !== 0) {
3766
4026
  return void 0;
3767
4027
  }
3768
- const secret = result2.stdout.trim();
4028
+ const secret = result.stdout.trim();
3769
4029
  return secret.length === 0 ? void 0 : secret;
3770
4030
  },
3771
4031
  async write(writeInput) {
3772
4032
  await requireSupported();
3773
- const result2 = await runCommand(securityPath, [
4033
+ const result = await runCommand(securityPath, [
3774
4034
  "add-generic-password",
3775
4035
  "-U",
3776
4036
  "-s",
@@ -3780,7 +4040,7 @@ function createMacOSKeychainStore(input = {}) {
3780
4040
  "-w",
3781
4041
  writeInput.secret
3782
4042
  ]);
3783
- if (result2.exitCode !== 0) {
4043
+ if (result.exitCode !== 0) {
3784
4044
  throw new Error("Failed to write Sift CLI token secret to macOS Keychain.");
3785
4045
  }
3786
4046
  },
@@ -3798,8 +4058,8 @@ function createMacOSKeychainStore(input = {}) {
3798
4058
  }
3799
4059
  async function runSecurityCommand(file, args) {
3800
4060
  try {
3801
- const result2 = await execFileAsync(file, args);
3802
- return { stdout: result2.stdout, stderr: result2.stderr, exitCode: 0 };
4061
+ const result = await execFileAsync(file, args);
4062
+ return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
3803
4063
  } catch (error) {
3804
4064
  if (isExecError(error)) {
3805
4065
  return {
@@ -3824,6 +4084,7 @@ import { hostname as hostname3 } from "os";
3824
4084
  import { promisify as promisify2 } from "util";
3825
4085
 
3826
4086
  // src/auth/loginHelpers.ts
4087
+ var DEFAULT_SIFT_API_BASE_URL = "https://sift-wiki-api.fly.dev";
3827
4088
  async function resolveLoginApiBaseUrl(input) {
3828
4089
  const options = parseOptions(input.argv);
3829
4090
  const fromFlag = clean2(options.get("api-base-url"));
@@ -3833,7 +4094,7 @@ async function resolveLoginApiBaseUrl(input) {
3833
4094
  const stored = await readStoredSiftConfig({ homeDir: input.homeDir });
3834
4095
  const profile = stored?.profiles[stored.currentProfile];
3835
4096
  if (profile !== void 0) return normalizeUrl(profile.apiBaseUrl);
3836
- return "https://api.sift.com";
4097
+ return DEFAULT_SIFT_API_BASE_URL;
3837
4098
  }
3838
4099
  function requestedCapabilities(rest) {
3839
4100
  const option = parseOptions(rest).get("capability");
@@ -3874,15 +4135,15 @@ function resolveCliOAuthConfig(input) {
3874
4135
  return void 0;
3875
4136
  }
3876
4137
  const registrationUrl = clean3(options.get("oauth-registration-url")) ?? clean3(input.env.SIFT_OAUTH_REGISTRATION_URL);
3877
- const config2 = { authorizeUrl, tokenUrl, clientId };
4138
+ const config = { authorizeUrl, tokenUrl, clientId };
3878
4139
  if (registrationUrl !== void 0) {
3879
- config2.registrationUrl = registrationUrl;
4140
+ config.registrationUrl = registrationUrl;
3880
4141
  }
3881
4142
  const scopes = parseScopeList(clean3(options.get("oauth-scopes")) ?? clean3(input.env.SIFT_OAUTH_SCOPES));
3882
4143
  if (scopes.length > 0) {
3883
- config2.defaultScopes = scopes;
4144
+ config.defaultScopes = scopes;
3884
4145
  }
3885
- return config2;
4146
+ return config;
3886
4147
  }
3887
4148
  function scopesForCapabilities(capabilities) {
3888
4149
  const scopes = /* @__PURE__ */ new Set(["read"]);
@@ -4057,11 +4318,11 @@ async function finalizeOAuthLogin(input, tokens) {
4057
4318
  tokenKind: "oauth",
4058
4319
  refreshable: tokens.refreshToken !== void 0
4059
4320
  };
4060
- const result2 = { profile, accessToken: tokens.accessToken };
4321
+ const result = { profile, accessToken: tokens.accessToken };
4061
4322
  if (tokens.refreshToken !== void 0) {
4062
- result2.refreshToken = tokens.refreshToken;
4323
+ result.refreshToken = tokens.refreshToken;
4063
4324
  }
4064
- return result2;
4325
+ return result;
4065
4326
  }
4066
4327
  function buildAuthorizeUrl(input) {
4067
4328
  const url = new URL(input.oauth.authorizeUrl);
@@ -4294,7 +4555,7 @@ function oauthRefresherFor(input, rest) {
4294
4555
  async function oauthBrowserLoginFlow(input, rest, json) {
4295
4556
  const apiBaseUrl = await resolveLoginApiBaseUrl({ argv: rest, env: input.env, homeDir: input.homeDir });
4296
4557
  const oauth = resolveOAuthConfigOrThrow(input, rest);
4297
- const result2 = await oauthBrowserLogin({
4558
+ const result = await oauthBrowserLogin({
4298
4559
  apiBaseUrl,
4299
4560
  appBaseUrl: resolveAppBaseUrl(input.env, apiBaseUrl),
4300
4561
  oauth,
@@ -4309,16 +4570,16 @@ async function oauthBrowserLoginFlow(input, rest, json) {
4309
4570
  return persistConvergedLogin(
4310
4571
  input,
4311
4572
  {
4312
- profile: result2.profile,
4313
- accessToken: result2.accessToken,
4314
- ...result2.refreshToken === void 0 ? {} : { refreshToken: result2.refreshToken }
4573
+ profile: result.profile,
4574
+ accessToken: result.accessToken,
4575
+ ...result.refreshToken === void 0 ? {} : { refreshToken: result.refreshToken }
4315
4576
  },
4316
4577
  json
4317
4578
  );
4318
4579
  }
4319
4580
  async function serviceTokenLoginFlow(input, rest, json) {
4320
4581
  const apiBaseUrl = await resolveLoginApiBaseUrl({ argv: rest, env: input.env, homeDir: input.homeDir });
4321
- const result2 = await serviceTokenLogin({
4582
+ const result = await serviceTokenLogin({
4322
4583
  apiBaseUrl,
4323
4584
  appBaseUrl: resolveAppBaseUrl(input.env, apiBaseUrl),
4324
4585
  rest,
@@ -4327,7 +4588,7 @@ async function serviceTokenLoginFlow(input, rest, json) {
4327
4588
  credentialStore: input.credentialStore,
4328
4589
  resolveCallerBearer: defaultCallerBearerResolver(input)
4329
4590
  });
4330
- return persistConvergedLogin(input, { profile: result2.profile, accessToken: result2.token }, json);
4591
+ return persistConvergedLogin(input, { profile: result.profile, accessToken: result.token }, json);
4331
4592
  }
4332
4593
  function resolveOAuthConfigOrThrow(input, rest) {
4333
4594
  const oauth = input.oauthConfig ?? resolveCliOAuthConfig({ argv: rest, env: input.env });
@@ -4388,19 +4649,19 @@ function resolveAppBaseUrl(env, apiBaseUrl) {
4388
4649
  if (fromEnv !== void 0) return normalizeUrl(fromEnv);
4389
4650
  return apiBaseUrl.replace(/\/\/api\./u, "//");
4390
4651
  }
4391
- async function persistConvergedLogin(input, result2, json) {
4392
- const { profile } = result2;
4652
+ async function persistConvergedLogin(input, result, json) {
4653
+ const { profile } = result;
4393
4654
  try {
4394
4655
  await input.credentialStore.write({
4395
4656
  apiBaseUrl: profile.apiBaseUrl,
4396
4657
  tokenId: profile.tokenId,
4397
- secret: result2.accessToken
4658
+ secret: result.accessToken
4398
4659
  });
4399
- if (result2.refreshToken !== void 0) {
4660
+ if (result.refreshToken !== void 0) {
4400
4661
  await input.credentialStore.write({
4401
4662
  apiBaseUrl: profile.apiBaseUrl,
4402
4663
  tokenId: refreshSlotTokenId(profile.tokenId),
4403
- secret: result2.refreshToken
4664
+ secret: result.refreshToken
4404
4665
  });
4405
4666
  }
4406
4667
  } catch (error) {
@@ -4510,9 +4771,9 @@ async function deviceLogin(input, rest, sleep, json) {
4510
4771
  { requestId: request.requestId, userCode: request.userCode }
4511
4772
  );
4512
4773
  if ("token" in token) {
4513
- const result2 = await persistLogin(input, token, json);
4514
- return result2.exitCode === 0 ? { ...result2, stdout: `Code: ${request.userCode}
4515
- ${result2.stdout}` } : result2;
4774
+ const result = await persistLogin(input, token, json);
4775
+ return result.exitCode === 0 ? { ...result, stdout: `Code: ${request.userCode}
4776
+ ${result.stdout}` } : result;
4516
4777
  }
4517
4778
  if (token.status === "authorization_pending" || token.status === "slow_down") {
4518
4779
  intervalSeconds = token.intervalSeconds;
@@ -4536,8 +4797,8 @@ async function persistLogin(input, token, json) {
4536
4797
  `Sift CLI login storage failure: ${error instanceof Error ? error.message : "credential store write failed"}`
4537
4798
  );
4538
4799
  }
4539
- const config2 = configFromToken(token);
4540
- await writeStoredSiftConfig({ homeDir: input.homeDir, config: config2 });
4800
+ const config = configFromToken(token);
4801
+ await writeStoredSiftConfig({ homeDir: input.homeDir, config });
4541
4802
  if (oldProfile !== void 0) {
4542
4803
  const oldSecret = await input.credentialStore.read({
4543
4804
  apiBaseUrl: oldProfile.apiBaseUrl,
@@ -4576,6 +4837,33 @@ async function authStatus(input, now, json) {
4576
4837
  if (profile === void 0) {
4577
4838
  return ok(json ? '{"auth":"none"}\n' : "Auth: none\n");
4578
4839
  }
4840
+ const expired = Date.parse(profile.tokenExpiresAt) <= now.getTime();
4841
+ if (expired) {
4842
+ return staleStoredStatus(
4843
+ profile,
4844
+ "expired",
4845
+ "Stored Sift CLI auth has expired; run `sift login` again.",
4846
+ json
4847
+ );
4848
+ }
4849
+ let secret;
4850
+ try {
4851
+ secret = await input.credentialStore.read({
4852
+ apiBaseUrl: profile.apiBaseUrl,
4853
+ tokenId: profile.tokenId
4854
+ });
4855
+ } catch (error) {
4856
+ const message = error instanceof Error ? error.message : "Stored Sift credential store could not be read; run `sift login` again.";
4857
+ return staleStoredStatus(profile, "credential_store_unavailable", message, json);
4858
+ }
4859
+ if (secret === void 0) {
4860
+ return staleStoredStatus(
4861
+ profile,
4862
+ "credential_missing",
4863
+ "Stored Sift credential store secret is missing; run `sift login` again.",
4864
+ json
4865
+ );
4866
+ }
4579
4867
  return ok(
4580
4868
  json ? `${JSON.stringify({ auth: "stored", ...profile })}
4581
4869
  ` : [
@@ -4590,6 +4878,24 @@ async function authStatus(input, now, json) {
4590
4878
  ].join("\n")
4591
4879
  );
4592
4880
  }
4881
+ function staleStoredStatus(profile, reason, message, json) {
4882
+ if (json) {
4883
+ return ok(
4884
+ `${JSON.stringify({ auth: "stored", status: "stale", reason, message, ...profile })}
4885
+ `
4886
+ );
4887
+ }
4888
+ return ok(
4889
+ [
4890
+ "Auth: stale",
4891
+ `Reason: ${reason}`,
4892
+ message,
4893
+ `API: ${profile.apiBaseUrl}`,
4894
+ `Token: ${profile.tokenLabel}`,
4895
+ ""
4896
+ ].join("\n")
4897
+ );
4898
+ }
4593
4899
  async function logout(input, now, json) {
4594
4900
  if (clean2(input.env.SIFT_API_TOKEN) !== void 0) {
4595
4901
  return ok(json ? '{"status":"env_auth_active"}\n' : "Auth: env\nUnset SIFT_API_TOKEN to log out.\n");
@@ -4681,6 +4987,249 @@ function failJson(message) {
4681
4987
  };
4682
4988
  }
4683
4989
 
4990
+ // src/roamMcpReader.ts
4991
+ import { spawn } from "child_process";
4992
+ function createRoamMcpReader(input = {}) {
4993
+ return {
4994
+ async exportPages(request) {
4995
+ const client = createRoamMcpJsonLineClient({
4996
+ command: input.command ?? "npx",
4997
+ args: input.args ?? ["-y", "@roam-research/roam-mcp"],
4998
+ spawnProcess: input.spawnProcess ?? spawn
4999
+ });
5000
+ try {
5001
+ return await exportRoamPagesFromMcp(client, request);
5002
+ } finally {
5003
+ await client.close();
5004
+ }
5005
+ }
5006
+ };
5007
+ }
5008
+ async function exportRoamPagesFromMcp(client, input) {
5009
+ const graphId = await resolveGraphId(client, input.graph);
5010
+ await client.callTool("get_graph_guidelines", graphArgs(graphId)).catch(() => void 0);
5011
+ const tuples = input.scope === "sift_tag" ? await queryMarkedPageTuples(client, graphId, input.limit) : await queryWholeGraphPageTuples(client, graphId, input.limit);
5012
+ const records = [];
5013
+ const seen = /* @__PURE__ */ new Set();
5014
+ for (const tuple of tuples) {
5015
+ const key = tuple.uid ?? tuple.title;
5016
+ if (seen.has(key)) continue;
5017
+ seen.add(key);
5018
+ const markdown = await fetchPageMarkdown(client, graphId, tuple);
5019
+ records.push({
5020
+ graphId,
5021
+ pageUid: tuple.uid ?? stableFallbackUid(tuple.title),
5022
+ pageTitle: tuple.title,
5023
+ markdown,
5024
+ scope: input.scope,
5025
+ blockCount: estimateBlockCount(markdown),
5026
+ importedAt: input.now.toISOString()
5027
+ });
5028
+ }
5029
+ return records;
5030
+ }
5031
+ async function resolveGraphId(client, graph) {
5032
+ if (graph !== void 0) return graph;
5033
+ const result = await client.callTool("list_graphs", {});
5034
+ const parsed = parseToolJson(result);
5035
+ const graphId = firstGraphId(parsed);
5036
+ if (graphId !== void 0) return graphId;
5037
+ const message = errorMessageFromParsedTool(parsed);
5038
+ if (message !== void 0) throw new Error(message);
5039
+ return "local-graph";
5040
+ }
5041
+ async function queryMarkedPageTuples(client, graphId, limit) {
5042
+ const result = await client.callTool("datalog_query", {
5043
+ ...graphArgs(graphId),
5044
+ query: '[:find ?title ?uid :where [?tag :node/title "Sift"] [?block :block/refs ?tag] [?block :block/page ?page] [?page :node/title ?title] [?page :block/uid ?uid]]'
5045
+ });
5046
+ return tuplesFromToolResult(result).slice(0, limit);
5047
+ }
5048
+ async function queryWholeGraphPageTuples(client, graphId, limit) {
5049
+ const result = await client.callTool("datalog_query", {
5050
+ ...graphArgs(graphId),
5051
+ query: "[:find ?title ?uid :where [?page :node/title ?title] [?page :block/uid ?uid]]"
5052
+ });
5053
+ return tuplesFromToolResult(result).slice(0, limit);
5054
+ }
5055
+ async function fetchPageMarkdown(client, graphId, tuple) {
5056
+ const result = await client.callTool("get_page", {
5057
+ ...graphArgs(graphId),
5058
+ ...tuple.uid === void 0 ? { title: tuple.title } : { uid: tuple.uid }
5059
+ });
5060
+ const text = toolText(result);
5061
+ const parsed = parseJsonIfPossible(text);
5062
+ const markdown = markdownFromParsed(parsed) ?? text;
5063
+ return stripRoamMetadataTags(markdown).trim();
5064
+ }
5065
+ function graphArgs(graphId) {
5066
+ return { graph: graphId };
5067
+ }
5068
+ function tuplesFromToolResult(result) {
5069
+ const parsed = parseToolJson(result);
5070
+ const values = candidateArrays(parsed);
5071
+ const tuples = [];
5072
+ for (const value of values) {
5073
+ if (Array.isArray(value) && typeof value[0] === "string") {
5074
+ tuples.push({ title: value[0], ...typeof value[1] === "string" ? { uid: value[1] } : {} });
5075
+ } else if (isRecord(value)) {
5076
+ const title = stringProperty(value, ["title", "pageTitle", "name"]);
5077
+ const uid = stringProperty(value, ["uid", "pageUid"]);
5078
+ if (title !== void 0) tuples.push({ title, ...uid === void 0 ? {} : { uid } });
5079
+ }
5080
+ }
5081
+ return tuples;
5082
+ }
5083
+ function parseToolJson(result) {
5084
+ const text = toolText(result);
5085
+ return parseJsonIfPossible(text);
5086
+ }
5087
+ function toolText(result) {
5088
+ if (isRecord(result) && Array.isArray(result.content)) {
5089
+ return result.content.flatMap((item) => isRecord(item) && typeof item.text === "string" ? [item.text] : []).join("\n").trim();
5090
+ }
5091
+ return typeof result === "string" ? result : JSON.stringify(result);
5092
+ }
5093
+ function parseJsonIfPossible(text) {
5094
+ try {
5095
+ return JSON.parse(text);
5096
+ } catch {
5097
+ return text;
5098
+ }
5099
+ }
5100
+ function candidateArrays(value) {
5101
+ if (Array.isArray(value)) return value;
5102
+ if (!isRecord(value)) return [];
5103
+ for (const key of ["results", "result", "data", "rows", "pages"]) {
5104
+ const candidate = value[key];
5105
+ if (Array.isArray(candidate)) return candidate;
5106
+ }
5107
+ return [];
5108
+ }
5109
+ function markdownFromParsed(value) {
5110
+ if (typeof value === "string") return value;
5111
+ if (!isRecord(value)) return void 0;
5112
+ return stringProperty(value, ["markdown", "content", "text"]);
5113
+ }
5114
+ function firstGraphId(value) {
5115
+ const graphs = isRecord(value) && Array.isArray(value.graphs) ? value.graphs : candidateArrays(value);
5116
+ for (const graph of graphs) {
5117
+ if (!isRecord(graph)) continue;
5118
+ const id = stringProperty(graph, ["nickname", "graph", "name", "id"]);
5119
+ if (id !== void 0) return id;
5120
+ }
5121
+ return void 0;
5122
+ }
5123
+ function errorMessageFromParsedTool(value) {
5124
+ if (!isRecord(value) || !isRecord(value.error)) return void 0;
5125
+ const message = value.error.message;
5126
+ return typeof message === "string" ? message : void 0;
5127
+ }
5128
+ function stringProperty(record, keys) {
5129
+ for (const key of keys) {
5130
+ const value = record[key];
5131
+ if (typeof value === "string" && value.trim().length > 0) return value;
5132
+ }
5133
+ return void 0;
5134
+ }
5135
+ function stripRoamMetadataTags(markdown) {
5136
+ return markdown.replace(/<roam\b[^>]*>/giu, "").replace(/<\/roam>/giu, "");
5137
+ }
5138
+ function estimateBlockCount(markdown) {
5139
+ const lines = markdown.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
5140
+ return Math.max(1, lines.length);
5141
+ }
5142
+ function stableFallbackUid(title) {
5143
+ return title.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 80);
5144
+ }
5145
+ function isRecord(value) {
5146
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5147
+ }
5148
+ function createRoamMcpJsonLineClient(input) {
5149
+ const child = input.spawnProcess(input.command, input.args, {
5150
+ stdio: ["pipe", "pipe", "pipe"]
5151
+ });
5152
+ child.stderr.resume();
5153
+ child.stdout.setEncoding("utf8");
5154
+ let nextId = 1;
5155
+ let buffer = "";
5156
+ const pending = /* @__PURE__ */ new Map();
5157
+ child.stdout.on("data", (chunk) => {
5158
+ buffer += chunk;
5159
+ let newline = buffer.indexOf("\n");
5160
+ while (newline >= 0) {
5161
+ const line = buffer.slice(0, newline).trim();
5162
+ buffer = buffer.slice(newline + 1);
5163
+ if (line.length > 0) handleJsonRpcLine(line, pending);
5164
+ newline = buffer.indexOf("\n");
5165
+ }
5166
+ });
5167
+ child.on("error", (error) => {
5168
+ for (const entry of pending.values()) entry.reject(error);
5169
+ pending.clear();
5170
+ });
5171
+ child.on("exit", () => {
5172
+ for (const entry of pending.values()) entry.reject(new Error("Roam MCP server exited."));
5173
+ pending.clear();
5174
+ });
5175
+ const request = (method, params) => {
5176
+ const id = nextId;
5177
+ nextId += 1;
5178
+ const promise = new Promise((resolve2, reject) => {
5179
+ pending.set(id, { resolve: resolve2, reject });
5180
+ });
5181
+ child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
5182
+ `);
5183
+ return promise;
5184
+ };
5185
+ const initialized = request("initialize", {
5186
+ protocolVersion: "2025-11-25",
5187
+ capabilities: {},
5188
+ clientInfo: { name: "sift-roam-import", version: "0.1.0" }
5189
+ }).then(() => {
5190
+ child.stdin.write(
5191
+ `${JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })}
5192
+ `
5193
+ );
5194
+ });
5195
+ return {
5196
+ async callTool(name, args) {
5197
+ await initialized;
5198
+ return request("tools/call", { name, arguments: args });
5199
+ },
5200
+ async close() {
5201
+ child.stdin.end();
5202
+ child.kill("SIGTERM");
5203
+ }
5204
+ };
5205
+ }
5206
+ function handleJsonRpcLine(line, pending) {
5207
+ let parsed;
5208
+ try {
5209
+ parsed = JSON.parse(line);
5210
+ } catch {
5211
+ return;
5212
+ }
5213
+ if (typeof parsed.id !== "number") return;
5214
+ const entry = pending.get(parsed.id);
5215
+ if (entry === void 0) return;
5216
+ pending.delete(parsed.id);
5217
+ if (parsed.error !== void 0) {
5218
+ entry.reject(
5219
+ new Error(
5220
+ typeof parsed.error.message === "string" ? parsed.error.message : "Roam MCP error."
5221
+ )
5222
+ );
5223
+ return;
5224
+ }
5225
+ entry.resolve(parsed.result);
5226
+ }
5227
+
5228
+ // src/startupAuthMessage.ts
5229
+ function formatMissingStartupAuthMessage(_args) {
5230
+ return "Not signed in. Run 'npx -y @sift-wiki/cli@latest login', then retry this command.";
5231
+ }
5232
+
4684
5233
  // src/bin/sift.ts
4685
5234
  var credentialStore = createMacOSKeychainStore();
4686
5235
  var authCommands = createSiftCliAuthCommands({
@@ -4689,51 +5238,86 @@ var authCommands = createSiftCliAuthCommands({
4689
5238
  credentialStore,
4690
5239
  fetch
4691
5240
  });
4692
- var loadedAuth = await authCommands.loadAuth();
4693
- var config = loadedAuth?.config ?? {
4694
- apiBaseUrl: "",
4695
- tokenLabel: "unset",
4696
- workspaceId: "",
4697
- brainId: "",
4698
- principalId: "",
4699
- capabilities: []
4700
- };
4701
5241
  var { argv, agentName } = extractAgentName(process.argv.slice(2), process.env.SIFT_AGENT);
4702
- var result = await runSiftCli({
4703
- argv,
4704
- config,
4705
- readStdin,
4706
- agentName,
4707
- executor: loadedAuth === void 0 ? void 0 : createHostedApiExecutor({
4708
- apiBaseUrl: loadedAuth.config.apiBaseUrl,
4709
- token: loadedAuth.token,
4710
- workspaceId: loadedAuth.config.workspaceId,
4711
- brainId: loadedAuth.config.brainId,
4712
- agentName
4713
- }),
4714
- authCommands,
4715
- mcpServer: {
4716
- serve: async ({ config: config2, executor }) => {
4717
- if (executor === void 0) {
4718
- throw new Error(
4719
- "Not signed in. Run 'sift login' to authenticate, then 'sift mcp serve' to start the local MCP server."
4720
- );
5242
+ var startupAuth = await loadAuthForStartup(authCommands, argv);
5243
+ if (startupAuth.ok === false) {
5244
+ process.stderr.write(`${startupAuth.message}
5245
+ `);
5246
+ process.exitCode = 1;
5247
+ } else {
5248
+ const loadedAuth = startupAuth.loadedAuth;
5249
+ const config = loadedAuth?.config ?? {
5250
+ apiBaseUrl: "",
5251
+ tokenLabel: "unset",
5252
+ workspaceId: "",
5253
+ brainId: "",
5254
+ principalId: "",
5255
+ capabilities: []
5256
+ };
5257
+ const result = await runSiftCli({
5258
+ argv,
5259
+ config,
5260
+ readStdin,
5261
+ agentName,
5262
+ executor: loadedAuth === void 0 ? void 0 : createHostedApiExecutor({
5263
+ apiBaseUrl: loadedAuth.config.apiBaseUrl,
5264
+ token: loadedAuth.token,
5265
+ workspaceId: loadedAuth.config.workspaceId,
5266
+ brainId: loadedAuth.config.brainId,
5267
+ agentName
5268
+ }),
5269
+ roamReader: createRoamMcpReader(),
5270
+ roamImporter: loadedAuth === void 0 ? void 0 : createSiftRoamImportClient({
5271
+ apiBaseUrl: loadedAuth.config.apiBaseUrl,
5272
+ token: loadedAuth.token,
5273
+ workspaceId: loadedAuth.config.workspaceId
5274
+ }),
5275
+ authCommands,
5276
+ mcpServer: {
5277
+ serve: async ({ config: config2, executor }) => {
5278
+ if (executor === void 0) {
5279
+ throw new Error(
5280
+ "Not signed in. Run 'sift login' to authenticate, then 'sift mcp serve' to start the local MCP server."
5281
+ );
5282
+ }
5283
+ const { createLocalMcpStdioServer: createLocalMcpStdioServer2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
5284
+ return createLocalMcpStdioServer2({
5285
+ input: process.stdin,
5286
+ output: process.stdout,
5287
+ error: process.stderr
5288
+ }).serve({
5289
+ capabilities: config2.capabilities,
5290
+ executor
5291
+ });
4721
5292
  }
4722
- const { createLocalMcpStdioServer: createLocalMcpStdioServer2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
4723
- return createLocalMcpStdioServer2({
4724
- input: process.stdin,
4725
- output: process.stdout,
4726
- error: process.stderr
4727
- }).serve({
4728
- capabilities: config2.capabilities,
4729
- executor
4730
- });
4731
5293
  }
5294
+ });
5295
+ process.stdout.write(result.stdout);
5296
+ process.stderr.write(result.stderr);
5297
+ process.exitCode = result.exitCode;
5298
+ }
5299
+ async function loadAuthForStartup(commands, args) {
5300
+ try {
5301
+ const loadedAuth = await commands.loadAuth();
5302
+ if (loadedAuth === void 0 && !canRunWithoutLoadedAuth(args)) {
5303
+ return { ok: false, message: formatMissingStartupAuthMessage(args) };
5304
+ }
5305
+ return { ok: true, loadedAuth };
5306
+ } catch (error) {
5307
+ if (canRunWithoutLoadedAuth(args)) {
5308
+ return { ok: true, loadedAuth: void 0 };
5309
+ }
5310
+ return {
5311
+ ok: false,
5312
+ message: error instanceof Error ? error.message : "Failed to load Sift CLI auth."
5313
+ };
4732
5314
  }
4733
- });
4734
- process.stdout.write(result.stdout);
4735
- process.stderr.write(result.stderr);
4736
- process.exitCode = result.exitCode;
5315
+ }
5316
+ function canRunWithoutLoadedAuth(args) {
5317
+ const commandArgs = args.filter((arg) => arg !== "--json");
5318
+ const [group, command] = commandArgs;
5319
+ return group === void 0 || group === "help" || group === "--help" || group === "doctor" || group === "skill" || group === "login" || group === "logout" || group === "auth" && command === "status";
5320
+ }
4737
5321
  function extractAgentName(args, envAgentName) {
4738
5322
  const flagIndex = args.indexOf("--as-agent");
4739
5323
  if (flagIndex !== -1 && args[flagIndex + 1] !== void 0) {