@salesforce/graphiti 10.10.2

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 (282) hide show
  1. package/AGENT_GUIDE.md +424 -0
  2. package/CHANGELOG.md +448 -0
  3. package/LICENSE.txt +82 -0
  4. package/README.md +204 -0
  5. package/TASK.md +249 -0
  6. package/dist/cli.d.ts +7 -0
  7. package/dist/cli.js +683 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/args.d.ts +13 -0
  10. package/dist/commands/args.js +207 -0
  11. package/dist/commands/args.js.map +1 -0
  12. package/dist/commands/build.d.ts +11 -0
  13. package/dist/commands/build.js +209 -0
  14. package/dist/commands/build.js.map +1 -0
  15. package/dist/commands/connect.d.ts +8 -0
  16. package/dist/commands/connect.js +55 -0
  17. package/dist/commands/connect.js.map +1 -0
  18. package/dist/commands/describe.d.ts +6 -0
  19. package/dist/commands/describe.js +229 -0
  20. package/dist/commands/describe.js.map +1 -0
  21. package/dist/commands/meta.d.ts +9 -0
  22. package/dist/commands/meta.js +140 -0
  23. package/dist/commands/meta.js.map +1 -0
  24. package/dist/commands/navigate.d.ts +14 -0
  25. package/dist/commands/navigate.js +105 -0
  26. package/dist/commands/navigate.js.map +1 -0
  27. package/dist/commands/query-helpers.d.ts +80 -0
  28. package/dist/commands/query-helpers.js +865 -0
  29. package/dist/commands/query-helpers.js.map +1 -0
  30. package/dist/commands/query.d.ts +26 -0
  31. package/dist/commands/query.js +901 -0
  32. package/dist/commands/query.js.map +1 -0
  33. package/dist/commands/review.d.ts +18 -0
  34. package/dist/commands/review.js +533 -0
  35. package/dist/commands/review.js.map +1 -0
  36. package/dist/commands/session-mgmt.d.ts +25 -0
  37. package/dist/commands/session-mgmt.js +206 -0
  38. package/dist/commands/session-mgmt.js.map +1 -0
  39. package/dist/commands/type.d.ts +6 -0
  40. package/dist/commands/type.js +342 -0
  41. package/dist/commands/type.js.map +1 -0
  42. package/dist/commands/validate-input.d.ts +6 -0
  43. package/dist/commands/validate-input.js +32 -0
  44. package/dist/commands/validate-input.js.map +1 -0
  45. package/dist/intent/build-aggregate.d.ts +33 -0
  46. package/dist/intent/build-aggregate.js +134 -0
  47. package/dist/intent/build-aggregate.js.map +1 -0
  48. package/dist/intent/build-create.d.ts +14 -0
  49. package/dist/intent/build-create.js +16 -0
  50. package/dist/intent/build-create.js.map +1 -0
  51. package/dist/intent/build-delete.d.ts +30 -0
  52. package/dist/intent/build-delete.js +53 -0
  53. package/dist/intent/build-delete.js.map +1 -0
  54. package/dist/intent/build-detail.d.ts +32 -0
  55. package/dist/intent/build-detail.js +80 -0
  56. package/dist/intent/build-detail.js.map +1 -0
  57. package/dist/intent/build-discover.d.ts +30 -0
  58. package/dist/intent/build-discover.js +149 -0
  59. package/dist/intent/build-discover.js.map +1 -0
  60. package/dist/intent/build-list.d.ts +28 -0
  61. package/dist/intent/build-list.js +75 -0
  62. package/dist/intent/build-list.js.map +1 -0
  63. package/dist/intent/build-mutation.d.ts +23 -0
  64. package/dist/intent/build-mutation.js +54 -0
  65. package/dist/intent/build-mutation.js.map +1 -0
  66. package/dist/intent/build-output.d.ts +27 -0
  67. package/dist/intent/build-output.js +60 -0
  68. package/dist/intent/build-output.js.map +1 -0
  69. package/dist/intent/build-raw.d.ts +23 -0
  70. package/dist/intent/build-raw.js +54 -0
  71. package/dist/intent/build-raw.js.map +1 -0
  72. package/dist/intent/build-update.d.ts +14 -0
  73. package/dist/intent/build-update.js +16 -0
  74. package/dist/intent/build-update.js.map +1 -0
  75. package/dist/intent/get-schema-with-priming.d.ts +26 -0
  76. package/dist/intent/get-schema-with-priming.js +32 -0
  77. package/dist/intent/get-schema-with-priming.js.map +1 -0
  78. package/dist/intent/select-child-relationship.d.ts +19 -0
  79. package/dist/intent/select-child-relationship.js +38 -0
  80. package/dist/intent/select-child-relationship.js.map +1 -0
  81. package/dist/intent/types.d.ts +159 -0
  82. package/dist/intent/types.js +21 -0
  83. package/dist/intent/types.js.map +1 -0
  84. package/dist/lib/apply-command.d.ts +15 -0
  85. package/dist/lib/apply-command.js +238 -0
  86. package/dist/lib/apply-command.js.map +1 -0
  87. package/dist/lib/auth.d.ts +38 -0
  88. package/dist/lib/auth.js +113 -0
  89. package/dist/lib/auth.js.map +1 -0
  90. package/dist/lib/codegen.d.ts +32 -0
  91. package/dist/lib/codegen.js +700 -0
  92. package/dist/lib/codegen.js.map +1 -0
  93. package/dist/lib/command-registry.d.ts +59 -0
  94. package/dist/lib/command-registry.js +366 -0
  95. package/dist/lib/command-registry.js.map +1 -0
  96. package/dist/lib/formatter.d.ts +76 -0
  97. package/dist/lib/formatter.js +419 -0
  98. package/dist/lib/formatter.js.map +1 -0
  99. package/dist/lib/fs-utils.d.ts +23 -0
  100. package/dist/lib/fs-utils.js +46 -0
  101. package/dist/lib/fs-utils.js.map +1 -0
  102. package/dist/lib/graphql-name.d.ts +27 -0
  103. package/dist/lib/graphql-name.js +32 -0
  104. package/dist/lib/graphql-name.js.map +1 -0
  105. package/dist/lib/interactive.d.ts +6 -0
  106. package/dist/lib/interactive.js +562 -0
  107. package/dist/lib/interactive.js.map +1 -0
  108. package/dist/lib/introspect.d.ts +40 -0
  109. package/dist/lib/introspect.js +280 -0
  110. package/dist/lib/introspect.js.map +1 -0
  111. package/dist/lib/object-info.d.ts +79 -0
  112. package/dist/lib/object-info.js +313 -0
  113. package/dist/lib/object-info.js.map +1 -0
  114. package/dist/lib/path-selection.d.ts +50 -0
  115. package/dist/lib/path-selection.js +146 -0
  116. package/dist/lib/path-selection.js.map +1 -0
  117. package/dist/lib/prime-schema.d.ts +59 -0
  118. package/dist/lib/prime-schema.js +158 -0
  119. package/dist/lib/prime-schema.js.map +1 -0
  120. package/dist/lib/query-builder.d.ts +10 -0
  121. package/dist/lib/query-builder.js +168 -0
  122. package/dist/lib/query-builder.js.map +1 -0
  123. package/dist/lib/query-commands.d.ts +19 -0
  124. package/dist/lib/query-commands.js +262 -0
  125. package/dist/lib/query-commands.js.map +1 -0
  126. package/dist/lib/session.d.ts +205 -0
  127. package/dist/lib/session.js +1075 -0
  128. package/dist/lib/session.js.map +1 -0
  129. package/dist/lib/tokenize.d.ts +12 -0
  130. package/dist/lib/tokenize.js +48 -0
  131. package/dist/lib/tokenize.js.map +1 -0
  132. package/dist/lib/uiapi.d.ts +109 -0
  133. package/dist/lib/uiapi.js +159 -0
  134. package/dist/lib/uiapi.js.map +1 -0
  135. package/dist/lib/validator.d.ts +27 -0
  136. package/dist/lib/validator.js +100 -0
  137. package/dist/lib/validator.js.map +1 -0
  138. package/dist/lib/variable-promotion.d.ts +69 -0
  139. package/dist/lib/variable-promotion.js +95 -0
  140. package/dist/lib/variable-promotion.js.map +1 -0
  141. package/dist/lib/walker.d.ts +147 -0
  142. package/dist/lib/walker.js +723 -0
  143. package/dist/lib/walker.js.map +1 -0
  144. package/dist/mcp/server.d.ts +7 -0
  145. package/dist/mcp/server.js +34 -0
  146. package/dist/mcp/server.js.map +1 -0
  147. package/dist/mcp/stdio.d.ts +7 -0
  148. package/dist/mcp/stdio.js +25 -0
  149. package/dist/mcp/stdio.js.map +1 -0
  150. package/dist/mcp/tools/echo.d.ts +7 -0
  151. package/dist/mcp/tools/echo.js +17 -0
  152. package/dist/mcp/tools/echo.js.map +1 -0
  153. package/dist/mcp/tools/sf-gql-aggregate.d.ts +11 -0
  154. package/dist/mcp/tools/sf-gql-aggregate.js +75 -0
  155. package/dist/mcp/tools/sf-gql-aggregate.js.map +1 -0
  156. package/dist/mcp/tools/sf-gql-create.d.ts +11 -0
  157. package/dist/mcp/tools/sf-gql-create.js +35 -0
  158. package/dist/mcp/tools/sf-gql-create.js.map +1 -0
  159. package/dist/mcp/tools/sf-gql-delete.d.ts +11 -0
  160. package/dist/mcp/tools/sf-gql-delete.js +31 -0
  161. package/dist/mcp/tools/sf-gql-delete.js.map +1 -0
  162. package/dist/mcp/tools/sf-gql-detail.d.ts +11 -0
  163. package/dist/mcp/tools/sf-gql-detail.js +58 -0
  164. package/dist/mcp/tools/sf-gql-detail.js.map +1 -0
  165. package/dist/mcp/tools/sf-gql-discover.d.ts +9 -0
  166. package/dist/mcp/tools/sf-gql-discover.js +51 -0
  167. package/dist/mcp/tools/sf-gql-discover.js.map +1 -0
  168. package/dist/mcp/tools/sf-gql-list.d.ts +11 -0
  169. package/dist/mcp/tools/sf-gql-list.js +53 -0
  170. package/dist/mcp/tools/sf-gql-list.js.map +1 -0
  171. package/dist/mcp/tools/sf-gql-raw.d.ts +11 -0
  172. package/dist/mcp/tools/sf-gql-raw.js +39 -0
  173. package/dist/mcp/tools/sf-gql-raw.js.map +1 -0
  174. package/dist/mcp/tools/sf-gql-update.d.ts +11 -0
  175. package/dist/mcp/tools/sf-gql-update.js +35 -0
  176. package/dist/mcp/tools/sf-gql-update.js.map +1 -0
  177. package/dist/mcp/tools/shared/zod-schemas.d.ts +49 -0
  178. package/dist/mcp/tools/shared/zod-schemas.js +46 -0
  179. package/dist/mcp/tools/shared/zod-schemas.js.map +1 -0
  180. package/package.json +36 -0
  181. package/ralph-loop.sh +120 -0
  182. package/scripts/smoke-orderby.sh +190 -0
  183. package/src/__tests__/helpers/prime-deps.ts +46 -0
  184. package/src/__tests__/helpers/schema.ts +73 -0
  185. package/src/__tests__/helpers/stdout.ts +33 -0
  186. package/src/__tests__/setup.ts +19 -0
  187. package/src/cli.ts +764 -0
  188. package/src/commands/__tests__/query.spec.ts +137 -0
  189. package/src/commands/args.ts +306 -0
  190. package/src/commands/build.ts +288 -0
  191. package/src/commands/connect.ts +60 -0
  192. package/src/commands/describe.ts +246 -0
  193. package/src/commands/meta.ts +171 -0
  194. package/src/commands/navigate.ts +134 -0
  195. package/src/commands/query-helpers.ts +1202 -0
  196. package/src/commands/query.ts +1085 -0
  197. package/src/commands/review.ts +670 -0
  198. package/src/commands/session-mgmt.ts +256 -0
  199. package/src/commands/type.ts +437 -0
  200. package/src/commands/validate-input.ts +38 -0
  201. package/src/intent/__tests__/build-aggregate.spec.ts +931 -0
  202. package/src/intent/__tests__/build-create-validation.spec.ts +135 -0
  203. package/src/intent/__tests__/build-delete.spec.ts +121 -0
  204. package/src/intent/__tests__/build-detail.spec.ts +333 -0
  205. package/src/intent/__tests__/build-discover.spec.ts +432 -0
  206. package/src/intent/__tests__/build-list.spec.ts +284 -0
  207. package/src/intent/__tests__/build-mutation.spec.ts +108 -0
  208. package/src/intent/__tests__/build-output.spec.ts +55 -0
  209. package/src/intent/__tests__/build-raw.spec.ts +153 -0
  210. package/src/intent/__tests__/build-update-validation.spec.ts +134 -0
  211. package/src/intent/build-aggregate.ts +182 -0
  212. package/src/intent/build-create.ts +19 -0
  213. package/src/intent/build-delete.ts +62 -0
  214. package/src/intent/build-detail.ts +95 -0
  215. package/src/intent/build-discover.ts +199 -0
  216. package/src/intent/build-list.ts +91 -0
  217. package/src/intent/build-mutation.ts +75 -0
  218. package/src/intent/build-output.ts +72 -0
  219. package/src/intent/build-raw.ts +61 -0
  220. package/src/intent/build-update.ts +19 -0
  221. package/src/intent/get-schema-with-priming.ts +43 -0
  222. package/src/intent/select-child-relationship.ts +48 -0
  223. package/src/intent/types.ts +181 -0
  224. package/src/lib/__tests__/apply-command.parity.spec.ts +97 -0
  225. package/src/lib/__tests__/apply-command.spec.ts +171 -0
  226. package/src/lib/__tests__/auth.spec.ts +292 -0
  227. package/src/lib/__tests__/formatter.spec.ts +86 -0
  228. package/src/lib/__tests__/graphql-name.spec.ts +64 -0
  229. package/src/lib/__tests__/introspect.spec.ts +399 -0
  230. package/src/lib/__tests__/object-info.spec.ts +124 -0
  231. package/src/lib/__tests__/path-selection.spec.ts +219 -0
  232. package/src/lib/__tests__/prime-schema.spec.ts +269 -0
  233. package/src/lib/__tests__/query-builder.spec.ts +95 -0
  234. package/src/lib/__tests__/query-commands.spec.ts +74 -0
  235. package/src/lib/__tests__/session.spec.ts +292 -0
  236. package/src/lib/__tests__/tokenize.spec.ts +33 -0
  237. package/src/lib/__tests__/uiapi.spec.ts +192 -0
  238. package/src/lib/__tests__/variable-promotion.spec.ts +211 -0
  239. package/src/lib/__tests__/walker.spec.ts +250 -0
  240. package/src/lib/apply-command.ts +286 -0
  241. package/src/lib/auth.ts +157 -0
  242. package/src/lib/codegen.ts +899 -0
  243. package/src/lib/command-registry.ts +434 -0
  244. package/src/lib/formatter.ts +587 -0
  245. package/src/lib/fs-utils.ts +46 -0
  246. package/src/lib/graphql-name.ts +35 -0
  247. package/src/lib/interactive.ts +634 -0
  248. package/src/lib/introspect.ts +320 -0
  249. package/src/lib/object-info.ts +409 -0
  250. package/src/lib/path-selection.ts +162 -0
  251. package/src/lib/prime-schema.ts +195 -0
  252. package/src/lib/query-builder.ts +193 -0
  253. package/src/lib/query-commands.ts +290 -0
  254. package/src/lib/session.ts +1304 -0
  255. package/src/lib/tokenize.ts +43 -0
  256. package/src/lib/uiapi.ts +176 -0
  257. package/src/lib/validator.ts +143 -0
  258. package/src/lib/variable-promotion.ts +145 -0
  259. package/src/lib/walker.ts +975 -0
  260. package/src/mcp/__tests__/server.spec.ts +155 -0
  261. package/src/mcp/server.ts +38 -0
  262. package/src/mcp/stdio.ts +29 -0
  263. package/src/mcp/tools/__tests__/sf-gql-aggregate.spec.ts +173 -0
  264. package/src/mcp/tools/__tests__/sf-gql-create.spec.ts +235 -0
  265. package/src/mcp/tools/__tests__/sf-gql-delete.spec.ts +194 -0
  266. package/src/mcp/tools/__tests__/sf-gql-detail.spec.ts +246 -0
  267. package/src/mcp/tools/__tests__/sf-gql-discover.spec.ts +320 -0
  268. package/src/mcp/tools/__tests__/sf-gql-list.spec.ts +128 -0
  269. package/src/mcp/tools/__tests__/sf-gql-raw.spec.ts +141 -0
  270. package/src/mcp/tools/__tests__/sf-gql-update.spec.ts +207 -0
  271. package/src/mcp/tools/echo.ts +24 -0
  272. package/src/mcp/tools/sf-gql-aggregate.ts +102 -0
  273. package/src/mcp/tools/sf-gql-create.ts +55 -0
  274. package/src/mcp/tools/sf-gql-delete.ts +49 -0
  275. package/src/mcp/tools/sf-gql-detail.ts +85 -0
  276. package/src/mcp/tools/sf-gql-discover.ts +67 -0
  277. package/src/mcp/tools/sf-gql-list.ts +73 -0
  278. package/src/mcp/tools/sf-gql-raw.ts +56 -0
  279. package/src/mcp/tools/sf-gql-update.ts +55 -0
  280. package/src/mcp/tools/shared/zod-schemas.ts +55 -0
  281. package/tsconfig.json +18 -0
  282. package/vitest.config.ts +14 -0
package/dist/cli.js ADDED
@@ -0,0 +1,683 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copyright (c) 2026, Salesforce, Inc.,
4
+ * All rights reserved.
5
+ * For full license text, see the LICENSE.txt file
6
+ */
7
+ /* eslint-disable @typescript-eslint/no-explicit-any -- graphiti traverses untyped schema/introspection JSON; see follow-up to replace with `unknown` + narrowing */
8
+ import fs from "fs";
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
11
+ import { Command } from "commander";
12
+ import { connectCommand } from "./commands/connect.js";
13
+ import { describeCommand } from "./commands/describe.js";
14
+ import { queryNew, queryHelp, querySessionsList, querySessionsRm, querySessionsPrune, querySessionsClean, dispatchQueryCommand, CommandError, } from "./commands/query.js";
15
+ import { typeCommand } from "./commands/type.js";
16
+ import { validateInputCommand } from "./commands/validate-input.js";
17
+ import { listOrgs } from "./lib/auth.js";
18
+ import { resolveCommand, setOutputMode } from "./lib/command-registry.js";
19
+ import { formatNavigationPath } from "./lib/formatter.js";
20
+ import { graphitiHome } from "./lib/fs-utils.js";
21
+ import { runInteractiveSession } from "./lib/interactive.js";
22
+ import { loadSession, saveSession as saveSessionFn } from "./lib/session.js";
23
+ import { tokenizeCommand } from "./lib/tokenize.js";
24
+ // ── Active session resolution ────────────────────────────────────────────────
25
+ function getActiveSessionDir() {
26
+ return graphitiHome();
27
+ }
28
+ function getActiveSessionId() {
29
+ // Priority: --session / -s flag (handled by caller), GRAPHITI_SESSION env var, active file
30
+ if (process.env.GRAPHITI_SESSION)
31
+ return process.env.GRAPHITI_SESSION;
32
+ const activePath = path.join(getActiveSessionDir(), "active");
33
+ if (fs.existsSync(activePath)) {
34
+ return fs.readFileSync(activePath, "utf-8").trim() || undefined;
35
+ }
36
+ return undefined;
37
+ }
38
+ function setActiveSession(sessionId) {
39
+ const dir = getActiveSessionDir();
40
+ fs.mkdirSync(dir, { recursive: true });
41
+ fs.writeFileSync(path.join(dir, "active"), sessionId, "utf-8");
42
+ }
43
+ // ── Output mode detection ────────────────────────────────────────────────────
44
+ if (process.env.GRAPHITI_AGENT === "1") {
45
+ setOutputMode("json");
46
+ }
47
+ // ── Exit code mapping ────────────────────────────────────────────────────────
48
+ function exitCodeForError(err) {
49
+ if (err instanceof CommandError) {
50
+ return err.exitCode;
51
+ }
52
+ return 1;
53
+ }
54
+ // ── Argument pre-processing ──────────────────────────────────────────────────
55
+ // Support the legacy `graphiti query <sid> <cmd>` form AND the new flat form.
56
+ // Also support `graphiti <orgAlias> <cmd>` form (org-first).
57
+ const _SESSION_COMMANDS = new Set([
58
+ "pwd",
59
+ "cd",
60
+ "ls",
61
+ "select",
62
+ "drop",
63
+ "alias",
64
+ "set",
65
+ "unset",
66
+ "var",
67
+ "show",
68
+ "check",
69
+ "run",
70
+ "undo",
71
+ "clone",
72
+ "reset",
73
+ "optional",
74
+ "interactive",
75
+ "codegen",
76
+ "describe",
77
+ ]);
78
+ const _STANDALONE_COMMANDS = new Set([
79
+ "connect",
80
+ "orgs",
81
+ "type",
82
+ "new",
83
+ "use",
84
+ "sessions",
85
+ "help",
86
+ "query",
87
+ "validate-input",
88
+ "--help",
89
+ "-h",
90
+ "--version",
91
+ "-V",
92
+ ]);
93
+ // ── CLI program ──────────────────────────────────────────────────────────────
94
+ const program = new Command();
95
+ program
96
+ .name("graphiti")
97
+ .description("Progressive GraphQL query builder CLI for Salesforce orgs")
98
+ .version("0.1.0");
99
+ // Global options
100
+ program
101
+ .option("-s, --session <id>", "Session ID or name to operate on")
102
+ .option("--json", "Output in JSON format (machine-readable)")
103
+ .option("-q, --quiet", "Suppress decorative output");
104
+ // ── orgs ─────────────────────────────────────────────────────────────────────
105
+ program
106
+ .command("orgs")
107
+ .description("List available Salesforce orgs")
108
+ .action(async () => {
109
+ try {
110
+ const orgs = await listOrgs();
111
+ if (orgs.length === 0) {
112
+ console.log("No orgs found. Run `sf org login web --alias <alias>` to authenticate an org.");
113
+ return;
114
+ }
115
+ const aliasWidth = Math.max(5, ...orgs.map((o) => o.alias.length));
116
+ const userWidth = Math.max(8, ...orgs.map((o) => o.username.length));
117
+ console.log(`${"ALIAS".padEnd(aliasWidth)} ${"USERNAME".padEnd(userWidth)} ${"TYPE".padEnd(12)} GRAPHITI`);
118
+ console.log("-".repeat(aliasWidth + userWidth + 28));
119
+ for (const org of orgs) {
120
+ const tag = org.isConnected ? "connected" : "";
121
+ console.log(`${org.alias.padEnd(aliasWidth)} ${org.username.padEnd(userWidth)} ${org.type.padEnd(12)} ${tag}`);
122
+ }
123
+ console.log("");
124
+ console.log("To connect an org: graphiti connect <alias>");
125
+ }
126
+ catch (err) {
127
+ console.error(`Error: ${err.message}`);
128
+ process.exit(1);
129
+ }
130
+ });
131
+ // ── connect ──────────────────────────────────────────────────────────────────
132
+ program
133
+ .command("connect")
134
+ .description("Connect to a Salesforce org and download its GraphQL schema")
135
+ .argument("[org-alias]", "Salesforce org alias")
136
+ .option("--refresh", "Force re-download even if schema is already cached")
137
+ .action(async (orgAlias, opts) => {
138
+ if (!orgAlias) {
139
+ console.error("Error: org alias is required. Usage: graphiti connect <org-alias>");
140
+ process.exit(1);
141
+ }
142
+ try {
143
+ await connectCommand(orgAlias, { refresh: opts.refresh });
144
+ }
145
+ catch (err) {
146
+ console.error(`Error: ${err.message}`);
147
+ process.exit(1);
148
+ }
149
+ });
150
+ // ── type (kept for backward compat, hidden) ──────────────────────────────────
151
+ program
152
+ .command("type")
153
+ .description("Inspect a named type from the schema (use `describe` instead)")
154
+ .argument("<org-alias>", "Salesforce org alias")
155
+ .argument("<type-name>", "Type name to inspect")
156
+ .action(async (orgAlias, typeName) => {
157
+ try {
158
+ await typeCommand(orgAlias, typeName);
159
+ }
160
+ catch (err) {
161
+ console.error(`Error: ${err.message}`);
162
+ process.exit(1);
163
+ }
164
+ });
165
+ // ── validate-input ───────────────────────────────────────────────────────────
166
+ program
167
+ .command("validate-input")
168
+ .description("Validate a JSON value against an input type")
169
+ .argument("<org-alias>", "Salesforce org alias")
170
+ .argument("<type-name>", "Input type name")
171
+ .argument("<json-value>", "JSON value to validate")
172
+ .action(async (orgAlias, typeName, jsonValue) => {
173
+ try {
174
+ await validateInputCommand(orgAlias, typeName, jsonValue);
175
+ }
176
+ catch (err) {
177
+ console.error(`Error: ${err.message}`);
178
+ process.exit(1);
179
+ }
180
+ });
181
+ // ── new ──────────────────────────────────────────────────────────────────────
182
+ program
183
+ .command("new")
184
+ .description("Create a new query session")
185
+ .argument("<org-alias>", "Salesforce org alias")
186
+ .option("--mutation", "Start a mutation instead of a query")
187
+ .option("--aggregate", "Start an aggregate query instead of a regular query")
188
+ .option("--name <name>", "Name for the session")
189
+ .option("-f, --force", "Replace existing session with same name")
190
+ .option("--from <session>", "Copy projection and args from an existing session")
191
+ .action(async (orgAlias, opts) => {
192
+ try {
193
+ const session = await queryNew(orgAlias, opts);
194
+ // If --from is specified, copy projection, args, and variables from the source session
195
+ if (opts.from) {
196
+ const sourceSession = loadSession(opts.from);
197
+ if (sourceSession.operation !== session.operation) {
198
+ console.log(`Warning: source session "${opts.from}" is ${sourceSession.operation} but new session is ${session.operation}. Copied paths may not be valid.`);
199
+ }
200
+ session.nodes = JSON.parse(JSON.stringify(sourceSession.nodes));
201
+ session.variables = JSON.parse(JSON.stringify(sourceSession.variables));
202
+ session.focusByPath = JSON.parse(JSON.stringify(sourceSession.focusByPath));
203
+ saveSessionFn(session);
204
+ console.log(`Copied projection from "${opts.from}".`);
205
+ }
206
+ const identifier = session.name ?? session.id;
207
+ setActiveSession(identifier);
208
+ console.log(`Active session: ${session.id}${session.name ? ` (${session.name})` : ""} — no need to run \`use\`.`);
209
+ }
210
+ catch (err) {
211
+ console.error(`Error: ${err.message}`);
212
+ process.exit(1);
213
+ }
214
+ });
215
+ // ── use ──────────────────────────────────────────────────────────────────────
216
+ program
217
+ .command("use")
218
+ .description("Set the active session")
219
+ .argument("<session-id-or-name>", "Session ID or name")
220
+ .action((sessionId) => {
221
+ try {
222
+ const session = loadSession(sessionId);
223
+ setActiveSession(session.id);
224
+ console.log(`Active session: ${session.id}${session.name ? ` (${session.name})` : ""}`);
225
+ }
226
+ catch (err) {
227
+ console.error(`Error: ${err.message}`);
228
+ process.exit(1);
229
+ }
230
+ });
231
+ // ── sessions ─────────────────────────────────────────────────────────────────
232
+ program
233
+ .command("sessions")
234
+ .description("List, delete, or prune sessions")
235
+ .argument("[action]", '"rm" or "prune"')
236
+ .argument("[target]", "Session ID/name (for rm) or ignored (for prune)")
237
+ .option("-a, --all", "Delete all sessions (with rm)")
238
+ .option("--older-than <dur>", "Duration threshold for prune (e.g. 7d, 12h)")
239
+ .action((action, target, opts) => {
240
+ try {
241
+ if (action === "rm" || action === "delete" || action === "remove") {
242
+ if (!target && opts?.all) {
243
+ querySessionsRm("--all");
244
+ }
245
+ else if (!target) {
246
+ console.error("Usage: graphiti sessions rm <session-id-or-name>");
247
+ process.exit(1);
248
+ }
249
+ else {
250
+ querySessionsRm(target);
251
+ }
252
+ }
253
+ else if (action === "prune") {
254
+ if (!opts?.olderThan) {
255
+ console.error("Usage: graphiti sessions prune --older-than <duration>");
256
+ process.exit(1);
257
+ }
258
+ querySessionsPrune(opts.olderThan);
259
+ }
260
+ else if (action === "clean") {
261
+ querySessionsClean();
262
+ }
263
+ else if (action) {
264
+ console.error(`Unknown sessions action "${action}". Use "rm", "prune", or "clean".`);
265
+ process.exit(1);
266
+ }
267
+ else {
268
+ querySessionsList();
269
+ }
270
+ }
271
+ catch (err) {
272
+ console.error(`Error: ${err.message}`);
273
+ process.exit(1);
274
+ }
275
+ });
276
+ // ── Legacy: query (backward compat) ──────────────────────────────────────────
277
+ program
278
+ .command("query")
279
+ .description("Legacy: Build GraphQL queries (use flat commands instead)")
280
+ .argument("<first>", 'Session ID, "new", "sessions", or "help"')
281
+ .argument("[rest...]", "Subcommand and arguments")
282
+ .option("--mutation", 'Start a mutation (with "new")')
283
+ .option("--name <name>", 'Session name (with "new")')
284
+ .option("--search <terms>", "Filter fields")
285
+ .option("--regex <pattern>", "Filter by regex")
286
+ .option("-l, --long", "Long listing")
287
+ .option("-a, --all", "Show all fields")
288
+ .option("--as <alias>", "Alias for selected field")
289
+ .option("-q, --quiet", "Suppress decorative output")
290
+ .option("-f, --force", "Force replace")
291
+ .option("--data-cloud", "Include Data Cloud objects")
292
+ .allowUnknownOption(true)
293
+ .action(async (first, rest, opts) => {
294
+ try {
295
+ if (first === "new") {
296
+ const orgAlias = rest[0];
297
+ if (!orgAlias) {
298
+ console.error("Usage: graphiti query new <org-alias>");
299
+ process.exit(1);
300
+ }
301
+ await queryNew(orgAlias, { mutation: opts.mutation, name: opts.name, force: opts.force });
302
+ return;
303
+ }
304
+ if (first === "help") {
305
+ queryHelp(undefined, rest[0]);
306
+ return;
307
+ }
308
+ if (first === "sessions") {
309
+ if (rest[0] === "rm" || rest[0] === "delete" || rest[0] === "remove") {
310
+ const target = rest[1];
311
+ if (!target && opts.all) {
312
+ querySessionsRm("--all");
313
+ return;
314
+ }
315
+ if (!target) {
316
+ console.error("Usage: graphiti query sessions rm <id>");
317
+ process.exit(1);
318
+ }
319
+ querySessionsRm(target);
320
+ return;
321
+ }
322
+ if (rest[0] === "prune") {
323
+ const idx = rest.indexOf("--older-than");
324
+ const dur = idx !== -1 ? rest[idx + 1] : undefined;
325
+ if (!dur) {
326
+ console.error("Usage: graphiti query sessions prune --older-than <dur>");
327
+ process.exit(1);
328
+ }
329
+ querySessionsPrune(dur);
330
+ return;
331
+ }
332
+ if (rest[0] === "clean") {
333
+ querySessionsClean();
334
+ return;
335
+ }
336
+ if (rest[0]) {
337
+ console.error(`Unknown sessions action "${rest[0]}". Use "rm", "prune", or "clean".`);
338
+ process.exit(1);
339
+ }
340
+ querySessionsList();
341
+ return;
342
+ }
343
+ const sessionId = first;
344
+ const subcommand = rest[0];
345
+ const subRest = rest.slice(1);
346
+ if (subcommand === "interactive") {
347
+ await runInteractiveSession(sessionId);
348
+ return;
349
+ }
350
+ await dispatchQueryCommand(sessionId, subcommand, subRest, opts);
351
+ }
352
+ catch (err) {
353
+ console.error(`Error: ${err.message}`);
354
+ process.exit(exitCodeForError(err));
355
+ }
356
+ });
357
+ // ── describe ─────────────────────────────────────────────────────────────────
358
+ program
359
+ .command("describe")
360
+ .description("Inspect an SObject with enriched metadata from ObjectInfo")
361
+ .argument("[sobject-or-org]", "SObject name, or org alias if second arg provided")
362
+ .argument("[sobject]", "SObject name when first arg is org alias")
363
+ .option("-s, --session <id>", "Session to infer org from")
364
+ .action(async (firstArg, secondArg, opts) => {
365
+ try {
366
+ let orgAlias;
367
+ let sObjectName;
368
+ if (secondArg) {
369
+ orgAlias = firstArg;
370
+ sObjectName = secondArg;
371
+ }
372
+ else if (firstArg) {
373
+ // Could be just an SObject name (infer org from active session)
374
+ const sessionId = opts?.session ?? getActiveSessionId();
375
+ if (sessionId) {
376
+ try {
377
+ const session = loadSession(sessionId);
378
+ orgAlias = session.orgAlias;
379
+ sObjectName = firstArg;
380
+ }
381
+ catch {
382
+ orgAlias = firstArg;
383
+ sObjectName = undefined;
384
+ }
385
+ }
386
+ else {
387
+ orgAlias = firstArg;
388
+ sObjectName = undefined;
389
+ }
390
+ }
391
+ else {
392
+ // No args — infer from active session's current navigation
393
+ const sessionId = opts?.session ?? getActiveSessionId();
394
+ if (!sessionId) {
395
+ console.error("Usage: graphiti describe <SObjectName> or graphiti describe <org> <SObjectName>");
396
+ process.exit(1);
397
+ }
398
+ const session = loadSession(sessionId);
399
+ orgAlias = session.orgAlias;
400
+ const { detectSObjectName } = await import("./commands/query-helpers.js");
401
+ sObjectName = detectSObjectName(session) ?? undefined;
402
+ if (!sObjectName) {
403
+ console.error("Cannot detect SObject from current navigation. Usage: graphiti describe <SObjectName>");
404
+ process.exit(1);
405
+ }
406
+ }
407
+ if (!sObjectName) {
408
+ console.error("Usage: graphiti describe <SObjectName> or graphiti describe <org> <SObjectName>");
409
+ process.exit(1);
410
+ }
411
+ await describeCommand(orgAlias, sObjectName);
412
+ }
413
+ catch (err) {
414
+ console.error(`Error: ${err.message}`);
415
+ process.exit(1);
416
+ }
417
+ });
418
+ // ── Flat session commands ────────────────────────────────────────────────────
419
+ // These are the new top-level commands that use implicit session resolution.
420
+ function resolveSessionId(opts) {
421
+ const fromFlag = opts?.session ?? program.opts()?.session;
422
+ if (fromFlag)
423
+ return fromFlag;
424
+ const active = getActiveSessionId();
425
+ if (active)
426
+ return active;
427
+ console.error("Error: No active session. Create one with `graphiti new <org>` or set one with `graphiti use <session>`.");
428
+ process.exit(1);
429
+ }
430
+ // Common options for all session commands
431
+ const sessionOpts = (cmd) => cmd
432
+ .option("-s, --session <id>", "Session ID or name")
433
+ .option("--search <terms>", "Filter fields by name")
434
+ .option("--regex <pattern>", "Filter fields by regex")
435
+ .option("-l, --long", "Long listing with metadata")
436
+ .option("-a, --all", "Show all fields")
437
+ .option("--as <alias>", "Alias for selected field")
438
+ .option("-q, --quiet", "Suppress decorative output")
439
+ .option("--data-cloud", "Include Data Cloud objects")
440
+ .option("--json", "Output in JSON format");
441
+ const COMMAND_NAME_MAP = {};
442
+ for (const cmdName of [
443
+ "pwd",
444
+ "cd",
445
+ "ls",
446
+ "select",
447
+ "drop",
448
+ "alias",
449
+ "set",
450
+ "unset",
451
+ "var",
452
+ "show",
453
+ "check",
454
+ "run",
455
+ "undo",
456
+ "clone",
457
+ "reset",
458
+ "optional",
459
+ "codegen",
460
+ ]) {
461
+ const def = resolveCommand(cmdName);
462
+ if (!def)
463
+ continue;
464
+ const cmd = program
465
+ .command(cmdName)
466
+ .description(def.summary)
467
+ .argument("[args...]", "Command arguments")
468
+ .allowUnknownOption(true);
469
+ sessionOpts(cmd);
470
+ if (def.usage || (def.examples && def.examples.length > 0)) {
471
+ const helpLines = [""];
472
+ if (def.usage)
473
+ helpLines.push(`Syntax: graphiti ${def.usage}`);
474
+ if (def.examples && def.examples.length > 0) {
475
+ helpLines.push("", "Examples:");
476
+ for (const ex of def.examples) {
477
+ helpLines.push(` ${ex}`);
478
+ }
479
+ }
480
+ cmd.addHelpText("after", helpLines.join("\n"));
481
+ }
482
+ cmd.action(async (args, opts) => {
483
+ try {
484
+ const sessionId = resolveSessionId(opts);
485
+ const dispatchName = COMMAND_NAME_MAP[cmdName] ?? cmdName;
486
+ // Pass args through to the dispatcher, preserving --flags that it handles
487
+ // (--dry-run, --strict, --var, --default, --name, --older-than, etc.)
488
+ // Also pass --json through so the dispatcher can detect it.
489
+ const filteredArgs = [];
490
+ for (let i = 0; i < args.length; i++) {
491
+ if (args[i] === "--search" ||
492
+ args[i] === "--regex" ||
493
+ args[i] === "--as" ||
494
+ args[i] === "--session" ||
495
+ args[i] === "-s") {
496
+ i++; // skip value-taking flags handled by Commander
497
+ }
498
+ else if (args[i] === "-l" ||
499
+ args[i] === "--long" ||
500
+ args[i] === "-a" ||
501
+ args[i] === "--all" ||
502
+ args[i] === "-q" ||
503
+ args[i] === "--quiet" ||
504
+ args[i] === "--data-cloud") {
505
+ // skip boolean flags handled by Commander
506
+ }
507
+ else {
508
+ filteredArgs.push(args[i]);
509
+ }
510
+ }
511
+ // Pass --all through for check command (Commander parses it into opts.all)
512
+ if (dispatchName === "check" && opts.all) {
513
+ filteredArgs.push("--all");
514
+ }
515
+ // Inject --json if Commander parsed it or env var is set
516
+ if (opts.json || program.opts()?.json) {
517
+ filteredArgs.push("--json");
518
+ }
519
+ const result = await dispatchQueryCommand(sessionId, dispatchName, filteredArgs, {
520
+ search: opts.search,
521
+ regex: opts.regex,
522
+ long: opts.long,
523
+ all: opts.all,
524
+ as: opts.as,
525
+ quiet: opts.quiet,
526
+ dataCloud: opts.dataCloud,
527
+ });
528
+ // Auto-activate the cloned session so subsequent commands target it
529
+ if (dispatchName === "clone" &&
530
+ result &&
531
+ typeof result === "object" &&
532
+ "_clonedSessionId" in result) {
533
+ setActiveSession(result._clonedSessionId);
534
+ }
535
+ }
536
+ catch (err) {
537
+ console.error(`Error: ${err.message}`);
538
+ process.exit(exitCodeForError(err));
539
+ }
540
+ });
541
+ }
542
+ // ── interactive (flat) ───────────────────────────────────────────────────────
543
+ program
544
+ .command("interactive")
545
+ .description("Start an interactive REPL session")
546
+ .option("-s, --session <id>", "Session ID or name")
547
+ .action(async (opts) => {
548
+ try {
549
+ const sessionId = resolveSessionId(opts);
550
+ await runInteractiveSession(sessionId);
551
+ }
552
+ catch (err) {
553
+ console.error(`Error: ${err.message}`);
554
+ process.exit(1);
555
+ }
556
+ });
557
+ // ── agent-guide ──────────────────────────────────────────────────────────────
558
+ program
559
+ .command("agent-guide")
560
+ .description("Print the agent usage guide (AGENT_GUIDE.md) to stdout")
561
+ .action(() => {
562
+ try {
563
+ const here = path.dirname(fileURLToPath(import.meta.url));
564
+ const candidates = [
565
+ path.join(here, "..", "AGENT_GUIDE.md"),
566
+ path.join(here, "..", "..", "AGENT_GUIDE.md"),
567
+ ];
568
+ const guidePath = candidates.find((p) => fs.existsSync(p));
569
+ if (!guidePath) {
570
+ console.error("Error: AGENT_GUIDE.md not found next to the graphiti CLI.");
571
+ process.exit(1);
572
+ }
573
+ process.stdout.write(fs.readFileSync(guidePath, "utf-8"));
574
+ }
575
+ catch (err) {
576
+ console.error(`Error: ${err.message}`);
577
+ process.exit(1);
578
+ }
579
+ });
580
+ // ── help ─────────────────────────────────────────────────────────────────────
581
+ program
582
+ .command("help")
583
+ .description("Show help for a command")
584
+ .argument("[topic]", "Command name to get help for")
585
+ .action((topic) => {
586
+ try {
587
+ queryHelp(undefined, topic);
588
+ }
589
+ catch (err) {
590
+ console.error(`Error: ${err.message}`);
591
+ process.exit(1);
592
+ }
593
+ });
594
+ // ── chain (semicolon-separated commands) ─────────────────────────────────
595
+ const CHAIN_EXCLUDED_COMMANDS = new Set([
596
+ "new",
597
+ "use",
598
+ "connect",
599
+ "orgs",
600
+ "interactive",
601
+ "describe",
602
+ "type",
603
+ "validate-input",
604
+ "chain",
605
+ ]);
606
+ program
607
+ .command("chain")
608
+ .description("Run multiple commands in sequence, separated by semicolons")
609
+ .argument("<commands>", 'Semicolon-separated commands (e.g. "cd Case; select Id; check")')
610
+ .option("-s, --session <id>", "Session ID or name")
611
+ .option("--json", "Output JSON array of results")
612
+ .action(async (commandStr, opts) => {
613
+ try {
614
+ const sessionId = resolveSessionId(opts);
615
+ const commands = commandStr
616
+ .split(";")
617
+ .map((c) => c.trim())
618
+ .filter(Boolean);
619
+ const jsonMode = opts.json || program.opts()?.json || process.env.GRAPHITI_AGENT === "1";
620
+ const results = [];
621
+ if (jsonMode) {
622
+ // Capture individual command JSON outputs and emit as a single array at the end
623
+ const origLog = console.log;
624
+ for (const cmd of commands) {
625
+ const tokens = tokenizeCommand(cmd);
626
+ if (tokens.length === 0)
627
+ continue;
628
+ const subcommand = COMMAND_NAME_MAP[tokens[0]] ?? tokens[0];
629
+ if (CHAIN_EXCLUDED_COMMANDS.has(subcommand)) {
630
+ throw new CommandError(`"${subcommand}" cannot be used inside chain. Run it as a separate command before chaining session commands.`);
631
+ }
632
+ const subRest = [...tokens.slice(1), "--json"];
633
+ // Capture the JSON output from each command
634
+ const captured = [];
635
+ console.log = (...args) => {
636
+ captured.push(args.map(String).join(" "));
637
+ };
638
+ try {
639
+ await dispatchQueryCommand(sessionId, subcommand, subRest, {});
640
+ }
641
+ finally {
642
+ console.log = origLog;
643
+ }
644
+ // Parse the captured JSON output back into an object
645
+ const jsonStr = captured.join("\n").trim();
646
+ if (jsonStr) {
647
+ try {
648
+ results.push(JSON.parse(jsonStr));
649
+ }
650
+ catch {
651
+ results.push({ command: subcommand, output: jsonStr });
652
+ }
653
+ }
654
+ }
655
+ origLog(JSON.stringify(results, null, 2));
656
+ }
657
+ else {
658
+ const startSession = loadSession(sessionId);
659
+ const startingPath = [...startSession.navigationPath];
660
+ for (const cmd of commands) {
661
+ const tokens = tokenizeCommand(cmd);
662
+ if (tokens.length === 0)
663
+ continue;
664
+ const subcommand = COMMAND_NAME_MAP[tokens[0]] ?? tokens[0];
665
+ if (CHAIN_EXCLUDED_COMMANDS.has(subcommand)) {
666
+ throw new CommandError(`"${subcommand}" cannot be used inside chain. Run it as a separate command before chaining session commands.`);
667
+ }
668
+ const subRest = tokens.slice(1);
669
+ await dispatchQueryCommand(sessionId, subcommand, subRest, {});
670
+ }
671
+ const finalSession = loadSession(sessionId);
672
+ if (JSON.stringify(finalSession.navigationPath) !== JSON.stringify(startingPath)) {
673
+ console.log(`\n${formatNavigationPath(finalSession.navigationPath)}`);
674
+ }
675
+ }
676
+ }
677
+ catch (err) {
678
+ console.error(`Error: ${err.message}`);
679
+ process.exit(exitCodeForError(err));
680
+ }
681
+ });
682
+ program.parse();
683
+ //# sourceMappingURL=cli.js.map