@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,137 @@
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 { describe, expect, it } from "vitest";
8
+ import { TEST_SCHEMA, makeSession } from "../../__tests__/helpers/schema.js";
9
+ import { captureStdout } from "../../__tests__/helpers/stdout.js";
10
+ import { createSession, loadSession, saveSession } from "../../lib/session.js";
11
+ import { clearSchemaCache, primeSchemaCache } from "../../lib/walker.js";
12
+ import {
13
+ dispatchQueryCommand,
14
+ getSessionSchema,
15
+ resolveDirectoryPathInSession,
16
+ selectLeafInSession,
17
+ } from "../query.js";
18
+
19
+ describe("commands/query", () => {
20
+ describe("dispatchQueryCommand", () => {
21
+ it("routes help correctly", async () => {
22
+ const session = makeSession();
23
+ saveSession(session);
24
+
25
+ const output = await captureStdout(async () => {
26
+ await dispatchQueryCommand(session.id, "help", [], {});
27
+ });
28
+
29
+ expect(output.includes("pwd") || output.includes("select")).toBe(true);
30
+ });
31
+
32
+ it("routes unknown commands with an error message", async () => {
33
+ const session = makeSession();
34
+ saveSession(session);
35
+
36
+ await expect(dispatchQueryCommand(session.id, "zzz_unknown", [], {})).rejects.toThrow(
37
+ /Unknown subcommand/,
38
+ );
39
+ });
40
+
41
+ it("cd updates session navigation path", async () => {
42
+ const session = makeSession();
43
+ session.navigationPath = [];
44
+ saveSession(session);
45
+
46
+ await captureStdout(async () => {
47
+ await dispatchQueryCommand(session.id, "cd", ["query/viewer"], {});
48
+ });
49
+
50
+ const updated = loadSession(session.id);
51
+ expect(updated.navigationPath).toEqual(["query", "viewer"]);
52
+ });
53
+
54
+ it("select adds leaf to projection", async () => {
55
+ const session = makeSession();
56
+ session.navigationPath = ["query", "viewer"];
57
+ saveSession(session);
58
+
59
+ await captureStdout(async () => {
60
+ await dispatchQueryCommand(session.id, "select", ["id"], {});
61
+ });
62
+
63
+ const updated = loadSession(session.id);
64
+ expect(updated.nodes.some((n) => n.kind === "field" && n.fieldName === "id")).toBe(true);
65
+ });
66
+ });
67
+
68
+ describe("resolveDirectoryPathInSession (cd)", () => {
69
+ it("resolves query/ prefix paths", () => {
70
+ const session = makeSession();
71
+ session.navigationPath = ["query"];
72
+
73
+ const resolved = resolveDirectoryPathInSession(session, "accounts");
74
+ expect(resolved).toEqual(["query", "accounts"]);
75
+ });
76
+
77
+ it("handles @args navigation", () => {
78
+ const session = makeSession();
79
+ session.navigationPath = ["query", "accounts"];
80
+
81
+ const resolved = resolveDirectoryPathInSession(session, "@args");
82
+ expect(resolved).toEqual(["query", "accounts", "@args"]);
83
+ });
84
+
85
+ it("cd into @args input object fields", () => {
86
+ const session = makeSession();
87
+ session.navigationPath = ["query", "accounts", "@args"];
88
+
89
+ const resolved = resolveDirectoryPathInSession(session, "where");
90
+ expect(resolved).toEqual(["query", "accounts", "@args", "where"]);
91
+ });
92
+
93
+ it("rejects scalar args as leaf", () => {
94
+ const session = makeSession();
95
+ session.navigationPath = ["query", "accounts", "@args"];
96
+
97
+ expect(() => resolveDirectoryPathInSession(session, "first")).toThrow(
98
+ /Cannot cd into "first" — it is a scalar/,
99
+ );
100
+ });
101
+ });
102
+
103
+ describe("selectLeafInSession", () => {
104
+ it("leaf-only selection rejects non-leaf fields", () => {
105
+ const session = makeSession();
106
+ session.navigationPath = ["query"];
107
+
108
+ expect(() => selectLeafInSession(session, "viewer")).toThrow(/Cannot select "viewer"/);
109
+
110
+ session.navigationPath = ["query", "viewer"];
111
+ selectLeafInSession(session, "id");
112
+ });
113
+
114
+ it("select through union without brackets gives a helpful error", () => {
115
+ const session = makeSession();
116
+ session.navigationPath = ["query", "search"];
117
+
118
+ expect(() => selectLeafInSession(session, "name")).toThrow(
119
+ /Use inline fragment syntax: on:Account\/name/,
120
+ );
121
+ });
122
+ });
123
+
124
+ describe("getSessionSchema", () => {
125
+ it("resolves by session.instanceUrl", () => {
126
+ clearSchemaCache();
127
+ primeSchemaCache("https://o.my.salesforce.com", TEST_SCHEMA);
128
+ const session = createSession("o", "query", "https://o.my.salesforce.com");
129
+ expect(getSessionSchema(session)).toBe(TEST_SCHEMA);
130
+ });
131
+
132
+ it("throws when session has no instanceUrl", () => {
133
+ const session = createSession("o", "query");
134
+ expect(() => getSessionSchema(session)).toThrow(/instanceUrl/);
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,306 @@
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
+ walkerResultAtPath,
11
+ parsePathInput,
12
+ resolveAliasSegments,
13
+ pathPointsToArgs,
14
+ assignViaPath,
15
+ assignInArgsContext,
16
+ assignInVariablesContext,
17
+ inferTypeFromArgsPath,
18
+ printQuery,
19
+ } from "./query-helpers.js";
20
+ import {
21
+ loadSession,
22
+ saveSession,
23
+ syncFocusFromNavigationPath,
24
+ getNavigationContext,
25
+ isInArgsContext,
26
+ getArgsFieldPath,
27
+ getInputSubPath,
28
+ addVariable,
29
+ deepSetArg,
30
+ deepRemoveArg,
31
+ formatPath,
32
+ type QuerySession,
33
+ } from "../lib/session.js";
34
+ import { resolvePath } from "../lib/walker.js";
35
+
36
+ export function queryAssign(
37
+ sessionId: string,
38
+ specs: { path: string; value: string }[],
39
+ setDefault = false,
40
+ ): void {
41
+ const session = loadSession(sessionId);
42
+ syncFocusFromNavigationPath(session);
43
+ const ctx = getNavigationContext(session.navigationPath);
44
+
45
+ for (const { path: dotPath, value } of specs) {
46
+ if (pathPointsToArgs(dotPath)) {
47
+ assignViaPath(session, dotPath, value);
48
+ console.log(`Assigned ${dotPath} = ${value}`);
49
+ continue;
50
+ }
51
+
52
+ const segments = dotPath.includes("/")
53
+ ? dotPath.split("/")
54
+ : dotPath.includes(".")
55
+ ? dotPath.split(".")
56
+ : [dotPath];
57
+
58
+ if (ctx === "variables") {
59
+ assignInVariablesContext(session, segments, value, setDefault);
60
+ } else if (isInArgsContext(session.navigationPath)) {
61
+ assignInArgsContext(session, segments, value);
62
+ } else if (ctx === "query") {
63
+ const normalizedDotPath = dotPath.replace(/\./g, "/");
64
+ assignViaPath(session, `@args/${normalizedDotPath}`, value);
65
+ const displayPath = segments.join(".");
66
+ console.log(`Assigned @args/${displayPath} = ${value}`);
67
+ continue;
68
+ } else {
69
+ throw new CommandError(
70
+ "The `assign` command works inside `@args/` or `/variables/`, or with a path containing `@args/`.\n" +
71
+ "Example: `assign @args/first 10` or `cd @args` first.",
72
+ );
73
+ }
74
+
75
+ const displayPath = segments.join(".");
76
+ console.log(`Assigned ${displayPath} = ${value}`);
77
+ }
78
+
79
+ saveSession(session);
80
+ console.log("");
81
+ printQuery(session);
82
+ }
83
+
84
+ export function queryUnassign(sessionId: string, rawPath: string): void {
85
+ const session = loadSession(sessionId);
86
+ syncFocusFromNavigationPath(session);
87
+
88
+ const normalized = rawPath.replace(/\./g, "/");
89
+ const _resolved: string[] = [];
90
+
91
+ let rawResolved = parsePathInput(session.navigationPath, normalized);
92
+ if (rawResolved.length > 0 && rawResolved[0] !== "query" && rawResolved[0] !== "variables") {
93
+ rawResolved = ["query", ...rawResolved];
94
+ }
95
+ rawResolved = resolveAliasSegments(session, rawResolved);
96
+
97
+ // Collect all candidate @args resolutions and try each until one succeeds.
98
+ // This handles both explicit @args paths and shorthand paths like
99
+ // "uiapi/query/Case/where/Status" → "uiapi/query/Case/@args/where/Status"
100
+ const candidates: string[][] = [];
101
+
102
+ if (isInArgsContext(rawResolved)) {
103
+ candidates.push(rawResolved);
104
+ }
105
+
106
+ // Try injecting @args/ at each possible position
107
+ const parts = normalized.split("/");
108
+ for (let i = parts.length - 1; i >= 1; i--) {
109
+ const fieldPath = parts.slice(0, i).join("/");
110
+ const argPath = parts.slice(i).join("/");
111
+ const candidate = parsePathInput(session.navigationPath, `${fieldPath}/@args/${argPath}`);
112
+ if (candidate.length > 0 && candidate[0] !== "query" && candidate[0] !== "variables") {
113
+ candidate.unshift("query");
114
+ }
115
+ if (isInArgsContext(candidate)) {
116
+ candidates.push(resolveAliasSegments(session, candidate));
117
+ }
118
+ }
119
+
120
+ // Fall back: try prepending @args/ to whole path (works when navigated into a field)
121
+ const withArgs = parsePathInput(session.navigationPath, `@args/${normalized}`);
122
+ if (isInArgsContext(withArgs)) {
123
+ candidates.push(withArgs);
124
+ }
125
+
126
+ if (candidates.length === 0) {
127
+ throw new CommandError(
128
+ `Path "${rawPath}" does not point into an @args/ directory. ` +
129
+ "Use a path like `@args/first` or `unassign first`.",
130
+ );
131
+ }
132
+
133
+ // Try each candidate — use the first one where data actually exists
134
+ let removed = false;
135
+ for (const candidate of candidates) {
136
+ const fieldSchemaPath = getArgsFieldPath(candidate);
137
+ const inputSubPath = getInputSubPath(candidate);
138
+
139
+ if (inputSubPath.length === 0) continue;
140
+
141
+ const argName = inputSubPath[0];
142
+ const inputPath = inputSubPath.slice(1);
143
+
144
+ if (deepRemoveArg(session, fieldSchemaPath, argName, inputPath)) {
145
+ removed = true;
146
+ break;
147
+ }
148
+ }
149
+
150
+ if (!removed) {
151
+ throw new CommandError(`No value set at "${rawPath}".`);
152
+ }
153
+
154
+ saveSession(session);
155
+ console.log(`Removed ${rawPath}.`);
156
+ console.log("");
157
+ printQuery(session);
158
+ }
159
+
160
+ export function queryDefine(sessionId: string, rest: string[]): void {
161
+ const session = loadSession(sessionId);
162
+ const varName = rest[0];
163
+ if (!varName) {
164
+ throw new CommandError(
165
+ "Usage: define $name <path> [default]\n e.g. define $filter @args/where",
166
+ );
167
+ }
168
+ const cleanName = varName.replace(/^\$/, "");
169
+ const pathArg = rest[1];
170
+ const defaultValue = rest[2];
171
+
172
+ if (pathArg) {
173
+ defineAtPath(session, cleanName, pathArg, defaultValue);
174
+ saveSession(session);
175
+ console.log("");
176
+ printQuery(session);
177
+ return;
178
+ }
179
+
180
+ if (isInArgsContext(session.navigationPath)) {
181
+ defineFromCurrentArgsPosition(session, cleanName);
182
+ saveSession(session);
183
+ console.log("");
184
+ printQuery(session);
185
+ return;
186
+ }
187
+
188
+ const ctx = getNavigationContext(session.navigationPath);
189
+ if (ctx === "query") {
190
+ const schemaPath = getArgsFieldPath(session.navigationPath);
191
+ if (schemaPath.length > 0) {
192
+ const wr = walkerResultAtPath(session);
193
+ if (wr.args.length === 0) {
194
+ throw new CommandError(
195
+ "This field has no arguments. Provide a path: `define $name @args/<arg>`.",
196
+ );
197
+ }
198
+ }
199
+ throw new CommandError(
200
+ "Provide a path: `define $name @args/where` or `define $name Account/@args/first`.",
201
+ );
202
+ }
203
+
204
+ if (ctx === "variables") {
205
+ throw new CommandError(
206
+ "Provide a path to an argument: `define $name /query/uiapi/query/Account/@args/where`.",
207
+ );
208
+ }
209
+
210
+ throw new CommandError("Usage: define $name <path> [default]\n e.g. define $filter @args/where");
211
+ }
212
+
213
+ function defineFromCurrentArgsPosition(session: QuerySession, cleanName: string): void {
214
+ const schema = getSessionSchema(session);
215
+ const fieldSchemaPath = getArgsFieldPath(session.navigationPath);
216
+ const inputSubPath = getInputSubPath(session.navigationPath);
217
+
218
+ if (inputSubPath.length === 0) {
219
+ throw new CommandError(
220
+ "At @args/ root. Specify which argument: `define $name first` or `define $name where`.",
221
+ );
222
+ }
223
+
224
+ const { inferredType, argName, argInputPath } = inferTypeFromArgsPath(
225
+ schema,
226
+ session.operation,
227
+ fieldSchemaPath,
228
+ inputSubPath,
229
+ );
230
+ addVariable(session, cleanName, inferredType);
231
+ deepSetArg(session, fieldSchemaPath, argName, argInputPath, `$${cleanName}`);
232
+
233
+ const pathDesc = inputSubPath.join(".");
234
+ console.log(`Defined $${cleanName}: ${inferredType}`);
235
+ console.log(`Auto-assigned: ${pathDesc} = $${cleanName}`);
236
+ console.log("Tip: use `undo` to remove this variable definition.");
237
+ }
238
+
239
+ // Exported for the MCP `sf_gql_raw` parser (lib/apply-command.ts), which needs the
240
+ // pure var-define logic without queryDefine's disk load/save. Already pure — only
241
+ // the `export` keyword is added.
242
+ export function defineAtPath(
243
+ session: QuerySession,
244
+ cleanName: string,
245
+ rawPath: string,
246
+ defaultValue?: string,
247
+ ): void {
248
+ const schema = getSessionSchema(session);
249
+
250
+ const normalized =
251
+ rawPath.includes(".") && !rawPath.includes("/") ? rawPath.replace(/\./g, "/") : rawPath;
252
+
253
+ let resolved = parsePathInput(session.navigationPath, normalized);
254
+
255
+ if (resolved.length > 0 && resolved[0] !== "query" && resolved[0] !== "variables") {
256
+ resolved = ["query", ...resolved];
257
+ }
258
+
259
+ if (!isInArgsContext(resolved)) {
260
+ // For mutations, auto-inject @args/input if the path points to a mutation field
261
+ if (session.operation === "mutation") {
262
+ const withArgsInput = [...resolved, "@args", "input"];
263
+ if (isInArgsContext(withArgsInput)) {
264
+ resolved = withArgsInput;
265
+ } else {
266
+ throw new CommandError(
267
+ `Path "${rawPath}" does not point into an @args/ directory. ` +
268
+ `Hint: for mutations, use the path with /@args/input appended, e.g. \`${rawPath}/@args/input\`.`,
269
+ );
270
+ }
271
+ } else {
272
+ throw new CommandError(
273
+ `Path "${rawPath}" does not point into an @args/ directory. ` +
274
+ "Use a path like `@args/where` or `Account/@args/first`.",
275
+ );
276
+ }
277
+ }
278
+
279
+ const fieldSchemaPath = getArgsFieldPath(resolved);
280
+ const inputSubPath = getInputSubPath(resolved);
281
+
282
+ if (inputSubPath.length === 0) {
283
+ throw new CommandError(
284
+ "Path points to @args/ root. Specify a specific argument: e.g. `@args/where` or `@args/first`.",
285
+ );
286
+ }
287
+
288
+ const wr = resolvePath(schema, session.operation, fieldSchemaPath);
289
+ if (wr.args.length === 0) {
290
+ throw new CommandError(`Field at ${formatPath(fieldSchemaPath)} has no arguments.`);
291
+ }
292
+
293
+ const { inferredType, argName, argInputPath } = inferTypeFromArgsPath(
294
+ schema,
295
+ session.operation,
296
+ fieldSchemaPath,
297
+ inputSubPath,
298
+ );
299
+ addVariable(session, cleanName, inferredType, defaultValue);
300
+ deepSetArg(session, fieldSchemaPath, argName, argInputPath, `$${cleanName}`);
301
+
302
+ const pathDesc = inputSubPath.join(".");
303
+ console.log(`Defined $${cleanName}: ${inferredType}${defaultValue ? ` = ${defaultValue}` : ""}`);
304
+ console.log(`Auto-assigned: ${pathDesc} = $${cleanName}`);
305
+ console.log("Tip: use `undo` to remove this variable definition.");
306
+ }