@gajae-code/coding-agent 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/types/append-only-mode.d.ts +31 -0
  3. package/dist/types/capability/rule.d.ts +8 -0
  4. package/dist/types/cli/completion-cli.d.ts +41 -0
  5. package/dist/types/cli/config-cli.d.ts +1 -0
  6. package/dist/types/cli/fixture-report.d.ts +20 -0
  7. package/dist/types/cli/fixture-report.test.d.ts +1 -0
  8. package/dist/types/cli/nofile-limit.d.ts +11 -0
  9. package/dist/types/cli/notify-cli.d.ts +7 -0
  10. package/dist/types/cli/update-cli.d.ts +22 -0
  11. package/dist/types/cli.d.ts +124 -1
  12. package/dist/types/commands/completion.d.ts +29 -0
  13. package/dist/types/commands/config.d.ts +3 -0
  14. package/dist/types/commands/deep-interview.d.ts +3 -0
  15. package/dist/types/config/file-lock.d.ts +7 -6
  16. package/dist/types/config/keybindings.d.ts +2 -2
  17. package/dist/types/config/model-profile-activation.d.ts +6 -0
  18. package/dist/types/config/settings-schema.d.ts +105 -1
  19. package/dist/types/config/settings.d.ts +8 -0
  20. package/dist/types/discovery/index.d.ts +1 -0
  21. package/dist/types/export/html/template.generated.d.ts +1 -1
  22. package/dist/types/export/ttsr.d.ts +14 -3
  23. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  24. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  25. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +39 -0
  26. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  27. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -1
  28. package/dist/types/gjc-runtime/deep-interview-state.d.ts +17 -2
  29. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +7 -0
  30. package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
  31. package/dist/types/gjc-runtime/team-runtime.d.ts +28 -1
  32. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
  33. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +120 -13
  34. package/dist/types/goals/runtime.d.ts +1 -0
  35. package/dist/types/hooks/native-skill-hook.d.ts +12 -0
  36. package/dist/types/main.d.ts +4 -1
  37. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  38. package/dist/types/modes/components/model-selector.d.ts +4 -0
  39. package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
  40. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  41. package/dist/types/modes/components/status-line.d.ts +9 -0
  42. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  43. package/dist/types/modes/controllers/event-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/input-controller.d.ts +1 -3
  45. package/dist/types/modes/interactive-mode.d.ts +2 -0
  46. package/dist/types/modes/print-mode.d.ts +13 -1
  47. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
  48. package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
  49. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
  50. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
  51. package/dist/types/modes/types.d.ts +1 -0
  52. package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
  53. package/dist/types/notifications/config.d.ts +14 -1
  54. package/dist/types/notifications/daemon-paths.d.ts +1 -0
  55. package/dist/types/notifications/index.d.ts +6 -3
  56. package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
  57. package/dist/types/notifications/telegram-daemon.d.ts +23 -3
  58. package/dist/types/notifications/telegram-reference.d.ts +0 -5
  59. package/dist/types/notifications/threaded-render.d.ts +6 -0
  60. package/dist/types/runtime-api-key.d.ts +7 -0
  61. package/dist/types/sdk.d.ts +3 -0
  62. package/dist/types/session/agent-session.d.ts +53 -1
  63. package/dist/types/session/agent-storage.d.ts +1 -0
  64. package/dist/types/session/file-mention-pruning.d.ts +8 -0
  65. package/dist/types/session/messages.d.ts +4 -0
  66. package/dist/types/session/session-manager.d.ts +17 -1
  67. package/dist/types/skill-state/active-state.d.ts +18 -1
  68. package/dist/types/system-prompt.d.ts +18 -2
  69. package/dist/types/task/executor.d.ts +3 -0
  70. package/dist/types/task/fork-context-budget.d.ts +7 -0
  71. package/dist/types/task/token-log.d.ts +30 -0
  72. package/dist/types/task/token-log.test.d.ts +1 -0
  73. package/dist/types/tools/bisect.d.ts +81 -0
  74. package/dist/types/tools/computer.d.ts +2 -2
  75. package/dist/types/tools/index.d.ts +1 -0
  76. package/dist/types/tools/search.d.ts +2 -2
  77. package/dist/types/utils/file-mentions.d.ts +14 -0
  78. package/dist/types/utils/git.d.ts +32 -0
  79. package/dist/types/web/scrapers/youtube.d.ts +8 -0
  80. package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
  81. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
  82. package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
  83. package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
  84. package/package.json +7 -7
  85. package/src/append-only-mode.ts +58 -0
  86. package/src/autoresearch/prompt.md +1 -1
  87. package/src/autoresearch/tools/log-experiment.ts +1 -1
  88. package/src/capability/rule.ts +9 -0
  89. package/src/cli/completion-cli.ts +234 -0
  90. package/src/cli/config-cli.ts +61 -9
  91. package/src/cli/fast-help.ts +1 -0
  92. package/src/cli/fixture-report.test.ts +230 -0
  93. package/src/cli/fixture-report.ts +171 -0
  94. package/src/cli/nofile-limit.ts +48 -0
  95. package/src/cli/notify-cli.ts +91 -7
  96. package/src/cli/update-cli.ts +85 -9
  97. package/src/cli.ts +29 -1
  98. package/src/commands/completion.ts +84 -0
  99. package/src/commands/config.ts +2 -0
  100. package/src/commands/contribution-prep.ts +1 -1
  101. package/src/commands/deep-interview.ts +2 -1
  102. package/src/config/file-lock.ts +73 -34
  103. package/src/config/keybindings.ts +2 -2
  104. package/src/config/model-profile-activation.ts +65 -0
  105. package/src/config/model-registry.ts +14 -2
  106. package/src/config/settings-schema.ts +68 -1
  107. package/src/config/settings.ts +22 -0
  108. package/src/coordinator-mcp/server.ts +34 -8
  109. package/src/defaults/gjc/skills/deep-interview/SKILL.md +50 -18
  110. package/src/defaults/gjc/skills/ralplan/SKILL.md +40 -99
  111. package/src/defaults/gjc/skills/team/SKILL.md +17 -14
  112. package/src/defaults/gjc/skills/ultragoal/SKILL.md +59 -4
  113. package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
  114. package/src/defaults/gjc-defaults.ts +9 -0
  115. package/src/discovery/helpers.ts +10 -0
  116. package/src/discovery/index.ts +1 -0
  117. package/src/export/html/template.generated.ts +1 -1
  118. package/src/export/html/template.js +60 -2
  119. package/src/export/ttsr.ts +96 -18
  120. package/src/extensibility/extensions/runner.ts +183 -219
  121. package/src/extensibility/extensions/types.ts +9 -0
  122. package/src/extensibility/gjc-plugins/runtime-adapters.ts +156 -44
  123. package/src/gjc-runtime/deep-interview-ambiguity.ts +206 -0
  124. package/src/gjc-runtime/deep-interview-recorder.ts +62 -8
  125. package/src/gjc-runtime/deep-interview-runtime.ts +210 -2
  126. package/src/gjc-runtime/deep-interview-state.ts +44 -2
  127. package/src/gjc-runtime/session-state-sidecar.ts +263 -20
  128. package/src/gjc-runtime/state-runtime.ts +12 -2
  129. package/src/gjc-runtime/state-writer.ts +15 -0
  130. package/src/gjc-runtime/team-runtime.ts +187 -15
  131. package/src/gjc-runtime/ultragoal-guard.ts +55 -66
  132. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +315 -0
  133. package/src/gjc-runtime/ultragoal-runtime.ts +1663 -159
  134. package/src/gjc-runtime/workflow-manifest.generated.json +113 -2
  135. package/src/gjc-runtime/workflow-manifest.ts +74 -1
  136. package/src/goals/runtime.ts +10 -2
  137. package/src/hindsight/backend.ts +1 -3
  138. package/src/hooks/native-skill-hook.ts +124 -4
  139. package/src/internal-urls/docs-index.generated.ts +16 -7
  140. package/src/main.ts +95 -43
  141. package/src/memories/index.ts +12 -1
  142. package/src/modes/components/assistant-message.ts +241 -82
  143. package/src/modes/components/custom-editor.ts +1 -0
  144. package/src/modes/components/model-selector.ts +54 -11
  145. package/src/modes/components/queued-message-selector.ts +92 -0
  146. package/src/modes/components/settings-selector.ts +4 -0
  147. package/src/modes/components/status-line.ts +167 -54
  148. package/src/modes/components/tool-execution.ts +44 -19
  149. package/src/modes/components/welcome.ts +160 -44
  150. package/src/modes/controllers/command-controller.ts +40 -4
  151. package/src/modes/controllers/event-controller.ts +66 -47
  152. package/src/modes/controllers/extension-ui-controller.ts +4 -10
  153. package/src/modes/controllers/input-controller.ts +180 -18
  154. package/src/modes/controllers/selector-controller.ts +122 -27
  155. package/src/modes/interactive-mode.ts +51 -16
  156. package/src/modes/print-mode.ts +43 -4
  157. package/src/modes/prompt-action-autocomplete.ts +21 -20
  158. package/src/modes/rpc/rpc-mode.ts +29 -3
  159. package/src/modes/rpc/rpc-types.ts +9 -0
  160. package/src/modes/shared/agent-wire/command-validation.ts +4 -0
  161. package/src/modes/shared/agent-wire/event-contract.ts +22 -0
  162. package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
  163. package/src/modes/shared/agent-wire/scopes.ts +1 -0
  164. package/src/modes/types.ts +1 -0
  165. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  166. package/src/modes/utils/ui-helpers.ts +19 -6
  167. package/src/notifications/config.ts +25 -5
  168. package/src/notifications/daemon-paths.ts +2 -0
  169. package/src/notifications/index.ts +117 -14
  170. package/src/notifications/lifecycle-commands.ts +37 -7
  171. package/src/notifications/lifecycle-orchestrator.ts +19 -6
  172. package/src/notifications/telegram-daemon-cli.ts +2 -2
  173. package/src/notifications/telegram-daemon-control.ts +5 -3
  174. package/src/notifications/telegram-daemon.ts +302 -51
  175. package/src/notifications/telegram-reference.ts +0 -16
  176. package/src/notifications/threaded-render.ts +17 -1
  177. package/src/prompts/agents/architect.md +19 -10
  178. package/src/prompts/agents/critic.md +16 -18
  179. package/src/prompts/agents/executor.md +14 -11
  180. package/src/prompts/agents/planner.md +18 -13
  181. package/src/prompts/ci-green-request.md +3 -8
  182. package/src/prompts/goals/goal-continuation.md +0 -5
  183. package/src/prompts/goals/goal-mode-active.md +0 -5
  184. package/src/prompts/system/agent-creation-architect.md +2 -2
  185. package/src/prompts/system/plan-mode-active.md +1 -1
  186. package/src/prompts/system/plan-mode-subagent.md +3 -3
  187. package/src/prompts/system/project-prompt.md +0 -13
  188. package/src/prompts/system/subagent-system-prompt.md +3 -2
  189. package/src/prompts/system/system-prompt.md +29 -19
  190. package/src/prompts/system/volatile-project-context.md +10 -0
  191. package/src/prompts/system/web-search.md +4 -7
  192. package/src/prompts/tools/apply-patch.md +6 -0
  193. package/src/prompts/tools/ast-edit.md +1 -0
  194. package/src/prompts/tools/bash.md +1 -1
  195. package/src/prompts/tools/bisect.md +22 -0
  196. package/src/prompts/tools/browser.md +1 -1
  197. package/src/prompts/tools/computer.md +2 -2
  198. package/src/prompts/tools/cron.md +0 -2
  199. package/src/prompts/tools/eval.md +2 -2
  200. package/src/prompts/tools/find.md +1 -1
  201. package/src/prompts/tools/github.md +6 -6
  202. package/src/prompts/tools/goal.md +1 -3
  203. package/src/prompts/tools/image-gen.md +12 -5
  204. package/src/prompts/tools/irc.md +5 -4
  205. package/src/prompts/tools/job.md +2 -2
  206. package/src/prompts/tools/lsp.md +1 -1
  207. package/src/prompts/tools/monitor.md +2 -2
  208. package/src/prompts/tools/patch.md +2 -2
  209. package/src/prompts/tools/replace.md +2 -21
  210. package/src/prompts/tools/rewind.md +1 -1
  211. package/src/prompts/tools/search-tool-bm25.md +1 -11
  212. package/src/prompts/tools/search.md +1 -1
  213. package/src/prompts/tools/skill.md +3 -2
  214. package/src/prompts/tools/ssh.md +2 -1
  215. package/src/prompts/tools/subagent.md +2 -2
  216. package/src/prompts/tools/task.md +4 -5
  217. package/src/prompts/tools/todo-write.md +1 -1
  218. package/src/prompts/tools/vim.md +1 -0
  219. package/src/prompts/tools/web-search.md +2 -0
  220. package/src/prompts/tools/write.md +8 -2
  221. package/src/runtime-api-key.ts +16 -0
  222. package/src/sdk.ts +52 -28
  223. package/src/session/agent-session.ts +951 -132
  224. package/src/session/agent-storage.ts +4 -0
  225. package/src/session/file-mention-pruning.ts +64 -0
  226. package/src/session/messages.ts +4 -0
  227. package/src/session/session-manager.ts +103 -4
  228. package/src/skill-state/active-state.ts +141 -5
  229. package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
  230. package/src/slash-commands/builtin-registry.ts +218 -71
  231. package/src/system-prompt.ts +57 -16
  232. package/src/task/executor.ts +132 -24
  233. package/src/task/fork-context-advisory.ts +2 -7
  234. package/src/task/fork-context-budget.ts +9 -0
  235. package/src/task/index.ts +13 -6
  236. package/src/task/output-manager.ts +20 -8
  237. package/src/task/receipt.ts +26 -7
  238. package/src/task/token-log.test.ts +188 -0
  239. package/src/task/token-log.ts +179 -0
  240. package/src/tools/bisect.ts +367 -0
  241. package/src/tools/computer.ts +246 -43
  242. package/src/tools/index.ts +3 -0
  243. package/src/tools/output-meta.ts +116 -4
  244. package/src/tools/search.ts +8 -2
  245. package/src/tools/skill.ts +15 -0
  246. package/src/tools/telegram-send.ts +6 -0
  247. package/src/utils/file-mentions.ts +39 -7
  248. package/src/utils/git.ts +73 -0
  249. package/src/web/scrapers/youtube.ts +24 -12
  250. package/src/workflow/workflow-intent-diff.ts +29 -6
  251. package/src/workflow/workflow-intent-report-consumer.ts +221 -0
  252. package/src/workflow/workflow-intent-report-schema.ts +129 -0
  253. package/src/workflow/workflow-intent-report.ts +135 -0
@@ -6,12 +6,13 @@ import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
6
6
  import { Spacer, Text } from "@gajae-code/tui";
7
7
  import { setProjectDir } from "@gajae-code/utils";
8
8
  import { jobElapsedMs } from "../async";
9
- import { materializeActiveModelProfileAssignment } from "../config/model-profile-activation";
9
+ import { materializeActiveModelProfileAssignments } from "../config/model-profile-activation";
10
10
  import {
11
11
  GJC_MODEL_ASSIGNMENT_TARGET_IDS,
12
12
  GJC_MODEL_ASSIGNMENT_TARGETS,
13
13
  type GjcModelAssignmentTargetId,
14
14
  } from "../config/model-registry";
15
+
15
16
  import {
16
17
  extractExplicitThinkingSelector,
17
18
  formatModelSelectorValue,
@@ -31,6 +32,7 @@ import {
31
32
  parseProviderCompatibility,
32
33
  } from "../setup/provider-onboarding";
33
34
  import { parseThinkingLevel } from "../thinking";
35
+ import { getDisplayChangelogEntries } from "../utils/changelog";
34
36
  import { buildContextReportText } from "./helpers/context-report";
35
37
  import { buildFastStatusReport } from "./helpers/fast-status-report";
36
38
  import { formatDuration } from "./helpers/format";
@@ -51,6 +53,13 @@ export type { BuiltinSlashCommand, SubcommandDef } from "./types";
51
53
  /** TUI-specific runtime accepted by `executeBuiltinSlashCommand`. */
52
54
  export type BuiltinSlashCommandRuntime = TuiSlashCommandRuntime;
53
55
 
56
+ type GjcModelBatchAssignmentTargetId = "all-role-agents" | "all-targets";
57
+ type ParsedModelCommandArgs =
58
+ | { kind: "summary" }
59
+ | { kind: "assign"; targetId: GjcModelAssignmentTargetId | GjcModelBatchAssignmentTargetId; selector: string };
60
+
61
+ const GJC_MODEL_ROLE_AGENT_TARGET_IDS: GjcModelAssignmentTargetId[] = ["executor", "architect", "planner", "critic"];
62
+
54
63
  function fastStatusRoleTargets(): Array<{ id: GjcModelAssignmentTargetId; label: string; isSubagentRole: boolean }> {
55
64
  return GJC_MODEL_ASSIGNMENT_TARGET_IDS.map(id => ({
56
65
  id,
@@ -59,6 +68,32 @@ function fastStatusRoleTargets(): Array<{ id: GjcModelAssignmentTargetId; label:
59
68
  }));
60
69
  }
61
70
 
71
+ function toSlashCommandRuntime(runtime: TuiSlashCommandRuntime): SlashCommandRuntime {
72
+ const ctx = runtime.ctx;
73
+ return {
74
+ session: ctx.session,
75
+ sessionManager: ctx.sessionManager,
76
+ settings: ctx.settings,
77
+ cwd: ctx.sessionManager.getCwd(),
78
+ output: (text: string) => {
79
+ ctx.showStatus(text);
80
+ },
81
+ refreshCommands: () => ctx.refreshSlashCommandState(),
82
+ reloadPlugins: async () => {
83
+ const projectPath = await resolveActiveProjectRegistryPath(ctx.sessionManager.getCwd());
84
+ clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
85
+ await ctx.refreshSlashCommandState();
86
+ await ctx.session.refreshSshTool({ activateIfAvailable: true });
87
+ },
88
+ notifyTitleChanged: () => {
89
+ ctx.statusLine.invalidate();
90
+ ctx.updateEditorBorderColor();
91
+ ctx.ui.requestRender();
92
+ },
93
+ notifyConfigChanged: () => ctx.notifyConfigChanged?.(),
94
+ };
95
+ }
96
+
62
97
  function parseProviderSetupSlashArgs(args: string): {
63
98
  preset?: string;
64
99
  compat?: string;
@@ -146,22 +181,37 @@ function formatModelAssignmentSummary(runtime: SlashCommandRuntime): string {
146
181
  return lines.join("\n");
147
182
  }
148
183
 
149
- function parseModelCommandArgs(args: string): { targetId: GjcModelAssignmentTargetId; selector: string } {
184
+ function parseModelCommandArgs(args: string): ParsedModelCommandArgs {
150
185
  const tokens = args.trim().split(/\s+/).filter(Boolean);
151
186
  const first = tokens[0]?.toLowerCase();
152
- const explicitTarget = GJC_MODEL_ASSIGNMENT_TARGET_IDS.includes(first as GjcModelAssignmentTargetId)
153
- ? (first as GjcModelAssignmentTargetId)
154
- : undefined;
187
+ if (first === "roles" || first === "assignments") return { kind: "summary" };
188
+
189
+ const parseTarget = (
190
+ token: string | undefined,
191
+ ): GjcModelAssignmentTargetId | GjcModelBatchAssignmentTargetId | undefined => {
192
+ const normalized = token?.toLowerCase();
193
+ if (GJC_MODEL_ASSIGNMENT_TARGET_IDS.includes(normalized as GjcModelAssignmentTargetId)) {
194
+ return normalized as GjcModelAssignmentTargetId;
195
+ }
196
+ if (normalized === "all-role-agents" || normalized === "all-targets") return normalized;
197
+ return undefined;
198
+ };
199
+
200
+ if (first === "assign") {
201
+ const targetId = parseTarget(tokens[1]);
202
+ if (targetId) return { kind: "assign", targetId, selector: tokens.slice(2).join(" ") };
203
+ return { kind: "assign", targetId: "default", selector: tokens.slice(1).join(" ") };
204
+ }
205
+
206
+ const explicitTarget = parseTarget(first);
155
207
  if (explicitTarget) {
156
- return { targetId: explicitTarget, selector: tokens.slice(1).join(" ") };
208
+ return { kind: "assign", targetId: explicitTarget, selector: tokens.slice(1).join(" ") };
157
209
  }
158
210
  if (first === "set") {
159
- const second = tokens[1]?.toLowerCase();
160
- if (GJC_MODEL_ASSIGNMENT_TARGET_IDS.includes(second as GjcModelAssignmentTargetId)) {
161
- return { targetId: second as GjcModelAssignmentTargetId, selector: tokens.slice(2).join(" ") };
162
- }
211
+ const targetId = parseTarget(tokens[1]);
212
+ if (targetId) return { kind: "assign", targetId, selector: tokens.slice(2).join(" ") };
163
213
  }
164
- return { targetId: "default", selector: args.trim() };
214
+ return { kind: "assign", targetId: "default", selector: args.trim() };
165
215
  }
166
216
 
167
217
  function splitExplicitThinkingSelector(selector: string): { baseSelector: string; thinkingLevel?: ThinkingLevel } {
@@ -296,11 +346,33 @@ async function resolveModelCommandSelection(
296
346
  };
297
347
  }
298
348
 
349
+ function getModelAssignmentTargetIds(
350
+ targetId: GjcModelAssignmentTargetId | GjcModelBatchAssignmentTargetId,
351
+ ): GjcModelAssignmentTargetId[] {
352
+ if (targetId === "all-role-agents") return [...GJC_MODEL_ROLE_AGENT_TARGET_IDS];
353
+ if (targetId === "all-targets") return [...GJC_MODEL_ASSIGNMENT_TARGET_IDS];
354
+ return [targetId];
355
+ }
356
+
357
+ function formatModelAssignmentSuccess(
358
+ targetId: GjcModelAssignmentTargetId | GjcModelBatchAssignmentTargetId,
359
+ selector: string,
360
+ ): string {
361
+ if (targetId === "all-role-agents") {
362
+ return `Role-agent models set to ${selector} for EXECUTOR, ARCHITECT, PLANNER, CRITIC.`;
363
+ }
364
+ if (targetId === "all-targets") {
365
+ return `All model targets set to ${selector} for DEFAULT, EXECUTOR, ARCHITECT, PLANNER, CRITIC.`;
366
+ }
367
+ if (targetId === "default") return `Default model set to ${selector}.`;
368
+ return `${targetId} agent model set to ${selector}.`;
369
+ }
370
+
299
371
  function modelSelectionUsage(runtime: SlashCommandRuntime, currentModelLine?: string): string {
300
372
  return [
301
373
  currentModelLine,
302
374
  formatModelAssignmentSummary(runtime),
303
- "ACP/text mode: use /model <model> for DEFAULT, or /model <target> <model> for EXECUTOR, ARCHITECT, PLANNER, or CRITIC.",
375
+ "Use /model <model> for DEFAULT, or /model <target> <model[:effort]> for EXECUTOR, ARCHITECT, PLANNER, or CRITIC.",
304
376
  formatModelOnboardingGuidance(),
305
377
  ]
306
378
  .filter((line): line is string => Boolean(line))
@@ -313,6 +385,30 @@ function refreshStatusLine(ctx: InteractiveModeContext): void {
313
385
  ctx.ui.requestRender();
314
386
  }
315
387
 
388
+ type ChangelogCommandArgs = { showFull: boolean } | { error: string };
389
+
390
+ function parseChangelogCommandArgs(args: string): ChangelogCommandArgs {
391
+ const normalized = args.trim().toLowerCase();
392
+ if (!normalized) return { showFull: false };
393
+ if (normalized === "full" || normalized === "--full") return { showFull: true };
394
+ return { error: "Usage: /changelog [full|--full]" };
395
+ }
396
+
397
+ function buildChangelogCommandOutput(showFull: boolean): string {
398
+ const allEntries = getDisplayChangelogEntries();
399
+ const entriesToShow = showFull ? allEntries : allEntries.slice(0, 3);
400
+ const changelogMarkdown =
401
+ entriesToShow.length > 0
402
+ ? [...entriesToShow]
403
+ .reverse()
404
+ .map(entry => entry.content)
405
+ .join("\n\n")
406
+ : "No changelog entries found.";
407
+ const title = showFull ? "Full Changelog" : "Recent Changes";
408
+ const hint = showFull ? "" : "\n\nUse `/changelog --full` to view the complete changelog.";
409
+ return `${title}\n\n${changelogMarkdown}${hint}`;
410
+ }
411
+
316
412
  const shutdownHandlerTui = (_command: ParsedSlashCommand, runtime: TuiSlashCommandRuntime): SlashCommandResult => {
317
413
  runtime.ctx.editor.setText("");
318
414
  void runtime.ctx.shutdown();
@@ -372,9 +468,16 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
372
468
  acpDescription: "Show current model selection",
373
469
  inlineHint: "[target] <model>",
374
470
  acpInputHint: "[target] <model>",
471
+ allowArgs: true,
375
472
  handle: async (command, runtime) => {
376
473
  if (command.args) {
377
474
  const parsedArgs = parseModelCommandArgs(command.args);
475
+ if (parsedArgs.kind === "summary") {
476
+ await runtime.output(formatModelAssignmentSummary(runtime));
477
+ return commandConsumed();
478
+ }
479
+
480
+ const targetIds = getModelAssignmentTargetIds(parsedArgs.targetId);
378
481
  const modelId = parsedArgs.selector;
379
482
  if (!modelId) {
380
483
  return usage(
@@ -388,24 +491,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
388
491
  }
389
492
  const { selection } = resolution;
390
493
  try {
391
- const persistedSelector = formatModelSelectorValue(selection.selector, selection.thinkingLevel);
392
- if (parsedArgs.targetId === "default") {
393
- await runtime.session.setModel(selection.model, "default", {
394
- selector: selection.selector,
395
- thinkingLevel: selection.thinkingLevel,
396
- });
397
- materializeActiveModelProfileAssignment({
398
- session: runtime.session,
399
- settings: runtime.settings,
400
- role: parsedArgs.targetId,
401
- selector: persistedSelector,
402
- });
403
- if (selection.thinkingLevel) {
404
- runtime.session.setThinkingLevel(selection.thinkingLevel);
405
- }
406
- await runtime.output(`Default model set to ${persistedSelector}.`);
407
- await runtime.notifyTitleChanged?.();
408
- } else {
494
+ const includesDefault = targetIds.includes("default");
495
+ const includesRoleAgent = targetIds.some(role => role !== "default");
496
+ if (includesRoleAgent) {
409
497
  const apiKey = await runtime.session.modelRegistry.getApiKey(
410
498
  selection.model,
411
499
  runtime.session.sessionId,
@@ -413,31 +501,60 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
413
501
  if (!apiKey) {
414
502
  throw new Error(`No API key for ${selection.model.provider}/${selection.model.id}`);
415
503
  }
416
- const overrides = runtime.settings.get("task.agentModelOverrides");
504
+ }
505
+
506
+ const overrides = runtime.settings.get("task.agentModelOverrides");
507
+ const assignments = new Map<GjcModelAssignmentTargetId, string>();
508
+ const existingDefaultThinkingLevel =
509
+ selection.thinkingLevel !== undefined
510
+ ? selection.thinkingLevel
511
+ : runtime.session.getActiveModelProfile?.()
512
+ ? undefined
513
+ : extractExplicitThinkingSelector(runtime.settings.getModelRole("default"), runtime.settings);
514
+ const persistedSelector = formatModelSelectorValue(selection.selector, existingDefaultThinkingLevel);
515
+ for (const targetId of targetIds) {
516
+ if (targetId === "default") {
517
+ assignments.set(targetId, persistedSelector);
518
+ continue;
519
+ }
417
520
  const thinkingLevel =
418
- selection.thinkingLevel ??
419
- extractExplicitThinkingSelector(overrides[parsedArgs.targetId], runtime.settings);
420
- const roleSelector = formatModelSelectorValue(selection.selector, thinkingLevel);
421
- const materializedProfile = materializeActiveModelProfileAssignment({
422
- session: runtime.session,
423
- settings: runtime.settings,
424
- role: parsedArgs.targetId,
425
- selector: roleSelector,
521
+ selection.thinkingLevel ?? extractExplicitThinkingSelector(overrides[targetId], runtime.settings);
522
+ assignments.set(targetId, formatModelSelectorValue(selection.selector, thinkingLevel));
523
+ }
524
+
525
+ if (includesDefault) {
526
+ await runtime.session.setModel(selection.model, "default", {
527
+ selector: selection.selector,
528
+ thinkingLevel: existingDefaultThinkingLevel,
426
529
  });
427
- if (!materializedProfile) {
428
- const target = GJC_MODEL_ASSIGNMENT_TARGETS[parsedArgs.targetId];
530
+ if (existingDefaultThinkingLevel) {
531
+ runtime.session.setThinkingLevel(existingDefaultThinkingLevel);
532
+ }
533
+ }
534
+
535
+ const materializedProfile = materializeActiveModelProfileAssignments({
536
+ session: runtime.session,
537
+ settings: runtime.settings,
538
+ assignments,
539
+ });
540
+ if (!materializedProfile) {
541
+ for (const [targetId, selector] of assignments) {
542
+ const target = GJC_MODEL_ASSIGNMENT_TARGETS[targetId];
429
543
  if (target.settingsPath === "modelRoles") {
430
- runtime.settings.setModelRole(parsedArgs.targetId, roleSelector);
544
+ runtime.settings.setModelRole(targetId, selector);
431
545
  } else {
432
- runtime.settings.set("task.agentModelOverrides", {
433
- ...overrides,
434
- [parsedArgs.targetId]: roleSelector,
435
- });
546
+ runtime.settings.setAgentModelOverride(targetId, selector);
436
547
  }
437
548
  }
438
- runtime.settings.getStorage()?.recordModelUsage(`${selection.model.provider}/${selection.model.id}`);
439
- await runtime.output(`${parsedArgs.targetId} agent model set to ${roleSelector}.`);
440
549
  }
550
+ runtime.settings.getStorage()?.recordModelUsage(`${selection.model.provider}/${selection.model.id}`);
551
+ await runtime.output(
552
+ formatModelAssignmentSuccess(
553
+ parsedArgs.targetId,
554
+ assignments.get(targetIds[0] ?? "default") ?? persistedSelector,
555
+ ),
556
+ );
557
+ if (includesDefault) await runtime.notifyTitleChanged?.();
441
558
  await runtime.notifyConfigChanged?.();
442
559
  return commandConsumed();
443
560
  } catch (err) {
@@ -454,7 +571,18 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
454
571
  );
455
572
  return commandConsumed();
456
573
  },
457
- handleTui: (_command, runtime) => {
574
+ handleTui: async (command, runtime) => {
575
+ if (command.args.trim()) {
576
+ const result = await BUILTIN_SLASH_COMMAND_LOOKUP.get(command.name)?.handle?.(
577
+ command,
578
+ toSlashCommandRuntime(runtime),
579
+ );
580
+ runtime.ctx.statusLine.invalidate();
581
+ runtime.ctx.updateEditorBorderColor();
582
+ runtime.ctx.editor.setText("");
583
+ runtime.ctx.ui.requestRender();
584
+ return result;
585
+ }
458
586
  runtime.ctx.showModelSelector();
459
587
  runtime.ctx.editor.setText("");
460
588
  },
@@ -719,6 +847,29 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
719
847
  runtime.ctx.editor.setText("");
720
848
  },
721
849
  },
850
+ {
851
+ name: "changelog",
852
+ description: "Show release notes and changelog entries",
853
+ inlineHint: "[full|--full]",
854
+ subcommands: [{ name: "full", description: "Show complete changelog" }],
855
+ allowArgs: true,
856
+ handle: async (command, runtime) => {
857
+ const parsed = parseChangelogCommandArgs(command.args);
858
+ if ("error" in parsed) return usage(parsed.error, runtime);
859
+ await runtime.output(buildChangelogCommandOutput(parsed.showFull));
860
+ return commandConsumed();
861
+ },
862
+ handleTui: async (command, runtime) => {
863
+ const parsed = parseChangelogCommandArgs(command.args);
864
+ if ("error" in parsed) {
865
+ runtime.ctx.showError(parsed.error);
866
+ runtime.ctx.editor.setText("");
867
+ return;
868
+ }
869
+ await runtime.ctx.handleChangelogCommand(parsed.showFull);
870
+ runtime.ctx.editor.setText("");
871
+ },
872
+ },
722
873
  {
723
874
  name: "help",
724
875
  priority: 100,
@@ -966,6 +1117,22 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
966
1117
  await runtime.ctx.handleSSHCommand(command.text);
967
1118
  },
968
1119
  },
1120
+ {
1121
+ name: "clear",
1122
+ priority: 97,
1123
+ description: "Clear context while preserving this session ID",
1124
+ acpDescription: "Clear context while preserving this session ID",
1125
+ handle: async (_command, runtime) => {
1126
+ const beforeSessionId = runtime.session.sessionId;
1127
+ await runtime.session.clearContext();
1128
+ await runtime.output(`Context cleared. Session preserved: ${beforeSessionId}`);
1129
+ return commandConsumed();
1130
+ },
1131
+ handleTui: async (_command, runtime) => {
1132
+ runtime.ctx.editor.setText("");
1133
+ await runtime.ctx.handleContextClearCommand();
1134
+ },
1135
+ },
969
1136
  {
970
1137
  name: "new",
971
1138
  priority: 96,
@@ -1226,6 +1393,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1226
1393
  },
1227
1394
  {
1228
1395
  name: "exit",
1396
+ aliases: ["quit"],
1229
1397
  description: "Exit the application",
1230
1398
  handleTui: shutdownHandlerTui,
1231
1399
  },
@@ -1303,29 +1471,8 @@ export async function executeBuiltinSlashCommand(
1303
1471
  return true;
1304
1472
  }
1305
1473
  if (command.handle) {
1306
- // No TUI-specific override → adapt the ACP/text-mode `handle` to the
1307
- // TUI by routing `runtime.output` through `ctx.showStatus`, clearing
1308
- // the editor after the call, and reusing the active session's plugin
1309
- // reload pipeline. Spec authors get a single body usable from either
1310
- // dispatcher without forcing every TUI test to construct the full
1311
- // `SlashCommandRuntime` shape.
1312
1474
  const ctx = runtime.ctx;
1313
- const adapted: SlashCommandRuntime = {
1314
- session: ctx.session,
1315
- sessionManager: ctx.sessionManager,
1316
- settings: ctx.settings,
1317
- cwd: ctx.sessionManager.getCwd(),
1318
- output: (text: string) => {
1319
- ctx.showStatus(text);
1320
- },
1321
- refreshCommands: () => ctx.refreshSlashCommandState(),
1322
- reloadPlugins: async () => {
1323
- const projectPath = await resolveActiveProjectRegistryPath(ctx.sessionManager.getCwd());
1324
- clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
1325
- await ctx.refreshSlashCommandState();
1326
- await ctx.session.refreshSshTool({ activateIfAvailable: true });
1327
- },
1328
- };
1475
+ const adapted = toSlashCommandRuntime(runtime);
1329
1476
  const result = await command.handle(parsed, adapted);
1330
1477
  ctx.editor.setText("");
1331
1478
  if (result && typeof result === "object" && "prompt" in result) return result.prompt;
@@ -14,6 +14,7 @@ import { loadSkills, type Skill } from "./extensibility/skills";
14
14
  import customSystemPromptTemplate from "./prompts/system/custom-system-prompt.md" with { type: "text" };
15
15
  import projectPromptTemplate from "./prompts/system/project-prompt.md" with { type: "text" };
16
16
  import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
17
+ import volatileProjectContextTemplate from "./prompts/system/volatile-project-context.md" with { type: "text" };
17
18
  import { shortenPath } from "./tools/render-utils";
18
19
  import { AGENTS_MD_LIMIT, buildWorkspaceTree, type WorkspaceTree } from "./workspace-tree";
19
20
 
@@ -242,9 +243,12 @@ function dedupeExactContextFiles(
242
243
  }
243
244
 
244
245
  /**
245
- * Load all project context files using the capability API.
246
+ * Load all context files using the capability API.
246
247
  * Returns {path, content, depth} entries for all discovered context files.
247
- * Files are sorted by depth (descending) so files closer to cwd appear last/more prominent.
248
+ * Native user-global files (`~/.gjc/agent/AGENTS.md`) come first, then project
249
+ * files sorted by depth (descending) so files closer to cwd appear last/more
250
+ * prominent. User-home files from foreign providers (`~/.claude/CLAUDE.md`,
251
+ * `~/.codex/AGENTS.md`, …) stay excluded — only gjc's own user config applies.
248
252
  */
249
253
  export async function loadProjectContextFiles(
250
254
  options: LoadContextFilesOptions = {},
@@ -252,28 +256,32 @@ export async function loadProjectContextFiles(
252
256
  const resolvedCwd = options.cwd ?? getProjectDir();
253
257
 
254
258
  const result = await loadCapability(contextFileCapability.id, { cwd: resolvedCwd });
259
+ const items = result.items as ContextFile[];
260
+
261
+ // Native user-global context applies everywhere and is least specific, so it
262
+ // renders first — project files rendered later take precedence over it.
263
+ const userFiles = items
264
+ .filter(item => item.level === "user" && item._source.provider === "native")
265
+ .map(item => ({ path: item.path, content: item.content }));
255
266
 
256
267
  // Convert project-level ContextFile items and preserve depth info
257
- const files = result.items
258
- .filter(item => (item as ContextFile).level === "project")
259
- .map(item => {
260
- const contextFile = item as ContextFile;
261
- return {
262
- path: contextFile.path,
263
- content: contextFile.content,
264
- depth: contextFile.depth,
265
- };
266
- });
268
+ const projectFiles = items
269
+ .filter(item => item.level === "project")
270
+ .map(item => ({
271
+ path: item.path,
272
+ content: item.content,
273
+ depth: item.depth,
274
+ }));
267
275
 
268
276
  // Sort by depth (descending): higher depth (farther from cwd) comes first,
269
277
  // so files closer to cwd appear later and are more prominent
270
- files.sort((a, b) => {
278
+ projectFiles.sort((a, b) => {
271
279
  const depthA = a.depth ?? -1;
272
280
  const depthB = b.depth ?? -1;
273
281
  return depthB - depthA;
274
282
  });
275
283
 
276
- return dedupeExactContextFiles(files);
284
+ return dedupeExactContextFiles([...userFiles, ...projectFiles]);
277
285
  }
278
286
 
279
287
  /**
@@ -368,6 +376,13 @@ export interface BuildSystemPromptOptions {
368
376
  secretsEnabled?: boolean;
369
377
  /** Pre-loaded workspace tree (skips discovery if provided). May be a Promise to allow early kick-off. */
370
378
  workspaceTree?: WorkspaceTree | Promise<WorkspaceTree>;
379
+ /**
380
+ * Render a trimmed role-agent base prompt for subagent sessions: omits the
381
+ * workflow-surface/routing/self-awareness (`<gjc-runtime>`) and `<soul>` blocks
382
+ * that only apply to the top-level interactive/print agent. Tool safety, repo
383
+ * safety, and the completion contract are retained. Default: false.
384
+ */
385
+ subagent?: boolean;
371
386
  }
372
387
 
373
388
  /** Result of building provider-facing system prompt messages. */
@@ -375,6 +390,29 @@ export interface BuildSystemPromptResult {
375
390
  /** Ordered system prompt blocks. Providers should preserve entries as distinct messages/blocks. */
376
391
  systemPrompt: string[];
377
392
  }
393
+ export interface BuildVolatileProjectContextOptions {
394
+ cwd?: string;
395
+ date?: string;
396
+ workspaceTree?: WorkspaceTree;
397
+ }
398
+
399
+ export function buildVolatileProjectContext(options: BuildVolatileProjectContextOptions = {}): string {
400
+ const resolvedCwd = options.cwd ?? getProjectDir();
401
+ const date = options.date ?? new Date().toISOString().slice(0, 10);
402
+ return prompt
403
+ .render(volatileProjectContextTemplate, {
404
+ date,
405
+ cwd: shortenPath(resolvedCwd.replace(/\\/g, "/")),
406
+ workspaceTree: options.workspaceTree ?? {
407
+ rootPath: resolvedCwd,
408
+ rendered: "",
409
+ truncated: false,
410
+ totalLines: 0,
411
+ agentsMdFiles: [],
412
+ },
413
+ })
414
+ .trim();
415
+ }
378
416
 
379
417
  /** Build the system prompt with tools, guidelines, and context */
380
418
  export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}): Promise<BuildSystemPromptResult> {
@@ -403,6 +441,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
403
441
  eagerTasks = false,
404
442
  secretsEnabled = false,
405
443
  workspaceTree: providedWorkspaceTree,
444
+ subagent = false,
406
445
  } = options;
407
446
  const resolvedCwd = cwd ?? getProjectDir();
408
447
 
@@ -534,6 +573,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
534
573
  // Build tool descriptions for system prompt rendering.
535
574
  const toolPromptNames = new Map<string, string>(toolNames.map(name => [name, tools?.get(name)?.wireName ?? name]));
536
575
  const toolRefs = Object.fromEntries(toolPromptNames.entries());
576
+ const hasHiddenToolDiscoveryTool = Object.hasOwn(toolRefs, "search_tool_bm25");
537
577
  const toolInfo = toolNames.map(name => ({
538
578
  name: toolPromptNames.get(name) ?? name,
539
579
  internalName: name,
@@ -575,13 +615,14 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
575
615
  cwd: promptCwd,
576
616
  intentTracing: !!intentField,
577
617
  intentField: intentField ?? "",
578
- mcpDiscoveryMode,
618
+ mcpDiscoveryMode: mcpDiscoveryMode && hasHiddenToolDiscoveryTool,
579
619
  hasMCPDiscoveryServers: mcpDiscoveryServerSummaries.length > 0,
580
620
  mcpDiscoveryServerSummaries,
581
- toolDiscoveryActive,
621
+ toolDiscoveryActive: toolDiscoveryActive && hasHiddenToolDiscoveryTool,
582
622
  discoverableTools,
583
623
  eagerTasks,
584
624
  secretsEnabled,
625
+ subagent,
585
626
  };
586
627
  const rendered = prompt.render(resolvedCustomPrompt ? customSystemPromptTemplate : systemPromptTemplate, data);
587
628
  const systemPrompt = [rendered];