@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
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import {
8
+ CommandError,
9
+ getSessionSchema,
10
+ selectLeafInSession,
11
+ parsePathInput,
12
+ printQuery,
13
+ } from "./query-helpers.js";
14
+ import {
15
+ loadSession,
16
+ saveSession,
17
+ getNavigationContext,
18
+ isInArgsContext,
19
+ queryNavToSchemaPath,
20
+ getArgsFieldPath,
21
+ getInputSubPath,
22
+ getChildren,
23
+ getFocusedNodeAtPath,
24
+ formatPath,
25
+ findDescendantByAlias,
26
+ removeNodeByIdWithPrune,
27
+ removeSelectionAtPath,
28
+ removeVariable,
29
+ deepRemoveArg,
30
+ removeListElement,
31
+ appendListElement,
32
+ createSiblingFieldInstance,
33
+ type FieldProjectionNode,
34
+ } from "../lib/session.js";
35
+ import { resolveFieldOnPath } from "../lib/walker.js";
36
+
37
+ export function querySelect(sessionId: string, leafName: string, opts: { alias?: string }): void {
38
+ const session = loadSession(sessionId);
39
+ selectLeafInSession(session, leafName, opts.alias);
40
+ saveSession(session);
41
+
42
+ console.log(`Selected ${leafName}${opts.alias ? ` as ${opts.alias}` : ""}.`);
43
+ console.log("");
44
+ printQuery(session);
45
+ }
46
+
47
+ export function querySelectLs(sessionId: string): void {
48
+ const session = loadSession(sessionId);
49
+ const currentSchemaPath = queryNavToSchemaPath(session.navigationPath);
50
+
51
+ const leaves = session.nodes.filter((node): node is FieldProjectionNode => {
52
+ if (node.kind !== "field") return false;
53
+ return getChildren(session, node.id).length === 0;
54
+ });
55
+
56
+ if (leaves.length === 0) {
57
+ console.log("No fields selected. Use `select <field>` or `select <path:alias>` to add fields.");
58
+ return;
59
+ }
60
+
61
+ console.log(`Selected fields (${leaves.length}):`);
62
+ console.log("");
63
+ for (const leaf of leaves) {
64
+ const fullPath = leaf.schemaPath;
65
+ let displayPath: string;
66
+ if (
67
+ currentSchemaPath.length > 0 &&
68
+ fullPath.length > currentSchemaPath.length &&
69
+ currentSchemaPath.every((seg, i) => seg === fullPath[i])
70
+ ) {
71
+ displayPath = fullPath.slice(currentSchemaPath.length).join(".");
72
+ } else {
73
+ displayPath = "/" + fullPath.join("/");
74
+ }
75
+ const aliasPart = leaf.alias ? ` → ${leaf.alias}` : "";
76
+ const optPart = leaf.directives.some((d) => d.name === "optional") ? " @optional" : "";
77
+ console.log(` ${displayPath}${aliasPart}${optPart}`);
78
+ }
79
+ console.log("");
80
+ console.log("Remove with: rm <alias> or rm <dot.path>");
81
+ }
82
+
83
+ export function queryRm(sessionId: string, rawInput: string): void {
84
+ const session = loadSession(sessionId);
85
+ const ctx = getNavigationContext(session.navigationPath);
86
+
87
+ if (rawInput.startsWith("$") && !isInArgsContext(session.navigationPath)) {
88
+ const cleanName = rawInput.replace(/^\$/, "");
89
+ const removed = removeVariable(session, cleanName);
90
+ if (!removed) {
91
+ throw new CommandError(`Variable "$${cleanName}" is not defined.`);
92
+ }
93
+ saveSession(session);
94
+ console.log(`Removed variable $${cleanName} and all references to it.`);
95
+ console.log("Tip: use `undo` to restore it.");
96
+ console.log("");
97
+ printQuery(session);
98
+ return;
99
+ }
100
+
101
+ if (ctx === "variables" && !isInArgsContext(session.navigationPath)) {
102
+ const cleanName = rawInput.replace(/^\$/, "");
103
+ const removed = removeVariable(session, cleanName);
104
+ if (!removed) {
105
+ throw new CommandError(`Variable "$${cleanName}" is not defined.`);
106
+ }
107
+ saveSession(session);
108
+ console.log(`Removed variable $${cleanName} and all references to it.`);
109
+ console.log("Tip: use `undo` to restore it.");
110
+ console.log("");
111
+ printQuery(session);
112
+ return;
113
+ }
114
+
115
+ if (isInArgsContext(session.navigationPath)) {
116
+ const fieldSchemaPath = getArgsFieldPath(session.navigationPath);
117
+ const inputSubPath = getInputSubPath(session.navigationPath);
118
+
119
+ const fullPath = [...inputSubPath, ...rawInput.split(".")];
120
+
121
+ if (fullPath.length === 0) {
122
+ throw new CommandError("Usage: rm <argName> or rm <index>");
123
+ }
124
+
125
+ const argName = fullPath[0];
126
+ const nested = fullPath.slice(1);
127
+
128
+ if (/^\d+$/.test(rawInput) && inputSubPath.length > 0) {
129
+ const parentArgName = inputSubPath[0];
130
+ const parentNested = inputSubPath.slice(1);
131
+ const removed = removeListElement(
132
+ session,
133
+ fieldSchemaPath,
134
+ parentArgName,
135
+ parentNested,
136
+ Number(rawInput),
137
+ );
138
+ if (removed) {
139
+ saveSession(session);
140
+ console.log(`Removed list element ${rawInput}.`);
141
+ console.log("");
142
+ printQuery(session);
143
+ return;
144
+ }
145
+ }
146
+
147
+ const removed = deepRemoveArg(session, fieldSchemaPath, argName, nested);
148
+ if (!removed) {
149
+ throw new CommandError(`No value set at "${rawInput}".`);
150
+ }
151
+ saveSession(session);
152
+ console.log(`Removed "${rawInput}".`);
153
+ console.log("");
154
+ printQuery(session);
155
+ return;
156
+ }
157
+
158
+ const currentSchemaPath = queryNavToSchemaPath(session.navigationPath);
159
+
160
+ let spec = rawInput;
161
+ let explicitAlias: string | undefined;
162
+ const colonIdx = rawInput.lastIndexOf(":");
163
+ const afterColon = colonIdx > 0 ? rawInput.slice(colonIdx + 1) : "";
164
+ if (colonIdx > 0 && afterColon.length > 0 && !/[./:]/.test(afterColon)) {
165
+ spec = rawInput.slice(0, colonIdx);
166
+ explicitAlias = afterColon;
167
+ }
168
+
169
+ const normalizedSpec =
170
+ spec.includes(".") && !spec.includes("/") ? spec.replace(/\./g, "/") : spec;
171
+
172
+ if (normalizedSpec.includes("/")) {
173
+ const parts = normalizedSpec.split("/");
174
+ const leafName = parts.pop()!;
175
+ const dirPath = parts.join("/");
176
+ const resolved = parsePathInput(session.navigationPath, dirPath);
177
+ const targetSchemaPath = queryNavToSchemaPath(resolved);
178
+ const selector = explicitAlias ?? undefined;
179
+ const removed =
180
+ removeSelectionAtPath(session, [...targetSchemaPath, leafName], selector) ||
181
+ removeSelectionAtPath(session, [...targetSchemaPath, leafName]);
182
+ if (!removed) {
183
+ throw new CommandError(`No selected projection matching "${rawInput}" exists.`);
184
+ }
185
+ saveSession(session);
186
+ console.log(`Removed "${rawInput}".`);
187
+ console.log("");
188
+ printQuery(session);
189
+ return;
190
+ }
191
+
192
+ const focusedNode = getFocusedNodeAtPath(session, currentSchemaPath, false);
193
+ const parentId = focusedNode?.id ?? null;
194
+ const aliasedChild = getChildren(session, parentId).find(
195
+ (n): n is FieldProjectionNode => n.kind === "field" && n.alias === rawInput,
196
+ );
197
+ if (aliasedChild) {
198
+ const removed =
199
+ removeSelectionAtPath(session, [...currentSchemaPath, aliasedChild.fieldName], rawInput) ||
200
+ removeSelectionAtPath(session, [...currentSchemaPath, aliasedChild.fieldName]);
201
+ if (removed) {
202
+ saveSession(session);
203
+ console.log(`Removed "${rawInput}".`);
204
+ console.log("");
205
+ printQuery(session);
206
+ return;
207
+ }
208
+ }
209
+
210
+ const fieldRemoved =
211
+ removeSelectionAtPath(session, [...currentSchemaPath, rawInput], rawInput) ||
212
+ removeSelectionAtPath(session, [...currentSchemaPath, rawInput]);
213
+ if (fieldRemoved) {
214
+ saveSession(session);
215
+ console.log(`Removed "${rawInput}".`);
216
+ console.log("");
217
+ printQuery(session);
218
+ return;
219
+ }
220
+
221
+ const deepNode = findDescendantByAlias(session, parentId, rawInput);
222
+ if (deepNode) {
223
+ removeNodeByIdWithPrune(session, deepNode.id);
224
+ saveSession(session);
225
+ console.log(`Removed "${rawInput}".`);
226
+ console.log("");
227
+ printQuery(session);
228
+ return;
229
+ }
230
+
231
+ throw new CommandError(
232
+ `No selected projection matching "${rawInput}" exists under ${formatPath(session.navigationPath)}.` +
233
+ "\nTip: Use dot-path syntax (e.g. `rm Owner.Name.value`) or alias name. Run `select ls` to see all selections.",
234
+ );
235
+ }
236
+
237
+ export function queryMkdir(sessionId: string, rest: string[]): void {
238
+ const session = loadSession(sessionId);
239
+ const ctx = getNavigationContext(session.navigationPath);
240
+
241
+ if (isInArgsContext(session.navigationPath)) {
242
+ const fieldSchemaPath = getArgsFieldPath(session.navigationPath);
243
+ const inputSubPath = getInputSubPath(session.navigationPath);
244
+
245
+ if (inputSubPath.length === 0) {
246
+ throw new CommandError(
247
+ "Navigate to a specific argument first (e.g. `cd orderBy`), then use `mkdir` to add a list element.",
248
+ );
249
+ }
250
+
251
+ const argName = inputSubPath[0];
252
+ const nestedPath = inputSubPath.slice(1);
253
+
254
+ const idx = appendListElement(session, fieldSchemaPath, argName, nestedPath);
255
+ saveSession(session);
256
+ console.log(`Created element ${idx}/`);
257
+ console.log(`Navigate into it with: cd ${idx}`);
258
+ return;
259
+ }
260
+
261
+ if (ctx === "query") {
262
+ if (rest.length < 2) {
263
+ throw new CommandError(
264
+ "Usage: mkdir <alias> <fieldName> — creates an aliased projection instance.",
265
+ );
266
+ }
267
+ const alias = rest[0];
268
+ const fieldName = rest[1];
269
+
270
+ const schemaPath = queryNavToSchemaPath(session.navigationPath);
271
+ const schema = getSessionSchema(session);
272
+
273
+ resolveFieldOnPath(schema, session.operation, schemaPath, fieldName);
274
+
275
+ const targetPath = [...schemaPath, fieldName];
276
+ createSiblingFieldInstance(session, targetPath, alias);
277
+ saveSession(session);
278
+ console.log(`Created aliased instance: ${alias}(${fieldName})/`);
279
+ console.log(`Navigate into it with: cd ${alias}`);
280
+ console.log("");
281
+ printQuery(session);
282
+ return;
283
+ }
284
+
285
+ throw new CommandError(
286
+ "`mkdir` works inside `query/` (to create aliased instances) or inside `@args/` (to add list elements).",
287
+ );
288
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { getOrgAuth } from "../lib/auth.js";
8
+ import { downloadSchema, getSchemaMetadata } from "../lib/introspect.js";
9
+
10
+ export async function connectCommand(
11
+ orgAlias: string,
12
+ opts: { refresh?: boolean } = {},
13
+ ): Promise<void> {
14
+ console.log(`Connecting to ${orgAlias}...`);
15
+
16
+ const auth = await getOrgAuth(orgAlias);
17
+ console.log(`Authenticated as ${auth.username} on ${auth.instanceUrl}`);
18
+
19
+ if (!opts.refresh) {
20
+ const existing = getSchemaMetadata(auth.instanceUrl);
21
+ if (existing) {
22
+ const age = formatSchemaAge(new Date(existing.downloadedAt));
23
+ console.log(`Schema already cached: ${existing.typeCount} types (downloaded ${age}).`);
24
+ console.log(`Run \`graphiti connect ${orgAlias} --refresh\` to re-download.`);
25
+ return;
26
+ }
27
+ }
28
+
29
+ console.log("Downloading GraphQL schema via introspection...");
30
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
31
+ let i = 0;
32
+ const startTime = Date.now();
33
+ const spinner = setInterval(() => {
34
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);
35
+ process.stderr.write(`\r${frames[i++ % frames.length]} Downloading schema... (${elapsed}s)`);
36
+ }, 100);
37
+
38
+ try {
39
+ const meta = await downloadSchema(auth);
40
+ clearInterval(spinner);
41
+ process.stderr.write("\r");
42
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
43
+ console.log(`Connected to ${orgAlias}. Schema cached: ${meta.typeCount} types (${elapsed}s).`);
44
+ } catch (err) {
45
+ clearInterval(spinner);
46
+ process.stderr.write("\r");
47
+ throw err;
48
+ }
49
+ }
50
+
51
+ function formatSchemaAge(date: Date): string {
52
+ const diffMs = Date.now() - date.getTime();
53
+ const diffMin = Math.floor(diffMs / 60_000);
54
+ if (diffMin < 1) return "just now";
55
+ if (diffMin < 60) return `${diffMin}m ago`;
56
+ const diffHr = Math.floor(diffMin / 60);
57
+ if (diffHr < 24) return `${diffHr}h ago`;
58
+ const diffDays = Math.floor(diffHr / 24);
59
+ return `${diffDays}d ago`;
60
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ /* eslint-disable @typescript-eslint/no-explicit-any -- graphiti traverses untyped schema/introspection JSON; see follow-up to replace with `unknown` + narrowing */
7
+
8
+ import type { GraphQLSchema } from "graphql";
9
+ import { getOrgAuth } from "../lib/auth.js";
10
+ import {
11
+ getObjectInfo,
12
+ getRequiredCreateFields,
13
+ type ObjectInfoResult,
14
+ type FieldMetadata,
15
+ } from "../lib/object-info.js";
16
+ import { getSchema, inspectType } from "../lib/walker.js";
17
+
18
+ export async function describeCommand(orgAlias: string, sObjectName: string): Promise<void> {
19
+ const auth = await getOrgAuth(orgAlias);
20
+ const schema = getSchema(auth.instanceUrl);
21
+
22
+ let objInfo: ObjectInfoResult | null = null;
23
+ try {
24
+ objInfo = await getObjectInfo(auth, orgAlias, sObjectName);
25
+ } catch {
26
+ // Fall back to schema-only
27
+ }
28
+
29
+ if (!objInfo) {
30
+ console.log(`Could not fetch ObjectInfo for "${sObjectName}". Showing schema-only view.`);
31
+ console.log("");
32
+ showSchemaOnlyView(schema, sObjectName);
33
+ return;
34
+ }
35
+
36
+ const _metaByApi = new Map(objInfo.fields.map((f) => [f.apiName, f]));
37
+
38
+ // Object header
39
+ const capabilities: string[] = [];
40
+ if (objInfo.queryable) capabilities.push("queryable");
41
+ if (objInfo.createable) capabilities.push("createable");
42
+ if (objInfo.updateable) capabilities.push("updateable");
43
+ if (objInfo.deletable) capabilities.push("deletable");
44
+ if (objInfo.searchable) capabilities.push("searchable");
45
+
46
+ console.log(
47
+ `${sObjectName} "${objInfo.labelPlural ?? objInfo.label ?? sObjectName}" (keyPrefix: ${objInfo.keyPrefix ?? "n/a"}, nameField: ${objInfo.nameFields.join(", ") || "n/a"})`,
48
+ );
49
+ console.log(` ${capabilities.join(", ")}`);
50
+ if (objInfo.recordTypeInfos.length > 1) {
51
+ const available = objInfo.recordTypeInfos.filter((r) => r.available);
52
+ console.log(` Record types: ${available.map((r) => r.name).join(", ")}`);
53
+ }
54
+ console.log("");
55
+
56
+ // Fields table
57
+ console.log(`Fields (${objInfo.fields.length}):`);
58
+ const sortedFields = [...objInfo.fields].sort((a, b) => {
59
+ // Name field first, then required, then alphabetical
60
+ if (a.nameField && !b.nameField) return -1;
61
+ if (!a.nameField && b.nameField) return 1;
62
+ if (a.required && !b.required) return -1;
63
+ if (!a.required && b.required) return 1;
64
+ return a.apiName.localeCompare(b.apiName);
65
+ });
66
+
67
+ for (const field of sortedFields) {
68
+ const line = formatFieldRow(field, objInfo);
69
+ console.log(` ${line}`);
70
+ }
71
+
72
+ // Create fields
73
+ const createFields = objInfo.fields.filter((f) => f.createable);
74
+ const requiredOnCreate = getRequiredCreateFields(objInfo);
75
+ if (createFields.length > 0) {
76
+ console.log("");
77
+ console.log(
78
+ `Create fields (${createFields.length}): ${createFields.map((f) => f.apiName).join(", ")}`,
79
+ );
80
+ if (requiredOnCreate.length > 0) {
81
+ console.log(` Required on create: ${requiredOnCreate.map((f) => f.apiName).join(", ")}`);
82
+ } else {
83
+ console.log(" Required on create: (none -- all have defaults or are optional)");
84
+ }
85
+ }
86
+
87
+ // Update fields
88
+ const updateFields = objInfo.fields.filter((f) => f.updateable);
89
+ if (updateFields.length > 0) {
90
+ console.log("");
91
+ console.log(
92
+ `Update fields (${updateFields.length}): ${updateFields.map((f) => f.apiName).join(", ")}`,
93
+ );
94
+ const createOnly = objInfo.fields.filter((f) => f.createable && !f.updateable);
95
+ if (createOnly.length > 0) {
96
+ console.log(` Create-only (cannot update): ${createOnly.map((f) => f.apiName).join(", ")}`);
97
+ }
98
+ }
99
+
100
+ // Child relationships
101
+ const namedRels = objInfo.childRelationships.filter((cr) => cr.relationshipName);
102
+ if (namedRels.length > 0) {
103
+ console.log("");
104
+ console.log(`Child relationships (${namedRels.length}):`);
105
+ for (const cr of namedRels.slice(0, 20)) {
106
+ console.log(
107
+ ` ${cr.relationshipName!.padEnd(30)} -> ${cr.childObjectApiName}.${cr.fieldName}`,
108
+ );
109
+ }
110
+ if (namedRels.length > 20) {
111
+ console.log(` ... ${namedRels.length - 20} more`);
112
+ }
113
+ }
114
+
115
+ // Picklists summary
116
+ if (objInfo.picklists.length > 0) {
117
+ console.log("");
118
+ console.log(`Picklist fields (${objInfo.picklists.length}):`);
119
+ for (const pl of objInfo.picklists) {
120
+ const vals = pl.values.map((v) => v.value).filter((v): v is string => v !== null);
121
+ console.log(` ${pl.apiName.padEnd(25)} ${vals.join(", ")}`);
122
+ }
123
+ }
124
+
125
+ // Filter and OrderBy examples
126
+ console.log("");
127
+ console.log("Filter examples:");
128
+ const filterableFields = objInfo.fields.filter((f) => f.filterable && !f.compound);
129
+ const picklistField = filterableFields.find((f) => f.dataType === "PICKLIST");
130
+ const stringField = filterableFields.find((f) => f.dataType === "STRING" && f.filterable);
131
+ const dateField = filterableFields.find((f) => f.dataType === "DATETIME" && f.filterable);
132
+
133
+ if (picklistField) {
134
+ const pl = objInfo.picklists.find((p) => p.apiName === picklistField.apiName);
135
+ const val = pl?.values[0]?.value ?? "<value>";
136
+ console.log(` { "${picklistField.apiName}": { "eq": "${val}" } }`);
137
+ }
138
+ if (stringField) {
139
+ console.log(` { "${stringField.apiName}": { "like": "%search%" } }`);
140
+ }
141
+ if (picklistField && dateField) {
142
+ console.log(
143
+ ` { "and": [{ "${picklistField.apiName}": { "eq": "<value>" } }, { "${dateField.apiName}": { "gt": "2024-01-01T00:00:00Z" } }] }`,
144
+ );
145
+ }
146
+
147
+ const sortableFields = objInfo.fields.filter((f) => f.sortable && !f.compound);
148
+ if (sortableFields.length > 0) {
149
+ console.log("");
150
+ console.log("OrderBy examples:");
151
+ const first = sortableFields[0];
152
+ console.log(` { "${first.apiName}": { "order": "DESC" } }`);
153
+ if (sortableFields.length > 1) {
154
+ console.log(
155
+ ` { "${sortableFields[0].apiName}": { "order": "ASC" }, "${sortableFields[1].apiName}": { "order": "DESC" } }`,
156
+ );
157
+ }
158
+ }
159
+ }
160
+
161
+ function formatFieldRow(field: FieldMetadata, objInfo: ObjectInfoResult): string {
162
+ const name = field.apiName.padEnd(28);
163
+ const dataType = (field.dataType ?? "UNKNOWN").padEnd(14);
164
+
165
+ const label = field.label && field.label !== field.apiName ? `"${field.label}"` : "";
166
+
167
+ const tags: string[] = [];
168
+ if (field.required && field.createable && !field.defaultedOnCreate) tags.push("required");
169
+ if (field.required && field.defaultedOnCreate) tags.push("auto");
170
+ if (field.nameField) tags.push("name-field");
171
+ if (!field.filterable) tags.push("no-filter");
172
+ if (!field.sortable) tags.push("no-sort");
173
+ if (!field.createable && !field.updateable) tags.push("read-only");
174
+ if (field.createable && !field.updateable) tags.push("create-only");
175
+ if (field.defaultedOnCreate && field.createable) tags.push("default-on-create");
176
+ if (field.calculated) tags.push("formula");
177
+ if (field.custom) tags.push("custom");
178
+ if (field.compound) tags.push("compound");
179
+ if (field.compoundFieldName) tags.push(`child of ${field.compoundFieldName}`);
180
+
181
+ let extra = "";
182
+ if (field.reference && field.referenceToInfos.length > 0) {
183
+ const targets = field.referenceToInfos.map((r) => {
184
+ const nameFields = r.nameFields.length > 0 ? ` (${r.nameFields.join(", ")})` : "";
185
+ return `${r.apiName}${nameFields}`;
186
+ });
187
+ extra = `-> ${targets.join(", ")}`;
188
+ }
189
+
190
+ const picklist = objInfo.picklists.find((p) => p.apiName === field.apiName);
191
+ if (picklist && picklist.values.length > 0) {
192
+ const vals = picklist.values.map((v) => v.value).filter((v): v is string => v !== null);
193
+ if (vals.length <= 6) {
194
+ extra = `values: [${vals.join(", ")}]`;
195
+ } else {
196
+ extra = `values: [${vals.slice(0, 5).join(", ")}, ... +${vals.length - 5}]`;
197
+ }
198
+ }
199
+
200
+ if (field.extraTypeInfo) {
201
+ extra += extra ? ` (${field.extraTypeInfo})` : `(${field.extraTypeInfo})`;
202
+ }
203
+
204
+ if (field.precision > 0) {
205
+ extra += extra ? ` precision=${field.precision}` : `precision=${field.precision}`;
206
+ if (field.scale > 0) extra += `,scale=${field.scale}`;
207
+ }
208
+
209
+ if (field.inlineHelpText) {
210
+ extra += extra ? ` -- ${field.inlineHelpText}` : field.inlineHelpText;
211
+ }
212
+
213
+ const tagStr = tags.length > 0 ? `[${tags.join(", ")}]` : "";
214
+ const labelStr = label ? ` ${label}` : "";
215
+
216
+ return `${name} ${dataType}${labelStr} ${tagStr} ${extra}`.trimEnd();
217
+ }
218
+
219
+ function showSchemaOnlyView(schema: GraphQLSchema, typeName: string): void {
220
+ try {
221
+ const info = inspectType(schema, typeName);
222
+ console.log(`${typeName} (${info.kind})`);
223
+ if (info.description) console.log(` ${info.description}`);
224
+ console.log("");
225
+ if (info.fields.length > 0) {
226
+ console.log("Fields:");
227
+ for (const f of info.fields) {
228
+ console.log(` ${f.name.padEnd(28)} ${f.typeName}${f.isNonNull ? "!" : ""}`);
229
+ }
230
+ }
231
+ if (info.inputFields.length > 0) {
232
+ console.log("Input fields:");
233
+ for (const f of info.inputFields) {
234
+ console.log(` ${f.name.padEnd(28)} ${f.typeName}${f.isNonNull ? "!" : ""}`);
235
+ }
236
+ }
237
+ if (info.enumValues.length > 0) {
238
+ console.log("Values:");
239
+ for (const v of info.enumValues) {
240
+ console.log(` ${v.name}${v.description ? ` -- ${v.description}` : ""}`);
241
+ }
242
+ }
243
+ } catch (_e: any) {
244
+ console.error(`Type "${typeName}" not found in schema.`);
245
+ }
246
+ }