@gajae-code/coding-agent 0.11.7 → 0.11.9

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 (213) hide show
  1. package/CHANGELOG.md +73 -1
  2. package/dist/types/cli/args.d.ts +0 -2
  3. package/dist/types/cli.d.ts +1 -0
  4. package/dist/types/config/keybindings.d.ts +33 -3
  5. package/dist/types/config/model-profile-activation.d.ts +4 -3
  6. package/dist/types/config/model-profile-contract.d.ts +38 -0
  7. package/dist/types/config/model-profiles.d.ts +2 -3
  8. package/dist/types/config/settings-schema.d.ts +15 -2
  9. package/dist/types/edit/modes/patch.d.ts +13 -0
  10. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  11. package/dist/types/eval/py/executor.d.ts +1 -1
  12. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  13. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +12 -5
  14. package/dist/types/gjc-runtime/deep-interview-draft.d.ts +8 -0
  15. package/dist/types/gjc-runtime/deep-interview-payload.d.ts +41 -0
  16. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +32 -17
  17. package/dist/types/gjc-runtime/deep-interview-repair.d.ts +17 -0
  18. package/dist/types/gjc-runtime/deep-interview-state.d.ts +135 -4
  19. package/dist/types/gjc-runtime/repository-binding.d.ts +63 -0
  20. package/dist/types/gjc-runtime/state-writer.d.ts +44 -0
  21. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +1 -0
  22. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +24 -0
  23. package/dist/types/gjc-runtime/workflow-manifest.d.ts +5 -3
  24. package/dist/types/main.d.ts +8 -0
  25. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  26. package/dist/types/modes/components/tool-status-header.d.ts +3 -2
  27. package/dist/types/modes/components/welcome.d.ts +4 -0
  28. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  29. package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
  30. package/dist/types/modes/interactive-mode.d.ts +5 -2
  31. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  32. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  33. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  34. package/dist/types/modes/types.d.ts +3 -0
  35. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  36. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  37. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  38. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  39. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  40. package/dist/types/sdk/broker/broker.d.ts +2 -0
  41. package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
  42. package/dist/types/sdk/bus/chat-daemon-control.d.ts +7 -1
  43. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  44. package/dist/types/sdk/bus/config.d.ts +2 -0
  45. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  46. package/dist/types/sdk/bus/index.d.ts +25 -0
  47. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  48. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +105 -0
  49. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +15 -3
  50. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  51. package/dist/types/sdk/bus/telegram-daemon.d.ts +128 -1
  52. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  53. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  54. package/dist/types/sdk/host/host.d.ts +1 -1
  55. package/dist/types/sdk/host/query/handlers.d.ts +9 -0
  56. package/dist/types/sdk/index.d.ts +2 -0
  57. package/dist/types/sdk/prompt-status.d.ts +88 -0
  58. package/dist/types/sdk/session-directory.d.ts +1 -1
  59. package/dist/types/sdk/startup-capability.d.ts +3 -0
  60. package/dist/types/session/agent-session.d.ts +2 -0
  61. package/dist/types/session/internal/managed-session-scope.d.ts +22 -3
  62. package/dist/types/session/internal/managed-session-storage.d.ts +24 -4
  63. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  64. package/dist/types/session/session-manager.d.ts +5 -1
  65. package/dist/types/session/session-storage.d.ts +31 -0
  66. package/dist/types/skill-state/active-state.d.ts +4 -0
  67. package/dist/types/task/index.d.ts +3 -1
  68. package/dist/types/task/receipt.d.ts +2 -0
  69. package/dist/types/task/types.d.ts +94 -0
  70. package/dist/types/tools/ask.d.ts +454 -174
  71. package/dist/types/tools/image-gen.d.ts +47 -1
  72. package/dist/types/tools/index.d.ts +4 -0
  73. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  74. package/package.json +8 -8
  75. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  76. package/scripts/dogfood-repository-binding.ts +209 -0
  77. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  78. package/scripts/run-test-manifest.ts +4 -4
  79. package/src/cli/args.ts +2 -92
  80. package/src/cli/mcp-cli.ts +4 -21
  81. package/src/cli/notify-cli.ts +5 -38
  82. package/src/cli.ts +50 -0
  83. package/src/commands/deep-interview.ts +9 -4
  84. package/src/commands/sdk.ts +2 -2
  85. package/src/commit/agentic/index.ts +3 -3
  86. package/src/commit/map-reduce/index.ts +2 -2
  87. package/src/config/keybindings.ts +157 -24
  88. package/src/config/model-profile-activation.ts +7 -18
  89. package/src/config/model-profile-contract.ts +161 -0
  90. package/src/config/model-profiles.ts +2 -3
  91. package/src/config/model-registry.ts +11 -1
  92. package/src/config/settings-schema.ts +17 -2
  93. package/src/coordinator-mcp/model-preset.ts +21 -74
  94. package/src/defaults/gjc/skills/deep-interview/SKILL.md +80 -165
  95. package/src/defaults/gjc/skills/ralplan/SKILL.md +3 -1
  96. package/src/edit/index.ts +3 -6
  97. package/src/edit/modes/patch.ts +38 -1
  98. package/src/edit/modes/replace.ts +42 -0
  99. package/src/edit/path-mutation-lock.ts +112 -0
  100. package/src/eval/py/executor.ts +30 -11
  101. package/src/eval/py/prelude.py +2 -2
  102. package/src/eval/py/tool-bridge.ts +34 -18
  103. package/src/extensibility/gjc-plugins/runtime-adapters.ts +2 -1
  104. package/src/gjc-runtime/deep-interview-ambiguity.ts +68 -47
  105. package/src/gjc-runtime/deep-interview-draft.ts +842 -0
  106. package/src/gjc-runtime/deep-interview-payload.ts +262 -0
  107. package/src/gjc-runtime/deep-interview-recorder.ts +294 -226
  108. package/src/gjc-runtime/deep-interview-repair.ts +1674 -0
  109. package/src/gjc-runtime/deep-interview-runtime.ts +264 -103
  110. package/src/gjc-runtime/deep-interview-state.ts +916 -4
  111. package/src/gjc-runtime/launch-tmux.ts +1 -1
  112. package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
  113. package/src/gjc-runtime/ralplan-runtime.ts +78 -4
  114. package/src/gjc-runtime/repository-binding.ts +267 -0
  115. package/src/gjc-runtime/session-state-sidecar.ts +25 -9
  116. package/src/gjc-runtime/state-writer.ts +166 -2
  117. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -1
  118. package/src/gjc-runtime/tmux-sessions.ts +45 -6
  119. package/src/gjc-runtime/ultragoal-guard.ts +5 -5
  120. package/src/gjc-runtime/ultragoal-runtime.ts +181 -19
  121. package/src/gjc-runtime/workflow-manifest.generated.json +386 -1
  122. package/src/gjc-runtime/workflow-manifest.ts +191 -5
  123. package/src/internal-urls/docs-index.generated.ts +8 -6
  124. package/src/internal-urls/local-protocol.ts +33 -16
  125. package/src/internal-urls/mcp-protocol.ts +73 -11
  126. package/src/main.ts +25 -10
  127. package/src/modes/DESIGN.md +5 -0
  128. package/src/modes/acp/acp-agent.ts +50 -16
  129. package/src/modes/components/model-selector.ts +42 -8
  130. package/src/modes/components/plan-preview-overlay.ts +1 -1
  131. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  132. package/src/modes/components/tool-status-header.ts +20 -4
  133. package/src/modes/components/welcome.ts +32 -20
  134. package/src/modes/controllers/command-controller.ts +97 -54
  135. package/src/modes/controllers/event-controller.ts +10 -0
  136. package/src/modes/controllers/input-controller.ts +7 -2
  137. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  138. package/src/modes/controllers/selector-controller.ts +46 -54
  139. package/src/modes/interactive-mode.ts +70 -36
  140. package/src/modes/prompt-action-autocomplete.ts +32 -13
  141. package/src/modes/prompt-suggestion-controller.ts +96 -0
  142. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  143. package/src/modes/types.ts +3 -0
  144. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  145. package/src/modes/utils/ui-helpers.ts +5 -3
  146. package/src/prompts/agents/architect.md +0 -1
  147. package/src/prompts/agents/critic.md +0 -1
  148. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  149. package/src/prompts/system/system-prompt.md +7 -4
  150. package/src/prompts/tools/cron.md +3 -1
  151. package/src/runtime-mcp/json-rpc.ts +7 -4
  152. package/src/runtime-mcp/manager.ts +51 -53
  153. package/src/runtime-mcp/oauth-flow.ts +0 -72
  154. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  155. package/src/runtime-mcp/redaction.ts +30 -0
  156. package/src/runtime-mcp/transports/http.ts +13 -6
  157. package/src/sdk/broker/broker.ts +34 -20
  158. package/src/sdk/broker/lifecycle.ts +185 -36
  159. package/src/sdk/broker/session-index.ts +11 -1
  160. package/src/sdk/bus/chat-daemon-control.ts +20 -5
  161. package/src/sdk/bus/config-commands.ts +2 -2
  162. package/src/sdk/bus/config.ts +9 -1
  163. package/src/sdk/bus/conversation-store.ts +40 -6
  164. package/src/sdk/bus/index.ts +240 -65
  165. package/src/sdk/bus/lifecycle-commands.ts +47 -3
  166. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  167. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  168. package/src/sdk/bus/notification-orchestration.ts +12 -0
  169. package/src/sdk/bus/notification-service.ts +27 -2
  170. package/src/sdk/bus/prompt-reconciliation.ts +239 -0
  171. package/src/sdk/bus/recent-activity.ts +90 -26
  172. package/src/sdk/bus/telegram-daemon-contract.ts +15 -3
  173. package/src/sdk/bus/telegram-daemon-control.ts +145 -33
  174. package/src/sdk/bus/telegram-daemon.ts +853 -112
  175. package/src/sdk/bus/telegram-reference.ts +20 -2
  176. package/src/sdk/cli/session-cli.ts +15 -2
  177. package/src/sdk/host/control/dispatch.ts +1 -1
  178. package/src/sdk/host/control/operations.ts +1 -1
  179. package/src/sdk/host/host.ts +2 -6
  180. package/src/sdk/host/query/handlers.ts +70 -5
  181. package/src/sdk/index.ts +12 -0
  182. package/src/sdk/prompt-status.ts +97 -0
  183. package/src/sdk/protocol/operation-inventory.generated.json +47 -0
  184. package/src/sdk/protocol/operation-registry.ts +18 -4
  185. package/src/sdk/session-directory.ts +5 -1
  186. package/src/sdk/session.ts +2 -0
  187. package/src/sdk/startup-capability.ts +9 -1
  188. package/src/session/agent-session.ts +138 -5
  189. package/src/session/blob-store.ts +7 -2
  190. package/src/session/internal/managed-session-scope.ts +998 -133
  191. package/src/session/internal/managed-session-storage.ts +268 -203
  192. package/src/session/internal/native-publish-outcome.ts +266 -0
  193. package/src/session/session-manager.ts +406 -360
  194. package/src/session/session-storage.ts +151 -11
  195. package/src/skill-state/active-state.ts +10 -0
  196. package/src/task/executor.ts +13 -5
  197. package/src/task/gjc-command.ts +2 -2
  198. package/src/task/index.ts +121 -15
  199. package/src/task/receipt.ts +3 -0
  200. package/src/task/types.ts +34 -3
  201. package/src/tools/ask.ts +199 -88
  202. package/src/tools/ast-edit.ts +2 -2
  203. package/src/tools/bash-pty-selection.ts +2 -2
  204. package/src/tools/browser/screenshot-format.ts +6 -1
  205. package/src/tools/cron.ts +1 -1
  206. package/src/tools/image-gen.ts +178 -6
  207. package/src/tools/index.ts +4 -0
  208. package/src/tools/render-utils.ts +7 -1
  209. package/src/utils/edit-mode.ts +2 -2
  210. package/src/utils/prompt-suggestion.ts +265 -0
  211. package/src/utils/sixel.ts +3 -3
  212. package/src/web/insane/url-guard.ts +3 -1
  213. package/src/web/scrapers/docs-rs.ts +30 -5
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Product-surface dogfood for #2901 repository binding.
3
+ *
4
+ * Creates two sibling git repos, runs real `gjc ultragoal` / `gjc ralplan`
5
+ * CLI entrypoints from source, and prints fail-closed mismatch evidence.
6
+ *
7
+ * Usage (from monorepo root):
8
+ * bun packages/coding-agent/scripts/dogfood-repository-binding.ts
9
+ */
10
+ import * as fsp from "node:fs/promises";
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import {
15
+ assertCwdMatchesRepositoryBinding,
16
+ assertPathUnderRepositoryBinding,
17
+ parseRepositoryBinding,
18
+ resolveTaskRepositoryBinding,
19
+ } from "../src/gjc-runtime/repository-binding";
20
+ import { readUltragoalPlan, startNextUltragoalGoal } from "../src/gjc-runtime/ultragoal-runtime";
21
+
22
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
23
+ const cli = path.join(repoRoot, "packages/coding-agent/src/cli.ts");
24
+
25
+ async function runGit(cwd: string, args: string[]): Promise<void> {
26
+ const proc = Bun.spawn(["git", ...args], { cwd, stdout: "pipe", stderr: "pipe" });
27
+ const code = await proc.exited;
28
+ if (code !== 0) throw new Error(`git ${args.join(" ")} failed: ${await new Response(proc.stderr).text()}`);
29
+ }
30
+
31
+ async function initRepo(root: string): Promise<void> {
32
+ await fsp.mkdir(root, { recursive: true });
33
+ await runGit(root, ["init"]);
34
+ await runGit(root, ["config", "user.email", "dogfood@example.com"]);
35
+ await runGit(root, ["config", "user.name", "Dogfood"]);
36
+ await fsp.writeFile(path.join(root, "README.md"), `repo ${path.basename(root)}\n`);
37
+ await runGit(root, ["add", "README.md"]);
38
+ await runGit(root, ["commit", "-m", `init ${path.basename(root)}`]);
39
+ }
40
+
41
+ async function runCli(
42
+ cwd: string,
43
+ args: string[],
44
+ env: NodeJS.ProcessEnv,
45
+ ): Promise<{ code: number; stdout: string; stderr: string }> {
46
+ const proc = Bun.spawn(["bun", cli, ...args], { cwd, env, stdout: "pipe", stderr: "pipe" });
47
+ const [code, stdout, stderr] = await Promise.all([
48
+ proc.exited,
49
+ new Response(proc.stdout).text(),
50
+ new Response(proc.stderr).text(),
51
+ ]);
52
+ return { code, stdout, stderr };
53
+ }
54
+
55
+ async function main(): Promise<void> {
56
+ const dogfoodRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "gjc-dogfood-2901-"));
57
+ const left = path.join(dogfoodRoot, "gajae-code");
58
+ const right = path.join(dogfoodRoot, "oh-my-openagent-senpi");
59
+ await initRepo(left);
60
+ await initRepo(right);
61
+
62
+ const sessionId = `dogfood-2901-${process.pid}`;
63
+ const env = { ...process.env, GJC_SESSION_ID: sessionId };
64
+
65
+ console.log("# Dogfood: repository binding (#2901)");
66
+ console.log(`root=${dogfoodRoot}`);
67
+ console.log(`left=${left}`);
68
+ console.log(`right=${right}`);
69
+ console.log(`session=${sessionId}`);
70
+ console.log(`cli=${cli}`);
71
+ console.log(`bun=${Bun.version}`);
72
+ console.log(
73
+ `commit=${Bun.spawnSync(["git", "-C", repoRoot, "rev-parse", "--short", "HEAD"]).stdout.toString().trim()}`,
74
+ );
75
+ console.log();
76
+
77
+ // 1) Product CLI: create goals stamps binding
78
+ const create = await runCli(
79
+ left,
80
+ [
81
+ "ultragoal",
82
+ "create-goals",
83
+ "--brief",
84
+ "@goal dogfood binding\nProve repository binding stamps and fail-closed sibling mismatch.",
85
+ "--json",
86
+ ],
87
+ env,
88
+ );
89
+ console.log("## 1) gjc ultragoal create-goals (LEFT)");
90
+ console.log(`exit=${create.code}`);
91
+ console.log(create.stdout.trim() || create.stderr.trim());
92
+ if (create.code !== 0) process.exit(1);
93
+
94
+ const plan = await readUltragoalPlan(left, sessionId);
95
+ if (!plan?.repositoryBinding) {
96
+ console.error("FAIL: plan missing repositoryBinding");
97
+ process.exit(1);
98
+ }
99
+ console.log();
100
+ console.log("## 2) goals.json repositoryBinding");
101
+ console.log(JSON.stringify(plan.repositoryBinding, null, 2));
102
+
103
+ // 2) Matching cwd starts goal
104
+ console.log();
105
+ console.log("## 3) startNextUltragoalGoal on LEFT (match)");
106
+ const started = await startNextUltragoalGoal({ cwd: left, sessionId });
107
+ console.log(`ok goal=${started.goal?.id} status=${started.goal?.status}`);
108
+
109
+ // 3) Sibling mismatch fails closed
110
+ console.log();
111
+ console.log("## 4) assertCwdMatchesRepositoryBinding on RIGHT (sibling)");
112
+ try {
113
+ await assertCwdMatchesRepositoryBinding(right, plan.repositoryBinding);
114
+ console.error("FAIL: expected identity_mismatch");
115
+ process.exit(1);
116
+ } catch (error) {
117
+ console.log(`fail_closed: ${error instanceof Error ? error.message : String(error)}`);
118
+ }
119
+
120
+ // 4) Path escape fails closed
121
+ console.log();
122
+ console.log("## 5) assertPathUnderRepositoryBinding sibling absolute path");
123
+ try {
124
+ assertPathUnderRepositoryBinding(plan.repositoryBinding, path.join(right, "README.md"));
125
+ console.error("FAIL: expected path_outside_root");
126
+ process.exit(1);
127
+ } catch (error) {
128
+ console.log(`fail_closed: ${error instanceof Error ? error.message : String(error)}`);
129
+ }
130
+
131
+ // 5) Task binding stamp + sibling fail-closed (pre-discovery authority)
132
+ console.log();
133
+ console.log("## 6) task binding stamp (omit declaration) + sibling reject");
134
+ const stamped = await resolveTaskRepositoryBinding(left, undefined);
135
+ console.log(`stamped_worktreeRoot=${stamped.worktreeRoot}`);
136
+ await assertCwdMatchesRepositoryBinding(left, stamped);
137
+ console.log("task_stamp_ok on LEFT");
138
+ const taskBinding = parseRepositoryBinding(plan.repositoryBinding);
139
+ try {
140
+ await resolveTaskRepositoryBinding(right, taskBinding);
141
+ console.error("FAIL: task binding should reject RIGHT");
142
+ process.exit(1);
143
+ } catch (error) {
144
+ console.log(`task_binding_fail_closed on RIGHT: ${error instanceof Error ? error.message : String(error)}`);
145
+ }
146
+
147
+ // 6) Ralplan seed stamps binding via product CLI
148
+ console.log();
149
+ console.log("## 7) gjc ralplan seed (LEFT)");
150
+ const ralplan = await runCli(left, ["ralplan", "--json", "dogfood multi-repo binding"], env);
151
+ console.log(`exit=${ralplan.code}`);
152
+ console.log(ralplan.stdout.trim() || ralplan.stderr.trim());
153
+ if (ralplan.code !== 0) process.exit(1);
154
+ const statePath = path.join(left, ".gjc", `_session-${sessionId}`, "state", "ralplan-state.json");
155
+ const state = JSON.parse(await fsp.readFile(statePath, "utf8")) as {
156
+ repository_binding?: unknown;
157
+ run_id?: string;
158
+ };
159
+ console.log();
160
+ console.log("## 8) ralplan-state.json repository_binding");
161
+ console.log(JSON.stringify({ run_id: state.run_id, repository_binding: state.repository_binding }, null, 2));
162
+ if (!state.repository_binding) {
163
+ console.error("FAIL: ralplan state missing repository_binding");
164
+ process.exit(1);
165
+ }
166
+
167
+ // 7) Ralplan stage write on LEFT succeeds and echoes repository_binding
168
+ console.log();
169
+ console.log("## 9) gjc ralplan --write planner on LEFT (match)");
170
+ const notePath = path.join(left, "dogfood-planner.md");
171
+ await fsp.writeFile(notePath, "# dogfood planner\n");
172
+ const writeLeft = await runCli(
173
+ left,
174
+ ["ralplan", "--write", "--stage", "planner", "--stage_n", "1", "--artifact", notePath, "--json"],
175
+ env,
176
+ );
177
+ console.log(`exit=${writeLeft.code}`);
178
+ console.log(writeLeft.stdout.trim() || writeLeft.stderr.trim());
179
+ if (writeLeft.code !== 0) process.exit(1);
180
+ const writePayload = JSON.parse(writeLeft.stdout) as { repository_binding?: { worktreeRoot?: string } };
181
+ if (!writePayload.repository_binding?.worktreeRoot) {
182
+ console.error("FAIL: write receipt missing repository_binding");
183
+ process.exit(1);
184
+ }
185
+
186
+ // 8) Copy seed authority into RIGHT session layout → stage write fails closed
187
+ console.log();
188
+ console.log("## 10) ralplan --write on RIGHT with LEFT binding (fail-closed)");
189
+ const rightStateDir = path.join(right, ".gjc", `_session-${sessionId}`, "state");
190
+ await fsp.mkdir(rightStateDir, { recursive: true });
191
+ await fsp.copyFile(statePath, path.join(rightStateDir, "ralplan-state.json"));
192
+ const writeRight = await runCli(
193
+ right,
194
+ ["ralplan", "--write", "--stage", "architect", "--stage_n", "1", "--artifact", "# arch\n", "--json"],
195
+ env,
196
+ );
197
+ console.log(`exit=${writeRight.code}`);
198
+ console.log((writeRight.stderr || writeRight.stdout).trim());
199
+ if (writeRight.code === 0) {
200
+ console.error("FAIL: expected sibling write to fail closed");
201
+ process.exit(1);
202
+ }
203
+ console.log("fail_closed: ralplan write on sibling rejected");
204
+
205
+ console.log();
206
+ console.log("DOGFOOD_OK");
207
+ }
208
+
209
+ await main();
@@ -39,6 +39,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
39
39
  "agent_session:setForcedToolChoice": "internal accessor/plumbing, not a user-facing control seam",
40
40
  "agent_session:getActiveSkillState": "internal accessor/plumbing, not a user-facing control seam",
41
41
  "agent_session:getActiveSkillPhase": "internal accessor/plumbing, not a user-facing control seam",
42
+ "agent_session:getDeepInterviewAskStage":
43
+ "internal AskTool schema-selection accessor, not a user-facing SDK control seam",
42
44
  "agent_session:peekQueueInvoker": "internal accessor/plumbing, not a user-facing control seam",
43
45
  "agent_session:peekStandingResolveHandler": "internal accessor/plumbing, not a user-facing control seam",
44
46
  "agent_session:setStandingResolveHandler": "internal accessor/plumbing, not a user-facing control seam",
@@ -60,11 +60,11 @@ function validateRowReceipts(rows: ManifestAdapterRow[]): void {
60
60
  }
61
61
  counts.set(row.adapter, (counts.get(row.adapter) ?? 0) + 1);
62
62
  }
63
- if (rows.length !== ADAPTERS.length * 91)
64
- throw new Error(`Expected ${ADAPTERS.length * 91} adapter rows; received ${rows.length}.`);
63
+ if (rows.length !== ADAPTERS.length * 93)
64
+ throw new Error(`Expected ${ADAPTERS.length * 93} adapter rows; received ${rows.length}.`);
65
65
  for (const adapter of ADAPTERS) {
66
- if (counts.get(adapter) !== 91)
67
- throw new Error(`Expected 91 ${adapter} rows; received ${counts.get(adapter) ?? 0}.`);
66
+ if (counts.get(adapter) !== 93)
67
+ throw new Error(`Expected 93 ${adapter} rows; received ${counts.get(adapter) ?? 0}.`);
68
68
  }
69
69
  process.stdout.write(
70
70
  `manifest check: ${rows.length} row receipts (${[...counts].map(([adapter, count]) => `${adapter}=${count}`).join(", ")})\n`,
package/src/cli/args.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  /**
2
- * CLI argument parsing and help display
2
+ * CLI argument parsing
3
3
  */
4
4
  import * as path from "node:path";
5
5
  import { type Effort, THINKING_EFFORTS } from "@gajae-code/ai";
6
- import { APP_NAME, CONFIG_DIR_NAME, logger } from "@gajae-code/utils";
6
+ import { logger } from "@gajae-code/utils";
7
7
  import { CliParseError } from "@gajae-code/utils/cli";
8
- import chalk from "chalk";
9
8
  import { parseEffort } from "../thinking";
10
9
  import { BUILTIN_TOOLS } from "../tools";
11
10
 
@@ -253,92 +252,3 @@ export function parseArgs(args: string[]): Args {
253
252
 
254
253
  return result;
255
254
  }
256
-
257
- export function getExtraHelpText(): string {
258
- return `${chalk.bold("Environment Variables:")}
259
- ${chalk.dim("# Core Providers")}
260
- ANTHROPIC_API_KEY - Anthropic Claude models
261
- ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth (takes precedence over API key)
262
- CLAUDE_CODE_USE_FOUNDRY - Enable Anthropic Foundry mode (uses Foundry endpoint + mTLS)
263
- FOUNDRY_BASE_URL - Anthropic Foundry base URL (e.g., https://<foundry-host>)
264
- ANTHROPIC_FOUNDRY_API_KEY - Anthropic token used as Authorization: Bearer <token> in Foundry mode
265
- ANTHROPIC_CUSTOM_HEADERS - Extra Foundry headers (e.g., "user-id: USERNAME")
266
- CLAUDE_CODE_CLIENT_CERT - Client certificate (PEM path or inline PEM) for mTLS
267
- CLAUDE_CODE_CLIENT_KEY - Client private key (PEM path or inline PEM) for mTLS
268
- NODE_EXTRA_CA_CERTS - CA bundle path (or inline PEM) for server certificate validation
269
- OPENAI_API_KEY - OpenAI GPT models
270
- GEMINI_API_KEY - Google Gemini models
271
- GITHUB_TOKEN - GitHub Copilot (or GH_TOKEN, COPILOT_GITHUB_TOKEN)
272
-
273
- ${chalk.dim("# Additional LLM Providers")}
274
- AZURE_OPENAI_API_KEY - Azure OpenAI models
275
- GROQ_API_KEY - Groq models
276
- CEREBRAS_API_KEY - Cerebras models
277
- XAI_API_KEY - xAI Grok models
278
- OPENROUTER_API_KEY - OpenRouter aggregated models
279
- KILO_API_KEY - Kilo Gateway models
280
- MISTRAL_API_KEY - Mistral models
281
- ZAI_API_KEY - z.ai models (ZhipuAI/GLM)
282
- MINIMAX_API_KEY - MiniMax models
283
- OPENCODE_API_KEY - OpenCode Zen/OpenCode Go models
284
- CURSOR_ACCESS_TOKEN - Cursor AI models
285
- AI_GATEWAY_API_KEY - Vercel AI Gateway
286
-
287
- ${chalk.dim("# Cloud Providers")}
288
- AWS_PROFILE - AWS Bedrock (or AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY)
289
- GOOGLE_CLOUD_PROJECT - Google Vertex AI (requires GOOGLE_CLOUD_LOCATION)
290
- GOOGLE_APPLICATION_CREDENTIALS - Service account for Vertex AI
291
-
292
- ${chalk.dim("# Search & Tools")}
293
- EXA_API_KEY - Exa web search
294
- BRAVE_API_KEY - Brave web search
295
- PERPLEXITY_API_KEY - Perplexity web search (API)
296
- PERPLEXITY_COOKIES - Perplexity web search (session cookie)
297
- TAVILY_API_KEY - Tavily web search
298
- ANTHROPIC_SEARCH_API_KEY - Anthropic search provider
299
-
300
- ${chalk.dim("# Configuration")}
301
- GJC_CODING_AGENT_DIR - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
302
- GJC_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)
303
- GJC_SMOL_MODEL - Override smol/fast model (see --smol)
304
- GJC_SLOW_MODEL - Override slow/reasoning model (see --slow)
305
- GJC_PLAN_MODEL - Override planning model (see --plan)
306
- GJC_NO_PTY - Disable PTY-based interactive bash execution
307
- --tmux - Launch interactive startup inside a fresh tmux session
308
- gjc session - List, inspect, create, remove, or attach tagged GJC-managed tmux sessions
309
- GJC_LAUNCH_POLICY - Launch policy for --tmux startup: tmux or direct
310
- GJC_TMUX_SESSION - Explicit tmux session name override for --tmux startup
311
- GJC_TMUX_PROFILE - Apply GJC tmux scroll/mouse/clipboard profile to --tmux sessions (set 0/off to skip)
312
- GJC_MOUSE - Mouse-wheel scroll in --tmux sessions (set 0/off to let the host terminal scroll)
313
-
314
- For complete environment variable reference, see:
315
- ${chalk.dim("docs/environment-variables.md")}
316
- ${chalk.bold("Available Tools (default-enabled unless noted):")}
317
- read - Read file contents
318
- bash - Execute bash commands
319
- edit - Edit files with find/replace
320
- write - Write files (creates/overwrites)
321
- grep - Search file contents
322
- find - Find files by glob pattern
323
- lsp - Language server protocol (code intelligence)
324
- python - Execute Python code (requires: ${APP_NAME} setup python)
325
- notebook - Edit Jupyter notebooks
326
- browser - Browser automation (Puppeteer)
327
- task - Launch sub-agents for parallel tasks
328
- todo_write - Manage todo/task lists
329
- web_search - Search the web
330
- ask - Ask user questions (interactive mode only)
331
-
332
- ${chalk.bold("Useful Commands:")}
333
- ${APP_NAME} --list-models - List configured provider models
334
- ${APP_NAME} --help - Show this help`;
335
- }
336
-
337
- export function printHelp(): void {
338
- process.stdout.write(
339
- `${chalk.bold(APP_NAME)} - red-claw AI coding assistant\n\n` +
340
- `Run ${APP_NAME} --help for full command and option details.\n` +
341
- `Run ${APP_NAME} <command> --help for command-specific help.\n\n` +
342
- `${getExtraHelpText()}\n`,
343
- );
344
- }
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { getMCPConfigPath, getProjectDir } from "@gajae-code/utils";
8
8
  import { getMCPServer, readMCPConfigFile, removeMCPServer, upsertMCPServer } from "../runtime-mcp/config-writer";
9
+ import { redactMCPEndpoint } from "../runtime-mcp/redaction";
9
10
  import type { MCPConfigFile, MCPServerConfig } from "../runtime-mcp/types";
10
11
 
11
12
  export type MCPAction = "add" | "list" | "remove";
@@ -141,24 +142,6 @@ function redactRecord(
141
142
  );
142
143
  }
143
144
 
144
- function redactUrl(value: string | undefined): string | undefined {
145
- if (!value) return value;
146
- try {
147
- const url = new URL(value);
148
- if (url.username) url.username = REDACTED;
149
- if (url.password) url.password = REDACTED;
150
- if (url.search) {
151
- for (const key of Array.from(url.searchParams.keys())) {
152
- url.searchParams.set(key, REDACTED);
153
- }
154
- }
155
- url.hash = "";
156
- return url.toString();
157
- } catch {
158
- return SENSITIVE_KEY_PATTERN.test(value) ? REDACTED : value;
159
- }
160
- }
161
-
162
145
  function redactArgs(args: string[] | undefined): string[] | undefined {
163
146
  if (!args) return undefined;
164
147
  const redacted: string[] = [];
@@ -196,7 +179,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
196
179
  if (headers) redacted.headers = headers;
197
180
  }
198
181
  if ("url" in redacted) {
199
- const url = redactUrl(redacted.url);
182
+ const url = redactMCPEndpoint(redacted.url);
200
183
  if (url) redacted.url = url;
201
184
  }
202
185
  if ("args" in redacted) {
@@ -207,7 +190,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
207
190
  redacted.auth = {
208
191
  type: redacted.auth.type,
209
192
  credentialId: redacted.auth.credentialId ? REDACTED : undefined,
210
- tokenUrl: redactUrl(redacted.auth.tokenUrl),
193
+ tokenUrl: redactMCPEndpoint(redacted.auth.tokenUrl),
211
194
  clientId: redacted.auth.clientId ? REDACTED : undefined,
212
195
  clientSecret: redacted.auth.clientSecret ? REDACTED : undefined,
213
196
  };
@@ -216,7 +199,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
216
199
  redacted.oauth = {
217
200
  clientId: redacted.oauth.clientId ? REDACTED : undefined,
218
201
  clientSecret: redacted.oauth.clientSecret ? REDACTED : undefined,
219
- redirectUri: redactUrl(redacted.oauth.redirectUri),
202
+ redirectUri: redactMCPEndpoint(redacted.oauth.redirectUri),
220
203
  callbackPort: redacted.oauth.callbackPort,
221
204
  callbackPath: redacted.oauth.callbackPath,
222
205
  };
@@ -7,7 +7,6 @@ import { createInterface } from "node:readline/promises";
7
7
  import { APP_NAME } from "@gajae-code/utils/dirs";
8
8
  import chalk from "chalk";
9
9
  import { Settings, type SettingsAtomicPatch } from "../config/settings";
10
- import { isProcessIncarnation, processIncarnation } from "../sdk/broker/process-incarnation";
11
10
  import { type EnsureChatDaemonResult, ensureDiscordDaemon, ensureSlackDaemon } from "../sdk/bus/chat-daemon-control";
12
11
  import { getNotificationConfig, maskToken, tokenFingerprint } from "../sdk/bus/config";
13
12
  import {
@@ -33,7 +32,7 @@ import {
33
32
  import {
34
33
  type EnsureTelegramDaemonDetailedResult,
35
34
  ensureTelegramDaemonRunningDetailed,
36
- readDaemonState,
35
+ resolveTelegramSetupPreflight,
37
36
  } from "../sdk/bus/telegram-daemon";
38
37
  import { runDaemonInternal } from "../sdk/bus/telegram-daemon-cli";
39
38
  import {
@@ -458,42 +457,10 @@ function proposedIdentityFromSetupPreflight(
458
457
 
459
458
  async function resolveSetupPreflight(settings: Settings, deps: NotifyCommandDeps): Promise<TelegramSetupPreflight> {
460
459
  if (deps.setupPreflight) return deps.setupPreflight;
461
- const cfg = getNotificationConfig(settings);
462
- try {
463
- const state = await readDaemonState(settings);
464
- if (!state) return { storedChatId: cfg.chatId };
465
- const validPid = Number.isSafeInteger(state.pid) && state.pid > 0;
466
- if (!validPid || !(deps.setupPidAlive ?? daemonPidAlive)(state.pid)) return { storedChatId: cfg.chatId };
467
- const persistedIncarnation = state.incarnation;
468
- const currentIncarnation = (deps.setupPidIncarnation ?? processIncarnation)(state.pid);
469
- if (
470
- !isProcessIncarnation(persistedIncarnation) ||
471
- !isProcessIncarnation(currentIncarnation) ||
472
- persistedIncarnation !== currentIncarnation
473
- )
474
- return { storedChatId: cfg.chatId };
475
- return {
476
- storedChatId: cfg.chatId,
477
- daemon: {
478
- live: true,
479
- tokenFingerprint: typeof state.tokenFingerprint === "string" ? state.tokenFingerprint : undefined,
480
- chatId: typeof state.chatId === "string" ? state.chatId : undefined,
481
- },
482
- };
483
- } catch {
484
- // A state read failure is not proof of a live daemon; proceed normally. The
485
- // daemon's own 409 handling remains the backstop against poller contention.
486
- return { storedChatId: cfg.chatId };
487
- }
488
- }
489
-
490
- function daemonPidAlive(pid: number): boolean {
491
- try {
492
- process.kill(pid, 0);
493
- return true;
494
- } catch (error) {
495
- return (error as NodeJS.ErrnoException).code === "EPERM";
496
- }
460
+ return await resolveTelegramSetupPreflight(settings, {
461
+ pidAlive: deps.setupPidAlive,
462
+ pidIncarnation: deps.setupPidIncarnation,
463
+ });
497
464
  }
498
465
 
499
466
  type TokenPromptInput = NodeJS.ReadStream & {
package/src/cli.ts CHANGED
@@ -8,6 +8,12 @@ import "@gajae-code/utils/postmortem";
8
8
  import { Args, type CliConfig, Command, type CommandEntry, Flags, run } from "@gajae-code/utils/cli";
9
9
  import { APP_NAME, formatBunRuntimeError, MIN_BUN_VERSION, VERSION } from "@gajae-code/utils/dirs";
10
10
  import { runFixtureReport } from "./cli/fixture-report";
11
+ import { admitManagedOwnerBeforeCli, completeManagedOwnerRecovery } from "./gjc-runtime/managed-owner-admission";
12
+ import {
13
+ isManagedOwnerSupervisorArgv,
14
+ MANAGED_OWNER_CHILD_TOKEN_ENV,
15
+ runManagedOwnerSupervisor,
16
+ } from "./gjc-runtime/managed-owner-supervisor";
11
17
  import { isTmuxOwnerIsolationCliArgv, runTmuxOwnerIsolationCliFromStdin } from "./gjc-runtime/tmux-owner-isolation-cli";
12
18
  import { smokeTestTabWorker } from "./tools/browser/tab-worker-smoke";
13
19
 
@@ -112,6 +118,36 @@ Options:
112
118
  `);
113
119
  }
114
120
 
121
+ export function interactiveBootstrapText(
122
+ argv: readonly string[],
123
+ stdinIsTTY = process.stdin.isTTY,
124
+ stdoutIsTTY = process.stdout.isTTY,
125
+ ): string | undefined {
126
+ if (!stdinIsTTY || !stdoutIsTTY || argv[0] !== "launch") return undefined;
127
+ for (let index = 1; index < argv.length; index++) {
128
+ const arg = argv[index];
129
+ if (
130
+ arg === "--print" ||
131
+ arg?.startsWith("--print=") ||
132
+ arg === "-p" ||
133
+ arg === "--export" ||
134
+ arg?.startsWith("--export=") ||
135
+ arg === "--list-models" ||
136
+ arg?.startsWith("--list-models=") ||
137
+ arg === "--mode" ||
138
+ arg?.startsWith("--mode=") ||
139
+ arg === "--help" ||
140
+ arg?.startsWith("--help=") ||
141
+ arg === "-h" ||
142
+ arg === "--version" ||
143
+ arg?.startsWith("--version=") ||
144
+ arg === "-v"
145
+ )
146
+ return undefined;
147
+ }
148
+ return "\u001b[?25h\u001b[38;5;45mGJC\u001b[0m warming workspace\r\n\r\n> ";
149
+ }
150
+
115
151
  function isNotifyDaemonInternalFastPath(argv: string[]): boolean {
116
152
  return argv[0] === "notify" && argv[1] === "daemon-internal";
117
153
  }
@@ -350,6 +386,18 @@ export async function runCli(argv: string[]): Promise<void> {
350
386
  await runTmuxOwnerIsolationCliFromStdin();
351
387
  return;
352
388
  }
389
+ if (isManagedOwnerSupervisorArgv(argv)) {
390
+ await runManagedOwnerSupervisor();
391
+ return;
392
+ }
393
+ if (process.env[MANAGED_OWNER_CHILD_TOKEN_ENV] !== undefined) {
394
+ const admission = await admitManagedOwnerBeforeCli();
395
+ if (admission.kind === "blocked") return;
396
+ if (admission.kind === "recovery") {
397
+ await completeManagedOwnerRecovery(admission.context);
398
+ return;
399
+ }
400
+ }
353
401
  if (isNotifyDaemonInternalFastPath(argv)) {
354
402
  await runNotifyDaemonInternalFastPath(argv);
355
403
  return;
@@ -394,6 +442,8 @@ export async function runCli(argv: string[]): Promise<void> {
394
442
  showStatsFastHelp();
395
443
  return;
396
444
  }
445
+ const bootstrap = interactiveBootstrapText(runArgv);
446
+ if (bootstrap) process.stdout.write(bootstrap);
397
447
  await installRuntimeGlobals();
398
448
  return run({ bin: APP_NAME, version: VERSION, argv: runArgv, commands, help: showHelp });
399
449
  }
@@ -2,7 +2,8 @@ import { Command, Flags } from "@gajae-code/utils/cli";
2
2
  import { runNativeDeepInterviewCommand } from "../gjc-runtime/deep-interview-runtime";
3
3
 
4
4
  export default class DeepInterview extends Command {
5
- static description = "Run native GJC deep-interview workflow";
5
+ static description =
6
+ "Run native GJC deep-interview workflow. Use `draft` for editable CLI-owned mutation inputs; JSON repair verbs are compatibility-only.";
6
7
  static strict = false;
7
8
  static flags = {
8
9
  quick: Flags.boolean({ description: "Seed a quick deep-interview run" }),
@@ -26,9 +27,13 @@ export default class DeepInterview extends Command {
26
27
  json: Flags.boolean({ description: "Output JSON" }),
27
28
  };
28
29
  static examples = [
29
- '$ gjc deep-interview --trace --standard "<idea>"',
30
- "$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md",
31
- "$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md --deliberate",
30
+ '$ gjc deep-interview --trace --standard "<idea>" --json',
31
+ "$ gjc deep-interview draft create --for initialize-context --session-id <id> --json",
32
+ "$ gjc deep-interview draft edit --draft-id <id> --expected-draft-revision 1 --op set --path /type --value greenfield --json",
33
+ "$ gjc deep-interview draft check --draft-id <id> --json",
34
+ "$ gjc deep-interview initialize-context --draft-id <id> --expected-draft-revision <n> --json",
35
+ "$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md --json",
36
+ "$ gjc deep-interview inspect --session-id <id> --selector summary --json",
32
37
  ];
33
38
 
34
39
  async run(): Promise<void> {
@@ -295,14 +295,14 @@ export async function runSessionHost(
295
295
  rollback.recordAbsent();
296
296
  const failure = normalizeSdkStartupFailure("registration", "failed", error);
297
297
  await writeFailure(failure, rollback.result);
298
- throw new Error(failure.message);
298
+ throw failure;
299
299
  }
300
300
  const { parsed } = opened;
301
301
  if ("failure" in created) {
302
302
  created.rollback.recordAbsent();
303
303
  await writeFailure(created.failure, created.rollback.result);
304
304
 
305
- throw new Error(created.failure.message);
305
+ throw created.failure;
306
306
  }
307
307
  const { session, capability, rollback } = created;
308
308
  let sessionDisposal: Promise<void> | undefined;
@@ -1,6 +1,6 @@
1
1
  import * as path from "node:path";
2
2
  import { createInterface } from "node:readline/promises";
3
- import { $env, getProjectDir, isEnoent, prompt } from "@gajae-code/utils";
3
+ import { $env, $pickenv, getProjectDir, isEnoent, prompt } from "@gajae-code/utils";
4
4
  import { applyChangelogProposals } from "../../commit/changelog";
5
5
  import { detectChangelogBoundaries } from "../../commit/changelog/detect";
6
6
  import { parseUnreleasedSection } from "../../commit/changelog/parse";
@@ -88,7 +88,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
88
88
  } else {
89
89
  process.stdout.write(" └─ (none found)\n");
90
90
  }
91
- const forceFallback = $env.PI_COMMIT_TEST_FALLBACK?.toLowerCase() === "true";
91
+ const forceFallback = $pickenv("GJC_COMMIT_TEST_FALLBACK", "PI_COMMIT_TEST_FALLBACK")?.toLowerCase() === "true";
92
92
  if (forceFallback) {
93
93
  process.stdout.write("● Forcing fallback commit generation...\n");
94
94
  const fallbackProposal = generateFallbackProposal(numstat);
@@ -152,7 +152,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
152
152
  }
153
153
 
154
154
  if (!usedFallback && !commitState.proposal && !commitState.splitProposal) {
155
- if ($env.PI_COMMIT_NO_FALLBACK?.toLowerCase() !== "true") {
155
+ if ($pickenv("GJC_COMMIT_NO_FALLBACK", "PI_COMMIT_NO_FALLBACK")?.toLowerCase() !== "true") {
156
156
  process.stdout.write("● Agent did not provide proposal, using fallback...\n");
157
157
  commitState.proposal = generateFallbackProposal(numstat);
158
158
  usedFallback = true;
@@ -1,6 +1,6 @@
1
1
  import type { ThinkingLevel } from "@gajae-code/agent-core";
2
2
  import type { Api, Model } from "@gajae-code/ai";
3
- import { $env } from "@gajae-code/utils";
3
+ import { $pickenv } from "@gajae-code/utils";
4
4
  import { parseFileDiffs } from "../../commit/git/diff";
5
5
  import type { ConventionalAnalysis } from "../../commit/types";
6
6
  import { isExcludedFile } from "../../commit/utils/exclusions";
@@ -34,7 +34,7 @@ export interface MapReduceInput {
34
34
  }
35
35
 
36
36
  export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings): boolean {
37
- if ($env.PI_COMMIT_MAP_REDUCE?.toLowerCase() === "false") return false;
37
+ if ($pickenv("GJC_COMMIT_MAP_REDUCE", "PI_COMMIT_MAP_REDUCE")?.toLowerCase() === "false") return false;
38
38
  if (settings?.enabled === false) return false;
39
39
  const minFiles = settings?.minFiles ?? MIN_FILES_FOR_MAP_REDUCE;
40
40
  const maxFileTokens = settings?.maxFileTokens ?? MAX_FILE_TOKENS;