@zleap-ai/sag-cli 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { confirm, password, select } from "@inquirer/prompts";
4
+ import { confirm, input, select } from "@inquirer/prompts";
5
5
 
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@zleap-ai/sag-cli",
9
- version: "0.2.1",
9
+ version: "0.4.0",
10
10
  description: "Command-line client and diagnostics for SAG knowledge bases",
11
11
  type: "module",
12
12
  bin: {
@@ -16,7 +16,8 @@ var package_default = {
16
16
  "dist",
17
17
  "README.md",
18
18
  "README.en.md",
19
- "LICENSE"
19
+ "LICENSE",
20
+ "skill"
20
21
  ],
21
22
  scripts: {
22
23
  build: "tsup",
@@ -176,7 +177,7 @@ var claudeEntrySchema = z.object({
176
177
  function invalidSpec() {
177
178
  return new CliError(
178
179
  "INVALID_ARGUMENT",
179
- "Claude Code v0.2 supports Docker stdio MCP connections only",
180
+ "The Claude Code adapter supports Docker stdio MCP connections only",
180
181
  {
181
182
  exitCode: exitCodes.invalidArgument
182
183
  }
@@ -205,6 +206,12 @@ var ClaudeCodeAdapter = class {
205
206
  }
206
207
  runner;
207
208
  id = "claude-code";
209
+ capabilities = {
210
+ transports: ["stdio"],
211
+ scopes: ["user"],
212
+ structuredRead: true,
213
+ projectApproval: false
214
+ };
208
215
  readConfig;
209
216
  async detect() {
210
217
  try {
@@ -293,7 +300,7 @@ var CODEX_STATUS_FIELDS = /* @__PURE__ */ new Set(["name", "disabled_reason", "a
293
300
  function invalidSpec2() {
294
301
  return new CliError(
295
302
  "INVALID_ARGUMENT",
296
- "Codex v0.2 supports Docker stdio MCP connections only",
303
+ "The Codex adapter supports Docker stdio MCP connections only",
297
304
  {
298
305
  exitCode: exitCodes.invalidArgument
299
306
  }
@@ -317,6 +324,12 @@ var CodexAdapter = class {
317
324
  }
318
325
  runner;
319
326
  id = "codex";
327
+ capabilities = {
328
+ transports: ["stdio"],
329
+ scopes: ["user"],
330
+ structuredRead: true,
331
+ projectApproval: false
332
+ };
320
333
  async detect() {
321
334
  try {
322
335
  const result = await this.runner.run({
@@ -533,9 +546,9 @@ var ManagedConnectionStore = class {
533
546
  await this.#saveUnlocked(state);
534
547
  });
535
548
  }
536
- async list(input) {
549
+ async list(input2) {
537
550
  return Object.values((await this.load()).connections).filter(
538
- (connection) => (!input?.agent || connection.agent === input.agent) && (!input?.scope || connection.scope === input.scope)
551
+ (connection) => (!input2?.agent || connection.agent === input2.agent) && (!input2?.scope || connection.scope === input2.scope)
539
552
  );
540
553
  }
541
554
  };
@@ -557,12 +570,12 @@ function validateProfileName(name) {
557
570
  }
558
571
  return name;
559
572
  }
560
- function normalizeOrigin(input) {
573
+ function normalizeOrigin(input2) {
561
574
  let url;
562
575
  try {
563
- url = new URL(input);
576
+ url = new URL(input2);
564
577
  } catch (cause) {
565
- throw new CliError("INVALID_ARGUMENT", `Invalid SAG URL: ${input}`, {
578
+ throw new CliError("INVALID_ARGUMENT", `Invalid SAG URL: ${input2}`, {
566
579
  exitCode: exitCodes.invalidArgument,
567
580
  cause,
568
581
  hint: "Use an absolute HTTP or HTTPS URL such as http://localhost:8000."
@@ -1089,16 +1102,6 @@ var McpVerifier = class {
1089
1102
  }
1090
1103
  cliVersion;
1091
1104
  async verify(spec, options) {
1092
- if (spec.transport !== "stdio") {
1093
- throw new CliError(
1094
- "INVALID_ARGUMENT",
1095
- "HTTP MCP is not supported in SAG CLI v0.2",
1096
- {
1097
- exitCode: exitCodes.invalidArgument,
1098
- hint: "Use a local Docker stdio connection."
1099
- }
1100
- );
1101
- }
1102
1105
  const client = new Client(
1103
1106
  {
1104
1107
  name: "@zleap-ai/sag-cli",
@@ -1190,12 +1193,12 @@ var McpVerifier = class {
1190
1193
  // src/process/runner.ts
1191
1194
  import { spawn } from "child_process";
1192
1195
  var NodeProcessRunner = class {
1193
- async run(input) {
1196
+ async run(input2) {
1194
1197
  return new Promise((resolve, reject) => {
1195
- const child = spawn(input.command, input.args, {
1198
+ const child = spawn(input2.command, input2.args, {
1196
1199
  shell: false,
1197
- env: input.env ?? process.env,
1198
- stdio: [input.stdin === "pipe" ? "pipe" : "ignore", "pipe", "pipe"]
1200
+ env: input2.env ?? process.env,
1201
+ stdio: [input2.stdin === "pipe" ? "pipe" : "ignore", "pipe", "pipe"]
1199
1202
  });
1200
1203
  const childStdout = child.stdout;
1201
1204
  const childStderr = child.stderr;
@@ -1209,7 +1212,7 @@ var NodeProcessRunner = class {
1209
1212
  forceKill = setTimeout(() => {
1210
1213
  child.kill("SIGKILL");
1211
1214
  }, 250);
1212
- }, input.timeoutMs);
1215
+ }, input2.timeoutMs);
1213
1216
  childStdout?.setEncoding("utf8");
1214
1217
  childStderr?.setEncoding("utf8");
1215
1218
  childStdout?.on("data", (chunk) => {
@@ -1222,10 +1225,10 @@ var NodeProcessRunner = class {
1222
1225
  clearTimeout(timeout);
1223
1226
  if (forceKill) clearTimeout(forceKill);
1224
1227
  reject(
1225
- new CliError("DEPENDENCY_MISSING", `Unable to start ${input.command}`, {
1228
+ new CliError("DEPENDENCY_MISSING", `Unable to start ${input2.command}`, {
1226
1229
  exitCode: exitCodes.dependencyMissing,
1227
1230
  cause,
1228
- hint: `Install ${input.command} and ensure it is available on PATH.`
1231
+ hint: `Install ${input2.command} and ensure it is available on PATH.`
1229
1232
  })
1230
1233
  );
1231
1234
  });
@@ -1234,9 +1237,9 @@ var NodeProcessRunner = class {
1234
1237
  if (forceKill) clearTimeout(forceKill);
1235
1238
  if (timedOut) {
1236
1239
  reject(
1237
- new CliError("DEPENDENCY_MISSING", `${input.command} timed out`, {
1240
+ new CliError("DEPENDENCY_MISSING", `${input2.command} timed out`, {
1238
1241
  exitCode: exitCodes.dependencyMissing,
1239
- hint: `Check that ${input.command} is responsive and try again.`
1242
+ hint: `Check that ${input2.command} is responsive and try again.`
1240
1243
  })
1241
1244
  );
1242
1245
  return;
@@ -1273,6 +1276,10 @@ var userSchema = z6.object({
1273
1276
  name: z6.string(),
1274
1277
  created_at: dateTime.optional()
1275
1278
  }).passthrough();
1279
+ var loginResponseSchema = z6.object({
1280
+ access_token: z6.string().min(1),
1281
+ user: userSchema
1282
+ }).passthrough();
1276
1283
  var sourceSchema = z6.object({
1277
1284
  id: z6.string().min(1),
1278
1285
  name: z6.string(),
@@ -1354,6 +1361,43 @@ var mcpDescriptorSchema = z6.object({
1354
1361
  headers: z6.record(z6.string(), z6.string())
1355
1362
  }).passthrough()
1356
1363
  }).passthrough();
1364
+ var outlineItemSchema = z6.object({
1365
+ rank: z6.number().int(),
1366
+ heading: z6.string(),
1367
+ chunk_id: z6.string()
1368
+ }).passthrough();
1369
+ var outlineSchema = z6.object({
1370
+ document_id: z6.string(),
1371
+ filename: z6.string(),
1372
+ outline: z6.array(outlineItemSchema)
1373
+ }).passthrough();
1374
+ var grepMatchSchema = z6.object({
1375
+ chunk_id: z6.string(),
1376
+ heading: z6.string(),
1377
+ snippet: z6.string()
1378
+ }).passthrough();
1379
+ var grepResponseSchema = z6.object({
1380
+ pattern: z6.string(),
1381
+ matches: z6.array(grepMatchSchema),
1382
+ count: z6.number().int().nonnegative()
1383
+ }).passthrough();
1384
+ var readResponseSchema = z6.object({
1385
+ document_id: z6.string(),
1386
+ filename: z6.string(),
1387
+ total_lines: z6.number().int().nonnegative(),
1388
+ offset: z6.number().int().positive(),
1389
+ limit: z6.number().int().positive(),
1390
+ lines: z6.array(z6.string())
1391
+ }).passthrough();
1392
+ var entityContextSchema = z6.object({
1393
+ entity_id: z6.string(),
1394
+ name: z6.string(),
1395
+ type: z6.string(),
1396
+ description: z6.string(),
1397
+ context: z6.string(),
1398
+ source_id: z6.string(),
1399
+ source_name: z6.string()
1400
+ }).passthrough();
1357
1401
 
1358
1402
  // src/api/client.ts
1359
1403
  var SagClient = class {
@@ -1385,6 +1429,14 @@ var SagClient = class {
1385
1429
  me() {
1386
1430
  return this.#request("/api/v1/auth/me", userSchema);
1387
1431
  }
1432
+ async login(name) {
1433
+ const response = await this.#request("/api/v1/auth/login", loginResponseSchema, {
1434
+ authenticated: false,
1435
+ method: "POST",
1436
+ body: { name }
1437
+ });
1438
+ return { accessToken: response.access_token, user: response.user };
1439
+ }
1388
1440
  mcpDescriptor() {
1389
1441
  return this.#request("/api/v1/system/mcp", mcpDescriptorSchema);
1390
1442
  }
@@ -1409,12 +1461,48 @@ var SagClient = class {
1409
1461
  documentSchema
1410
1462
  );
1411
1463
  }
1412
- search(input) {
1464
+ search(input2) {
1413
1465
  return this.#request("/api/v1/search", searchResponseSchema, {
1414
1466
  method: "POST",
1415
- body: input
1467
+ body: input2
1416
1468
  });
1417
1469
  }
1470
+ outline(sourceId, documentId) {
1471
+ const params = new URLSearchParams({ document_id: documentId });
1472
+ return this.#request(
1473
+ `/api/v1/sources/${encodeURIComponent(sourceId)}/outline?${params}`,
1474
+ outlineSchema
1475
+ );
1476
+ }
1477
+ grep(sourceId, pattern, limit) {
1478
+ const params = new URLSearchParams({ pattern });
1479
+ if (limit !== void 0) {
1480
+ params.set("limit", String(limit));
1481
+ }
1482
+ return this.#request(
1483
+ `/api/v1/sources/${encodeURIComponent(sourceId)}/grep?${params}`,
1484
+ grepResponseSchema
1485
+ );
1486
+ }
1487
+ readDocument(sourceId, documentId, offset, limit) {
1488
+ const params = new URLSearchParams();
1489
+ if (offset !== void 0) {
1490
+ params.set("offset", String(offset));
1491
+ }
1492
+ if (limit !== void 0) {
1493
+ params.set("limit", String(limit));
1494
+ }
1495
+ return this.#request(
1496
+ `/api/v1/sources/${encodeURIComponent(sourceId)}/documents/${encodeURIComponent(documentId)}/read?${params}`,
1497
+ readResponseSchema
1498
+ );
1499
+ }
1500
+ getEntityContext(sourceId, name) {
1501
+ return this.#request(
1502
+ `/api/v1/sources/${encodeURIComponent(sourceId)}/entities/${encodeURIComponent(name)}/context`,
1503
+ entityContextSchema
1504
+ );
1505
+ }
1418
1506
  async #request(path5, schema, options = {}) {
1419
1507
  const authenticated = options.authenticated ?? true;
1420
1508
  if (authenticated && !this.#token) {
@@ -1529,81 +1617,98 @@ function hostFailure3(message, cause, hint) {
1529
1617
  hint: hint ?? "Inspect the Agent MCP configuration and run the command again."
1530
1618
  });
1531
1619
  }
1532
- function determinePlan(input) {
1620
+ function determinePlan(input2) {
1533
1621
  let action;
1534
- if (!input.hostFingerprint || !input.hostSpecFingerprint) {
1535
- if (input.managed) {
1622
+ if (!input2.hostFingerprint || !input2.hostSpecFingerprint) {
1623
+ if (input2.managed) {
1536
1624
  throw conflict("Managed state exists but the Agent MCP entry is missing");
1537
1625
  }
1538
1626
  action = "create";
1539
- } else if (!input.managed) {
1627
+ } else if (!input2.managed) {
1540
1628
  throw conflict("The Agent MCP name is already owned by the user");
1541
- } else if (input.managed.fingerprint !== input.hostFingerprint) {
1629
+ } else if (input2.managed.fingerprint !== input2.hostFingerprint) {
1542
1630
  throw conflict("The managed Agent MCP entry was changed outside SAG CLI");
1543
- } else if (input.hostSpecFingerprint === input.desiredFingerprint) {
1631
+ } else if (input2.hostSpecFingerprint === input2.desiredFingerprint) {
1544
1632
  action = "unchanged";
1545
1633
  } else {
1546
1634
  action = "update";
1547
1635
  }
1548
1636
  return {
1549
1637
  action,
1550
- agent: input.agent,
1551
- serverName: input.serverName,
1552
- scope: input.scope,
1638
+ agent: input2.agent,
1639
+ serverName: input2.serverName,
1640
+ scope: input2.scope,
1553
1641
  provider: "docker-stdio",
1554
- target: input.target.name,
1555
- sourceId: input.sourceId ?? null,
1556
- ...input.hostSpecFingerprint ? { currentFingerprint: input.hostSpecFingerprint } : {},
1557
- desiredFingerprint: input.desiredFingerprint
1642
+ target: input2.target.name,
1643
+ sourceId: input2.sourceId ?? null,
1644
+ ...input2.hostSpecFingerprint ? { currentFingerprint: input2.hostSpecFingerprint } : {},
1645
+ desiredFingerprint: input2.desiredFingerprint
1558
1646
  };
1559
1647
  }
1560
- function managedConnection(input, resolved, fingerprint, existing) {
1648
+ function managedConnection(input2, resolved, fingerprint, existing) {
1561
1649
  return {
1562
- agent: input.agent,
1650
+ agent: input2.agent,
1563
1651
  provider: "docker-stdio",
1564
- profile: input.profile,
1565
- serverName: input.serverName,
1652
+ profile: input2.profile,
1653
+ serverName: input2.serverName,
1566
1654
  docker: {
1567
1655
  ...resolved.target.composeProject ? { composeProject: resolved.target.composeProject } : {},
1568
1656
  ...resolved.target.composeService ? { composeService: resolved.target.composeService } : {},
1569
1657
  containerName: resolved.target.name
1570
1658
  },
1571
- sourceId: input.sourceId ?? null,
1572
- scope: input.scope,
1659
+ sourceId: input2.sourceId ?? null,
1660
+ scope: input2.scope,
1573
1661
  fingerprint,
1574
- createdAt: existing?.createdAt ?? input.now().toISOString()
1662
+ createdAt: existing?.createdAt ?? input2.now().toISOString()
1575
1663
  };
1576
1664
  }
1577
- async function rollbackHost(input) {
1578
- if (input.previousSpec) {
1665
+ async function rollbackHost(input2) {
1666
+ if (input2.previousSpec) {
1579
1667
  try {
1580
- await input.adapter.remove(input.serverName, input.scope);
1668
+ await input2.adapter.remove(input2.serverName, input2.scope);
1581
1669
  } catch {
1582
1670
  }
1583
- await input.adapter.add(input.serverName, input.scope, input.previousSpec);
1584
- const restored = await input.adapter.read(input.serverName, input.scope);
1585
- if (!restored || restored.fingerprint !== (input.previousFingerprint ?? fingerprintConnection(input.previousSpec))) {
1671
+ await input2.adapter.add(input2.serverName, input2.scope, input2.previousSpec);
1672
+ const restored = await input2.adapter.read(input2.serverName, input2.scope);
1673
+ if (!restored || restored.fingerprint !== (input2.previousFingerprint ?? fingerprintConnection(input2.previousSpec))) {
1586
1674
  throw new Error("Previous Agent MCP entry was not restored");
1587
1675
  }
1588
1676
  return;
1589
1677
  }
1590
- await input.adapter.remove(input.serverName, input.scope);
1678
+ await input2.adapter.remove(input2.serverName, input2.scope);
1591
1679
  }
1592
- async function connectAgent(input) {
1593
- validateServerName(input.serverName);
1594
- const resolved = await input.resolveConnection();
1595
- await input.verifier.verify(resolved.spec, { timeoutMs: input.timeoutMs });
1596
- await input.adapter.detect();
1597
- const host = await input.adapter.read(input.serverName, input.scope);
1598
- const key = connectionKey(input.agent, input.scope, input.serverName);
1599
- const managed = await input.state.get(key);
1680
+ async function connectAgent(input2) {
1681
+ validateServerName(input2.serverName);
1682
+ const resolved = await input2.resolveConnection();
1683
+ if (!input2.adapter.capabilities.transports.includes(resolved.spec.transport)) {
1684
+ throw new CliError(
1685
+ "INVALID_ARGUMENT",
1686
+ `${input2.agent} adapter does not support ${resolved.spec.transport} MCP connections`,
1687
+ {
1688
+ exitCode: exitCodes.invalidArgument,
1689
+ hint: "Choose a provider supported by the installed Agent adapter."
1690
+ }
1691
+ );
1692
+ }
1693
+ if (!input2.adapter.capabilities.scopes.includes(input2.scope)) {
1694
+ throw new CliError(
1695
+ "INVALID_ARGUMENT",
1696
+ `${input2.agent} adapter does not support ${input2.scope} scope`,
1697
+ { exitCode: exitCodes.invalidArgument }
1698
+ );
1699
+ }
1700
+ await input2.verifier.verify(resolved.spec, { timeoutMs: input2.timeoutMs });
1701
+ await input2.adapter.detect();
1702
+ const host = await input2.adapter.read(input2.serverName, input2.scope);
1703
+ const key = connectionKey(input2.agent, input2.scope, input2.serverName);
1704
+ const managed = await input2.state.get(key);
1600
1705
  const desiredFingerprint = fingerprintConnection(resolved.spec);
1601
1706
  const plan = determinePlan({
1602
- agent: input.agent,
1603
- serverName: input.serverName,
1604
- scope: input.scope,
1707
+ agent: input2.agent,
1708
+ serverName: input2.serverName,
1709
+ scope: input2.scope,
1605
1710
  target: resolved.target,
1606
- ...input.sourceId ? { sourceId: input.sourceId } : {},
1711
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {},
1607
1712
  desiredFingerprint,
1608
1713
  ...host ? { hostFingerprint: host.fingerprint } : {},
1609
1714
  ...host ? { hostSpecFingerprint: fingerprintConnection(host.spec) } : {},
@@ -1611,9 +1716,9 @@ async function connectAgent(input) {
1611
1716
  });
1612
1717
  if (plan.action === "unchanged") {
1613
1718
  return {
1614
- agent: input.agent,
1615
- serverName: input.serverName,
1616
- scope: input.scope,
1719
+ agent: input2.agent,
1720
+ serverName: input2.serverName,
1721
+ scope: input2.scope,
1617
1722
  action: "unchanged",
1618
1723
  changed: false,
1619
1724
  verified: true,
@@ -1621,11 +1726,11 @@ async function connectAgent(input) {
1621
1726
  plan
1622
1727
  };
1623
1728
  }
1624
- if (input.dryRun) {
1729
+ if (input2.dryRun) {
1625
1730
  return {
1626
- agent: input.agent,
1627
- serverName: input.serverName,
1628
- scope: input.scope,
1731
+ agent: input2.agent,
1732
+ serverName: input2.serverName,
1733
+ scope: input2.scope,
1629
1734
  action: plan.action,
1630
1735
  changed: false,
1631
1736
  verified: true,
@@ -1633,13 +1738,13 @@ async function connectAgent(input) {
1633
1738
  plan
1634
1739
  };
1635
1740
  }
1636
- if (!input.yes && !await input.confirm(
1637
- `${plan.action === "create" ? "Add" : "Update"} ${input.serverName} in ${input.agent}?`
1741
+ if (!input2.yes && !await input2.confirm(
1742
+ `${plan.action === "create" ? "Add" : "Update"} ${input2.serverName} in ${input2.agent}?`
1638
1743
  )) {
1639
1744
  return {
1640
- agent: input.agent,
1641
- serverName: input.serverName,
1642
- scope: input.scope,
1745
+ agent: input2.agent,
1746
+ serverName: input2.serverName,
1747
+ scope: input2.scope,
1643
1748
  action: "cancelled",
1644
1749
  changed: false,
1645
1750
  verified: true,
@@ -1649,23 +1754,23 @@ async function connectAgent(input) {
1649
1754
  }
1650
1755
  try {
1651
1756
  if (plan.action === "update") {
1652
- await input.adapter.remove(input.serverName, input.scope);
1757
+ await input2.adapter.remove(input2.serverName, input2.scope);
1653
1758
  }
1654
- await input.adapter.add(input.serverName, input.scope, resolved.spec);
1655
- const readBack = await input.adapter.read(input.serverName, input.scope);
1759
+ await input2.adapter.add(input2.serverName, input2.scope, resolved.spec);
1760
+ const readBack = await input2.adapter.read(input2.serverName, input2.scope);
1656
1761
  if (!readBack || fingerprintConnection(readBack.spec) !== desiredFingerprint) {
1657
1762
  throw hostFailure3("Agent MCP read-back did not match the requested connection");
1658
1763
  }
1659
- await input.verifier.verify(readBack.spec, { timeoutMs: input.timeoutMs });
1660
- await input.state.set(
1661
- managedConnection(input, resolved, readBack.fingerprint, managed)
1764
+ await input2.verifier.verify(readBack.spec, { timeoutMs: input2.timeoutMs });
1765
+ await input2.state.set(
1766
+ managedConnection(input2, resolved, readBack.fingerprint, managed)
1662
1767
  );
1663
1768
  } catch (cause) {
1664
1769
  try {
1665
1770
  await rollbackHost({
1666
- adapter: input.adapter,
1667
- serverName: input.serverName,
1668
- scope: input.scope,
1771
+ adapter: input2.adapter,
1772
+ serverName: input2.serverName,
1773
+ scope: input2.scope,
1669
1774
  ...host ? { previousSpec: host.spec } : {},
1670
1775
  ...host ? { previousFingerprint: host.fingerprint } : {}
1671
1776
  });
@@ -1673,7 +1778,7 @@ async function connectAgent(input) {
1673
1778
  throw hostFailure3(
1674
1779
  "Agent MCP configuration failed and rollback also failed",
1675
1780
  cause,
1676
- `Inspect the ${input.agent} MCP entry \`${input.serverName}\` before retrying.`
1781
+ `Inspect the ${input2.agent} MCP entry \`${input2.serverName}\` before retrying.`
1677
1782
  );
1678
1783
  }
1679
1784
  throw hostFailure3(
@@ -1682,9 +1787,9 @@ async function connectAgent(input) {
1682
1787
  );
1683
1788
  }
1684
1789
  return {
1685
- agent: input.agent,
1686
- serverName: input.serverName,
1687
- scope: input.scope,
1790
+ agent: input2.agent,
1791
+ serverName: input2.serverName,
1792
+ scope: input2.scope,
1688
1793
  action: plan.action,
1689
1794
  changed: true,
1690
1795
  verified: true,
@@ -1692,15 +1797,15 @@ async function connectAgent(input) {
1692
1797
  plan
1693
1798
  };
1694
1799
  }
1695
- async function disconnectAgent(input) {
1696
- validateServerName(input.serverName);
1697
- await input.adapter.detect();
1698
- const key = connectionKey(input.agent, input.scope, input.serverName);
1699
- const managed = await input.state.get(key);
1800
+ async function disconnectAgent(input2) {
1801
+ validateServerName(input2.serverName);
1802
+ await input2.adapter.detect();
1803
+ const key = connectionKey(input2.agent, input2.scope, input2.serverName);
1804
+ const managed = await input2.state.get(key);
1700
1805
  if (!managed) {
1701
1806
  throw conflict("The Agent MCP entry is not managed by SAG CLI");
1702
1807
  }
1703
- const host = await input.adapter.read(input.serverName, input.scope);
1808
+ const host = await input2.adapter.read(input2.serverName, input2.scope);
1704
1809
  if (!host) {
1705
1810
  throw conflict("Managed state exists but the Agent MCP entry is missing");
1706
1811
  }
@@ -1709,20 +1814,20 @@ async function disconnectAgent(input) {
1709
1814
  }
1710
1815
  const plan = {
1711
1816
  action: "unchanged",
1712
- agent: input.agent,
1713
- serverName: input.serverName,
1714
- scope: input.scope,
1817
+ agent: input2.agent,
1818
+ serverName: input2.serverName,
1819
+ scope: input2.scope,
1715
1820
  provider: "docker-stdio",
1716
1821
  target: managed.docker.containerName,
1717
1822
  sourceId: managed.sourceId,
1718
1823
  currentFingerprint: host.fingerprint,
1719
1824
  desiredFingerprint: host.fingerprint
1720
1825
  };
1721
- if (!input.yes && !await input.confirm(`Remove ${input.serverName} from ${input.agent}?`)) {
1826
+ if (!input2.yes && !await input2.confirm(`Remove ${input2.serverName} from ${input2.agent}?`)) {
1722
1827
  return {
1723
- agent: input.agent,
1724
- serverName: input.serverName,
1725
- scope: input.scope,
1828
+ agent: input2.agent,
1829
+ serverName: input2.serverName,
1830
+ scope: input2.scope,
1726
1831
  action: "cancelled",
1727
1832
  changed: false,
1728
1833
  verified: true,
@@ -1730,12 +1835,12 @@ async function disconnectAgent(input) {
1730
1835
  plan
1731
1836
  };
1732
1837
  }
1733
- await input.adapter.remove(input.serverName, input.scope);
1838
+ await input2.adapter.remove(input2.serverName, input2.scope);
1734
1839
  try {
1735
- await input.state.delete(key);
1840
+ await input2.state.delete(key);
1736
1841
  } catch (cause) {
1737
1842
  try {
1738
- await input.adapter.add(input.serverName, input.scope, host.spec);
1843
+ await input2.adapter.add(input2.serverName, input2.scope, host.spec);
1739
1844
  } catch {
1740
1845
  throw hostFailure3(
1741
1846
  "Managed state cleanup failed and the Agent entry could not be restored",
@@ -1748,9 +1853,9 @@ async function disconnectAgent(input) {
1748
1853
  );
1749
1854
  }
1750
1855
  return {
1751
- agent: input.agent,
1752
- serverName: input.serverName,
1753
- scope: input.scope,
1856
+ agent: input2.agent,
1857
+ serverName: input2.serverName,
1858
+ scope: input2.scope,
1754
1859
  action: "disconnect",
1755
1860
  changed: true,
1756
1861
  verified: true,
@@ -1763,21 +1868,21 @@ async function disconnectAgent(input) {
1763
1868
  var SOURCE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
1764
1869
  var DockerStdioProvider = class {
1765
1870
  id = "docker-stdio";
1766
- async inspect(input) {
1871
+ async inspect(input2) {
1767
1872
  return {
1768
1873
  provider: this.id,
1769
1874
  ready: true,
1770
- target: input.target.name
1875
+ target: input2.target.name
1771
1876
  };
1772
1877
  }
1773
- async createSpec(input) {
1774
- if (input.sourceId && !SOURCE_ID_PATTERN.test(input.sourceId)) {
1878
+ async createSpec(input2) {
1879
+ if (input2.sourceId && !SOURCE_ID_PATTERN.test(input2.sourceId)) {
1775
1880
  throw new CliError("INVALID_ARGUMENT", "Invalid SAG source id", {
1776
1881
  exitCode: exitCodes.invalidArgument,
1777
1882
  hint: "Use 1-128 letters, numbers, underscores, or dashes."
1778
1883
  });
1779
1884
  }
1780
- const sourceArguments = input.sourceId ? ["-e", `SAG_MCP_SOURCE_ID=${input.sourceId}`] : [];
1885
+ const sourceArguments = input2.sourceId ? ["-e", `SAG_MCP_SOURCE_ID=${input2.sourceId}`] : [];
1781
1886
  return {
1782
1887
  transport: "stdio",
1783
1888
  command: "docker",
@@ -1785,7 +1890,7 @@ var DockerStdioProvider = class {
1785
1890
  "exec",
1786
1891
  "-i",
1787
1892
  ...sourceArguments,
1788
- input.target.name,
1893
+ input2.target.name,
1789
1894
  "python",
1790
1895
  "-m",
1791
1896
  "sag_api.mcp.server"
@@ -1835,13 +1940,13 @@ async function validateTarget(docker, container) {
1835
1940
  }
1836
1941
  return toTarget(inspection);
1837
1942
  }
1838
- async function discoverSagContainer(input) {
1839
- await input.docker.version();
1840
- if (input.container) {
1841
- validateContainer(input.container);
1842
- return validateTarget(input.docker, input.container);
1943
+ async function discoverSagContainer(input2) {
1944
+ await input2.docker.version();
1945
+ if (input2.container) {
1946
+ validateContainer(input2.container);
1947
+ return validateTarget(input2.docker, input2.container);
1843
1948
  }
1844
- const containers = await input.docker.listRunningContainers();
1949
+ const containers = await input2.docker.listRunningContainers();
1845
1950
  const apiCandidates = containers.filter(
1846
1951
  (container) => container.labels["com.docker.compose.service"] === "api"
1847
1952
  );
@@ -1856,7 +1961,7 @@ async function discoverSagContainer(input) {
1856
1961
  let preferredFailure;
1857
1962
  for (const candidate of apiCandidates) {
1858
1963
  try {
1859
- targets.push(await validateTarget(input.docker, candidate.id));
1964
+ targets.push(await validateTarget(input2.docker, candidate.id));
1860
1965
  } catch (error) {
1861
1966
  firstFailure ??= error;
1862
1967
  if (candidate.labels["com.docker.compose.project"] === "sag") {
@@ -1872,13 +1977,13 @@ async function discoverSagContainer(input) {
1872
1977
  if (rankedTargets.length === 1) {
1873
1978
  return rankedTargets[0];
1874
1979
  }
1875
- if (!input.interactive || !input.select) {
1980
+ if (!input2.interactive || !input2.select) {
1876
1981
  throw new CliError("CONFIG_CONFLICT", "Multiple SAG API containers were found", {
1877
1982
  exitCode: exitCodes.configConflict,
1878
1983
  hint: "Pass `--container <name-or-id>` to select one."
1879
1984
  });
1880
1985
  }
1881
- const selectedId = await input.select(rankedTargets);
1986
+ const selectedId = await input2.select(rankedTargets);
1882
1987
  const selected = rankedTargets.find(
1883
1988
  (target) => target.id === selectedId || target.name === selectedId
1884
1989
  );
@@ -1891,34 +1996,34 @@ async function discoverSagContainer(input) {
1891
1996
  }
1892
1997
 
1893
1998
  // src/commands/agent.ts
1894
- async function connectLocalAgent(runtime2, input) {
1999
+ async function connectLocalAgent(runtime2, input2) {
1895
2000
  return connectAgent({
1896
- agent: input.agent,
1897
- serverName: input.serverName,
2001
+ agent: input2.agent,
2002
+ serverName: input2.serverName,
1898
2003
  scope: "user",
1899
- profile: input.profile,
1900
- ...input.sourceId ? { sourceId: input.sourceId } : {},
1901
- timeoutMs: input.timeoutMs,
1902
- dryRun: input.dryRun,
1903
- yes: input.yes,
2004
+ profile: input2.profile,
2005
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {},
2006
+ timeoutMs: input2.timeoutMs,
2007
+ dryRun: input2.dryRun,
2008
+ yes: input2.yes,
1904
2009
  resolveConnection: async () => {
1905
2010
  const target = await discoverSagContainer({
1906
2011
  docker: runtime2.docker,
1907
- ...input.container ? { container: input.container } : {},
2012
+ ...input2.container ? { container: input2.container } : {},
1908
2013
  interactive: runtime2.interactive,
1909
2014
  select: runtime2.selectContainer
1910
2015
  });
1911
2016
  const spec = await new DockerStdioProvider().createSpec({
1912
2017
  target,
1913
- ...input.sourceId ? { sourceId: input.sourceId } : {}
2018
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {}
1914
2019
  });
1915
2020
  return { target, spec };
1916
2021
  },
1917
2022
  verifier: runtime2.verifier,
1918
- adapter: runtime2.adapters[input.agent],
2023
+ adapter: runtime2.adapters[input2.agent],
1919
2024
  state: runtime2.state,
1920
- confirm: input.confirm,
1921
- now: input.now
2025
+ confirm: input2.confirm,
2026
+ now: input2.now
1922
2027
  });
1923
2028
  }
1924
2029
  async function agentStatus(runtime2, agent, serverName) {
@@ -1997,10 +2102,10 @@ async function agentStatus(runtime2, agent, serverName) {
1997
2102
  };
1998
2103
  }
1999
2104
  }
2000
- async function statusLocalAgents(runtime2, input) {
2001
- const agents = input.agent ? [input.agent] : ["codex", "claude-code"];
2105
+ async function statusLocalAgents(runtime2, input2) {
2106
+ const agents = input2.agent ? [input2.agent] : ["codex", "claude-code"];
2002
2107
  return Promise.all(
2003
- agents.map((agent) => agentStatus(runtime2, agent, input.serverName))
2108
+ agents.map((agent) => agentStatus(runtime2, agent, input2.serverName))
2004
2109
  );
2005
2110
  }
2006
2111
  async function listAgentHosts(runtime2) {
@@ -2021,58 +2126,69 @@ async function listAgentHosts(runtime2) {
2021
2126
  })
2022
2127
  );
2023
2128
  }
2024
- async function disconnectLocalAgent(runtime2, input) {
2129
+ async function disconnectLocalAgent(runtime2, input2) {
2025
2130
  return disconnectAgent({
2026
- agent: input.agent,
2027
- serverName: input.serverName,
2131
+ agent: input2.agent,
2132
+ serverName: input2.serverName,
2028
2133
  scope: "user",
2029
- adapter: runtime2.adapters[input.agent],
2134
+ adapter: runtime2.adapters[input2.agent],
2030
2135
  state: runtime2.state,
2031
- yes: input.yes,
2032
- confirm: input.confirm
2136
+ yes: input2.yes,
2137
+ confirm: input2.confirm
2033
2138
  });
2034
2139
  }
2035
2140
 
2036
2141
  // src/commands/auth.ts
2037
- async function login(input) {
2038
- const token = input.environmentToken?.trim() || await input.promptToken();
2039
- if (!token.trim()) {
2040
- throw new CliError("AUTH_REQUIRED", "A SAG token is required", {
2041
- exitCode: exitCodes.authRequired,
2042
- hint: "Paste a token when prompted or set SAG_TOKEN."
2142
+ async function login(input2) {
2143
+ const environmentToken = input2.environmentToken?.trim();
2144
+ if (environmentToken) {
2145
+ const user2 = await input2.validate(environmentToken);
2146
+ await input2.store.set(input2.credentialRef, environmentToken);
2147
+ return {
2148
+ authenticated: true,
2149
+ credentialRef: input2.credentialRef,
2150
+ credentialStore: input2.store.kind,
2151
+ user: user2
2152
+ };
2153
+ }
2154
+ const name = (input2.name ?? await input2.promptName()).trim();
2155
+ if (!name) {
2156
+ throw new CliError("INVALID_ARGUMENT", "A SAG user name is required", {
2157
+ exitCode: exitCodes.invalidArgument,
2158
+ hint: "Pass `--name <name>` or enter a name when prompted."
2043
2159
  });
2044
2160
  }
2045
- const user = await input.validate(token);
2046
- await input.store.set(input.credentialRef, token);
2161
+ const { accessToken, user } = await input2.authenticate(name);
2162
+ await input2.store.set(input2.credentialRef, accessToken);
2047
2163
  return {
2048
2164
  authenticated: true,
2049
- credentialRef: input.credentialRef,
2050
- credentialStore: input.store.kind,
2165
+ credentialRef: input2.credentialRef,
2166
+ credentialStore: input2.store.kind,
2051
2167
  user
2052
2168
  };
2053
2169
  }
2054
- async function status(input) {
2055
- const token = input.environmentToken?.trim() || await input.store.get(input.credentialRef);
2170
+ async function status(input2) {
2171
+ const token = input2.environmentToken?.trim() || await input2.store.get(input2.credentialRef);
2056
2172
  if (!token) {
2057
2173
  return {
2058
2174
  authenticated: false,
2059
- credentialRef: input.credentialRef,
2060
- credentialStore: input.store.kind
2175
+ credentialRef: input2.credentialRef,
2176
+ credentialStore: input2.store.kind
2061
2177
  };
2062
2178
  }
2063
- const user = await input.validate(token);
2179
+ const user = await input2.validate(token);
2064
2180
  return {
2065
2181
  authenticated: true,
2066
- credentialRef: input.credentialRef,
2067
- credentialStore: input.store.kind,
2182
+ credentialRef: input2.credentialRef,
2183
+ credentialStore: input2.store.kind,
2068
2184
  user
2069
2185
  };
2070
2186
  }
2071
- async function logout(input) {
2072
- await input.store.delete(input.credentialRef);
2187
+ async function logout(input2) {
2188
+ await input2.store.delete(input2.credentialRef);
2073
2189
  return {
2074
2190
  authenticated: false,
2075
- credentialRef: input.credentialRef
2191
+ credentialRef: input2.credentialRef
2076
2192
  };
2077
2193
  }
2078
2194
 
@@ -2133,25 +2249,63 @@ async function documentStatus(client, sourceId, documentId) {
2133
2249
  };
2134
2250
  }
2135
2251
 
2252
+ // src/commands/knowledge.ts
2253
+ function requiredIdentifier2(value, label) {
2254
+ const normalized = value.trim();
2255
+ if (!normalized) {
2256
+ throw new CliError("INVALID_ARGUMENT", `${label} is required`, {
2257
+ exitCode: exitCodes.invalidArgument
2258
+ });
2259
+ }
2260
+ return normalized;
2261
+ }
2262
+ async function outline(client, sourceId, documentId) {
2263
+ return client.outline(
2264
+ requiredIdentifier2(sourceId, "Source ID"),
2265
+ requiredIdentifier2(documentId, "Document ID")
2266
+ );
2267
+ }
2268
+ async function grep(client, sourceId, pattern, limit) {
2269
+ return client.grep(
2270
+ requiredIdentifier2(sourceId, "Source ID"),
2271
+ requiredIdentifier2(pattern, "Pattern"),
2272
+ limit
2273
+ );
2274
+ }
2275
+ async function read(client, sourceId, documentId, offset, limit) {
2276
+ return client.readDocument(
2277
+ requiredIdentifier2(sourceId, "Source ID"),
2278
+ requiredIdentifier2(documentId, "Document ID"),
2279
+ offset,
2280
+ limit
2281
+ );
2282
+ }
2283
+ async function getEntityContext(client, sourceId, name) {
2284
+ return client.getEntityContext(
2285
+ requiredIdentifier2(sourceId, "Source ID"),
2286
+ requiredIdentifier2(name, "Entity name")
2287
+ );
2288
+ }
2289
+
2136
2290
  // src/commands/mcp.ts
2137
- async function testLocalMcp(runtime2, input) {
2291
+ async function testLocalMcp(runtime2, input2) {
2138
2292
  const container = await discoverSagContainer({
2139
2293
  docker: runtime2.docker,
2140
- ...input.container ? { container: input.container } : {},
2294
+ ...input2.container ? { container: input2.container } : {},
2141
2295
  interactive: runtime2.interactive,
2142
2296
  select: runtime2.selectContainer
2143
2297
  });
2144
2298
  const spec = await new DockerStdioProvider().createSpec({
2145
2299
  target: container,
2146
- ...input.sourceId ? { sourceId: input.sourceId } : {}
2300
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {}
2147
2301
  });
2148
2302
  const verification = await runtime2.verifier.verify(spec, {
2149
- timeoutMs: input.timeoutMs
2303
+ timeoutMs: input2.timeoutMs
2150
2304
  });
2151
2305
  return {
2152
2306
  provider: "docker-stdio",
2153
2307
  container,
2154
- sourceId: input.sourceId ?? null,
2308
+ sourceId: input2.sourceId ?? null,
2155
2309
  ...verification
2156
2310
  };
2157
2311
  }
@@ -2163,9 +2317,9 @@ function skipped(name, detail) {
2163
2317
  detail
2164
2318
  };
2165
2319
  }
2166
- async function localMcpDoctorChecks(runtime2, input) {
2320
+ async function localMcpDoctorChecks(runtime2, input2) {
2167
2321
  try {
2168
- const report = await testLocalMcp(runtime2, input);
2322
+ const report = await testLocalMcp(runtime2, input2);
2169
2323
  return [
2170
2324
  {
2171
2325
  name: "docker",
@@ -2362,16 +2516,16 @@ function directCredentialRef(url) {
2362
2516
  const fingerprint = createHash2("sha256").update(url).digest("hex").slice(0, 16);
2363
2517
  return `sag-cli/direct-${fingerprint}`;
2364
2518
  }
2365
- async function createRuntimeContext(input) {
2366
- const config = await input.configStore.load();
2367
- const connection = resolveConnection(input.options, input.environment, config);
2519
+ async function createRuntimeContext(input2) {
2520
+ const config = await input2.configStore.load();
2521
+ const connection = resolveConnection(input2.options, input2.environment, config);
2368
2522
  const credentialRef = connection.credentialRef ?? directCredentialRef(connection.url);
2369
- const token = connection.environmentToken ?? await input.credentialStore.get(credentialRef) ?? void 0;
2523
+ const token = connection.environmentToken ?? await input2.credentialStore.get(credentialRef) ?? void 0;
2370
2524
  return {
2371
2525
  connection,
2372
2526
  credentialRef,
2373
2527
  ...token ? { token } : {},
2374
- client: input.createClient({
2528
+ client: input2.createClient({
2375
2529
  origin: connection.url,
2376
2530
  ...token ? { token } : {}
2377
2531
  })
@@ -2475,7 +2629,7 @@ function documentDetail(total, statuses) {
2475
2629
  const parts = displayOrder.filter((status2) => statuses[status2] > 0).map((status2) => `${statuses[status2]} ${status2}`);
2476
2630
  return `${total} ${total === 1 ? "document" : "documents"}${parts.length ? `: ${parts.join(", ")}` : ""}`;
2477
2631
  }
2478
- async function runDoctor(client, input, localMcpChecks) {
2632
+ async function runDoctor(client, input2, localMcpChecks) {
2479
2633
  const checks = [];
2480
2634
  let sagVersion;
2481
2635
  let authenticated = false;
@@ -2590,8 +2744,8 @@ async function runDoctor(client, input, localMcpChecks) {
2590
2744
  }
2591
2745
  return {
2592
2746
  status: overallStatus(checks),
2593
- url: input.url,
2594
- ...input.profileName ? { profileName: input.profileName } : {},
2747
+ url: input2.url,
2748
+ ...input2.profileName ? { profileName: input2.profileName } : {},
2595
2749
  ...sagVersion ? { sagVersion } : {},
2596
2750
  checks,
2597
2751
  summary: {
@@ -2681,6 +2835,45 @@ function renderLocalMcp(value) {
2681
2835
  ].join("\n")}
2682
2836
  `;
2683
2837
  }
2838
+ function renderOutline(value) {
2839
+ const filename = typeof value.filename === "string" ? value.filename : "Unknown";
2840
+ const items = Array.isArray(value.outline) ? value.outline.filter(isRecord) : [];
2841
+ const lines = items.map(
2842
+ (item) => `${String(item.rank ?? 0).padStart(3)}. ${item.heading || "(untitled)"} (chunk_id=${item.chunk_id})`
2843
+ );
2844
+ return `${filename}
2845
+ ${lines.join("\n")}
2846
+ `;
2847
+ }
2848
+ function renderGrep(value) {
2849
+ const matches = Array.isArray(value.matches) ? value.matches.filter(isRecord) : [];
2850
+ const lines = matches.map(
2851
+ (match, index) => `[${index + 1}] ${match.heading || "Match"}
2852
+ ${match.snippet}`
2853
+ );
2854
+ return lines.length ? `${lines.join("\n\n")}
2855
+ ` : "No matches.\n";
2856
+ }
2857
+ function renderRead(value) {
2858
+ const filename = typeof value.filename === "string" ? value.filename : "Unknown";
2859
+ const total = typeof value.total_lines === "number" ? value.total_lines : 0;
2860
+ const offset = typeof value.offset === "number" ? value.offset : 1;
2861
+ const lines = Array.isArray(value.lines) ? value.lines.map(String) : [];
2862
+ const header = `${filename} \xB7 lines ${offset}-${offset + lines.length - 1} / ${total}`;
2863
+ return `${header}
2864
+ ${lines.join("")}`;
2865
+ }
2866
+ function renderEntityContext(value) {
2867
+ const name = typeof value.name === "string" ? value.name : "Unknown";
2868
+ const type = typeof value.type === "string" ? value.type : "";
2869
+ const source = typeof value.source_name === "string" ? value.source_name : "";
2870
+ const context = typeof value.context === "string" ? value.context : "";
2871
+ return `Entity: ${name}${type ? ` (${type})` : ""}
2872
+ Source: ${source}
2873
+
2874
+ ${context}
2875
+ `;
2876
+ }
2684
2877
  function renderHuman(value, quiet = false) {
2685
2878
  if (quiet) {
2686
2879
  if (Array.isArray(value)) {
@@ -2703,6 +2896,18 @@ function renderHuman(value, quiet = false) {
2703
2896
  if (isRecord(value) && Array.isArray(value.sections) && "query" in value) {
2704
2897
  return renderSearch(value);
2705
2898
  }
2899
+ if (isRecord(value) && Array.isArray(value.outline) && "filename" in value) {
2900
+ return renderOutline(value);
2901
+ }
2902
+ if (isRecord(value) && Array.isArray(value.matches) && "pattern" in value) {
2903
+ return renderGrep(value);
2904
+ }
2905
+ if (isRecord(value) && Array.isArray(value.lines) && "total_lines" in value) {
2906
+ return renderRead(value);
2907
+ }
2908
+ if (isRecord(value) && "entity_id" in value && "context" in value) {
2909
+ return renderEntityContext(value);
2910
+ }
2706
2911
  if (isRecord(value) && value.provider === "docker-stdio" && "protocolVersion" in value && "container" in value) {
2707
2912
  return renderLocalMcp(value);
2708
2913
  }
@@ -2847,12 +3052,14 @@ function addProfileCommands(program, dependencies2) {
2847
3052
  }
2848
3053
  function addAuthCommands(program, dependencies2) {
2849
3054
  const auth = program.command("auth").description("Manage SAG credentials");
2850
- auth.command("login").action(async () => {
3055
+ auth.command("login").option("--name <name>", "SAG user name for the existing local login").action(async (options) => {
2851
3056
  const context = await runtime(program, dependencies2);
2852
3057
  const result = await login({
2853
3058
  credentialRef: context.credentialRef,
2854
3059
  ...context.connection.environmentToken ? { environmentToken: context.connection.environmentToken } : {},
2855
- promptToken: dependencies2.promptToken,
3060
+ ...options.name ? { name: options.name } : {},
3061
+ promptName: dependencies2.promptName,
3062
+ authenticate: (name) => dependencies2.createClient({ origin: context.connection.url }).login(name),
2856
3063
  validate: async (token) => dependencies2.createClient({ origin: context.connection.url, token }).me(),
2857
3064
  store: dependencies2.credentialStore
2858
3065
  });
@@ -2946,6 +3153,46 @@ function addKnowledgeCommands(program, dependencies2) {
2946
3153
  );
2947
3154
  }
2948
3155
  );
3156
+ program.command("outline").argument("<document-id>").requiredOption("--source <source-id>").action(async (documentId, options) => {
3157
+ const context = await runtime(program, dependencies2);
3158
+ emit(
3159
+ program,
3160
+ dependencies2,
3161
+ await outline(context.client, options.source, documentId)
3162
+ );
3163
+ });
3164
+ program.command("grep").argument("<pattern>").requiredOption("--source <source-id>").option("--limit <number>", "Maximum result count", parseInteger).action(async (pattern, options) => {
3165
+ const context = await runtime(program, dependencies2);
3166
+ emit(
3167
+ program,
3168
+ dependencies2,
3169
+ await grep(context.client, options.source, pattern, options.limit)
3170
+ );
3171
+ });
3172
+ program.command("read").argument("<document-id>").requiredOption("--source <source-id>").option("--offset <number>", "Starting line (1-based)", parseInteger).option("--limit <number>", "Lines to read", parseInteger).action(
3173
+ async (documentId, options) => {
3174
+ const context = await runtime(program, dependencies2);
3175
+ emit(
3176
+ program,
3177
+ dependencies2,
3178
+ await read(
3179
+ context.client,
3180
+ options.source,
3181
+ documentId,
3182
+ options.offset,
3183
+ options.limit
3184
+ )
3185
+ );
3186
+ }
3187
+ );
3188
+ program.command("get-entity").argument("<name>").requiredOption("--source <source-id>").action(async (name, options) => {
3189
+ const context = await runtime(program, dependencies2);
3190
+ emit(
3191
+ program,
3192
+ dependencies2,
3193
+ await getEntityContext(context.client, options.source, name)
3194
+ );
3195
+ });
2949
3196
  }
2950
3197
  function addLocalMcpCommands(program, dependencies2) {
2951
3198
  const mcp = program.command("mcp").description("Verify local SAG MCP");
@@ -3081,10 +3328,7 @@ var dependencies = {
3081
3328
  configStore: new ConfigStore(defaultConfigPath()),
3082
3329
  credentialStore: credentialSelection.store,
3083
3330
  createClient: defaultClientFactory,
3084
- promptToken: () => password({
3085
- message: "SAG Token",
3086
- mask: "*"
3087
- }),
3331
+ promptName: () => input({ message: "SAG user name" }),
3088
3332
  confirm: (message) => confirm({ message, default: false }),
3089
3333
  writeStdout: (value) => process.stdout.write(value),
3090
3334
  writeStderr: (value) => process.stderr.write(value),