@gajae-code/coding-agent 0.2.4 → 0.2.5

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 (179) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -1
  3. package/dist/types/async/job-manager.d.ts +61 -0
  4. package/dist/types/config/settings-schema.d.ts +7 -3
  5. package/dist/types/config/settings.d.ts +1 -1
  6. package/dist/types/discovery/helpers.d.ts +1 -0
  7. package/dist/types/exec/bash-executor.d.ts +8 -1
  8. package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +2 -0
  9. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
  10. package/dist/types/modes/components/skill-hud/render.d.ts +1 -1
  11. package/dist/types/modes/interactive-mode.d.ts +1 -0
  12. package/dist/types/modes/theme/defaults/index.d.ts +45 -9477
  13. package/dist/types/modes/theme/theme.d.ts +1 -5
  14. package/dist/types/modes/types.d.ts +1 -0
  15. package/dist/types/sdk.d.ts +2 -0
  16. package/dist/types/session/streaming-output.d.ts +11 -0
  17. package/dist/types/skill-state/active-state.d.ts +1 -0
  18. package/dist/types/task/types.d.ts +1 -0
  19. package/dist/types/tools/bash-allowed-prefixes.d.ts +5 -0
  20. package/dist/types/tools/bash.d.ts +24 -0
  21. package/dist/types/tools/cron.d.ts +110 -0
  22. package/dist/types/tools/index.d.ts +4 -0
  23. package/dist/types/tools/monitor.d.ts +54 -0
  24. package/dist/types/web/search/index.d.ts +1 -0
  25. package/dist/types/web/search/provider.d.ts +11 -4
  26. package/dist/types/web/search/providers/duckduckgo.d.ts +57 -0
  27. package/dist/types/web/search/types.d.ts +1 -1
  28. package/package.json +7 -7
  29. package/src/async/job-manager.ts +224 -0
  30. package/src/cli/agents-cli.ts +3 -0
  31. package/src/config/settings-schema.ts +8 -2
  32. package/src/config/settings.ts +44 -7
  33. package/src/defaults/gjc/skills/deep-interview/SKILL.md +9 -2
  34. package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
  35. package/src/discovery/helpers.ts +5 -0
  36. package/src/eval/js/shared/rewrite-imports.ts +1 -2
  37. package/src/exec/bash-executor.ts +20 -9
  38. package/src/gjc-runtime/ralplan-runtime.ts +2 -0
  39. package/src/gjc-runtime/restricted-role-agent-bash.ts +5 -0
  40. package/src/hooks/skill-state.ts +1 -1
  41. package/src/internal-urls/docs-index.generated.ts +5 -3
  42. package/src/lsp/render.ts +1 -1
  43. package/src/modes/acp/acp-agent.ts +1 -1
  44. package/src/modes/acp/acp-client-bridge.ts +1 -1
  45. package/src/modes/components/agent-dashboard.ts +1 -1
  46. package/src/modes/components/diff.ts +2 -2
  47. package/src/modes/components/skill-hud/render.ts +7 -2
  48. package/src/modes/controllers/input-controller.ts +10 -2
  49. package/src/modes/controllers/selector-controller.ts +1 -1
  50. package/src/modes/interactive-mode.ts +20 -2
  51. package/src/modes/theme/defaults/index.ts +0 -196
  52. package/src/modes/theme/theme.ts +35 -35
  53. package/src/modes/types.ts +1 -0
  54. package/src/prompts/agents/architect.md +5 -1
  55. package/src/prompts/agents/critic.md +5 -1
  56. package/src/prompts/agents/frontmatter.md +1 -0
  57. package/src/prompts/agents/planner.md +5 -1
  58. package/src/prompts/tools/bash.md +9 -0
  59. package/src/prompts/tools/cron.md +25 -0
  60. package/src/prompts/tools/monitor.md +30 -0
  61. package/src/runtime-mcp/oauth-flow.ts +4 -2
  62. package/src/sdk.ts +3 -0
  63. package/src/session/agent-session.ts +16 -5
  64. package/src/session/streaming-output.ts +21 -0
  65. package/src/skill-state/active-state.ts +163 -12
  66. package/src/task/agents.ts +1 -0
  67. package/src/task/executor.ts +1 -0
  68. package/src/task/types.ts +1 -0
  69. package/src/tools/bash-allowed-prefixes.ts +169 -0
  70. package/src/tools/bash.ts +190 -29
  71. package/src/tools/browser/tab-worker.ts +1 -1
  72. package/src/tools/cron.ts +665 -0
  73. package/src/tools/index.ts +20 -2
  74. package/src/tools/monitor.ts +136 -0
  75. package/src/vim/engine.ts +3 -3
  76. package/src/web/search/index.ts +31 -18
  77. package/src/web/search/provider.ts +57 -12
  78. package/src/web/search/providers/duckduckgo.ts +279 -0
  79. package/src/web/search/types.ts +2 -0
  80. package/src/modes/theme/dark.json +0 -95
  81. package/src/modes/theme/defaults/alabaster.json +0 -93
  82. package/src/modes/theme/defaults/amethyst.json +0 -96
  83. package/src/modes/theme/defaults/anthracite.json +0 -93
  84. package/src/modes/theme/defaults/basalt.json +0 -91
  85. package/src/modes/theme/defaults/birch.json +0 -95
  86. package/src/modes/theme/defaults/dark-abyss.json +0 -91
  87. package/src/modes/theme/defaults/dark-arctic.json +0 -104
  88. package/src/modes/theme/defaults/dark-aurora.json +0 -95
  89. package/src/modes/theme/defaults/dark-catppuccin.json +0 -107
  90. package/src/modes/theme/defaults/dark-cavern.json +0 -91
  91. package/src/modes/theme/defaults/dark-copper.json +0 -95
  92. package/src/modes/theme/defaults/dark-cosmos.json +0 -90
  93. package/src/modes/theme/defaults/dark-cyberpunk.json +0 -102
  94. package/src/modes/theme/defaults/dark-dracula.json +0 -98
  95. package/src/modes/theme/defaults/dark-eclipse.json +0 -91
  96. package/src/modes/theme/defaults/dark-ember.json +0 -95
  97. package/src/modes/theme/defaults/dark-equinox.json +0 -90
  98. package/src/modes/theme/defaults/dark-forest.json +0 -96
  99. package/src/modes/theme/defaults/dark-github.json +0 -105
  100. package/src/modes/theme/defaults/dark-gruvbox.json +0 -112
  101. package/src/modes/theme/defaults/dark-lavender.json +0 -95
  102. package/src/modes/theme/defaults/dark-lunar.json +0 -89
  103. package/src/modes/theme/defaults/dark-midnight.json +0 -95
  104. package/src/modes/theme/defaults/dark-monochrome.json +0 -94
  105. package/src/modes/theme/defaults/dark-monokai.json +0 -98
  106. package/src/modes/theme/defaults/dark-nebula.json +0 -90
  107. package/src/modes/theme/defaults/dark-nord.json +0 -97
  108. package/src/modes/theme/defaults/dark-ocean.json +0 -101
  109. package/src/modes/theme/defaults/dark-one.json +0 -100
  110. package/src/modes/theme/defaults/dark-poimandres.json +0 -141
  111. package/src/modes/theme/defaults/dark-rainforest.json +0 -91
  112. package/src/modes/theme/defaults/dark-reef.json +0 -91
  113. package/src/modes/theme/defaults/dark-retro.json +0 -92
  114. package/src/modes/theme/defaults/dark-rose-pine.json +0 -96
  115. package/src/modes/theme/defaults/dark-sakura.json +0 -95
  116. package/src/modes/theme/defaults/dark-slate.json +0 -95
  117. package/src/modes/theme/defaults/dark-solarized.json +0 -97
  118. package/src/modes/theme/defaults/dark-solstice.json +0 -90
  119. package/src/modes/theme/defaults/dark-starfall.json +0 -91
  120. package/src/modes/theme/defaults/dark-sunset.json +0 -99
  121. package/src/modes/theme/defaults/dark-swamp.json +0 -90
  122. package/src/modes/theme/defaults/dark-synthwave.json +0 -103
  123. package/src/modes/theme/defaults/dark-taiga.json +0 -91
  124. package/src/modes/theme/defaults/dark-terminal.json +0 -95
  125. package/src/modes/theme/defaults/dark-tokyo-night.json +0 -101
  126. package/src/modes/theme/defaults/dark-tundra.json +0 -91
  127. package/src/modes/theme/defaults/dark-twilight.json +0 -91
  128. package/src/modes/theme/defaults/dark-volcanic.json +0 -91
  129. package/src/modes/theme/defaults/graphite.json +0 -92
  130. package/src/modes/theme/defaults/light-arctic.json +0 -107
  131. package/src/modes/theme/defaults/light-aurora-day.json +0 -91
  132. package/src/modes/theme/defaults/light-canyon.json +0 -91
  133. package/src/modes/theme/defaults/light-catppuccin.json +0 -106
  134. package/src/modes/theme/defaults/light-cirrus.json +0 -90
  135. package/src/modes/theme/defaults/light-coral.json +0 -95
  136. package/src/modes/theme/defaults/light-cyberpunk.json +0 -96
  137. package/src/modes/theme/defaults/light-dawn.json +0 -90
  138. package/src/modes/theme/defaults/light-dunes.json +0 -91
  139. package/src/modes/theme/defaults/light-eucalyptus.json +0 -95
  140. package/src/modes/theme/defaults/light-forest.json +0 -100
  141. package/src/modes/theme/defaults/light-frost.json +0 -95
  142. package/src/modes/theme/defaults/light-github.json +0 -115
  143. package/src/modes/theme/defaults/light-glacier.json +0 -91
  144. package/src/modes/theme/defaults/light-gruvbox.json +0 -108
  145. package/src/modes/theme/defaults/light-haze.json +0 -90
  146. package/src/modes/theme/defaults/light-honeycomb.json +0 -95
  147. package/src/modes/theme/defaults/light-lagoon.json +0 -91
  148. package/src/modes/theme/defaults/light-lavender.json +0 -95
  149. package/src/modes/theme/defaults/light-meadow.json +0 -91
  150. package/src/modes/theme/defaults/light-mint.json +0 -95
  151. package/src/modes/theme/defaults/light-monochrome.json +0 -101
  152. package/src/modes/theme/defaults/light-ocean.json +0 -99
  153. package/src/modes/theme/defaults/light-one.json +0 -99
  154. package/src/modes/theme/defaults/light-opal.json +0 -91
  155. package/src/modes/theme/defaults/light-orchard.json +0 -91
  156. package/src/modes/theme/defaults/light-paper.json +0 -95
  157. package/src/modes/theme/defaults/light-poimandres.json +0 -141
  158. package/src/modes/theme/defaults/light-prism.json +0 -90
  159. package/src/modes/theme/defaults/light-retro.json +0 -98
  160. package/src/modes/theme/defaults/light-sand.json +0 -95
  161. package/src/modes/theme/defaults/light-savanna.json +0 -91
  162. package/src/modes/theme/defaults/light-solarized.json +0 -102
  163. package/src/modes/theme/defaults/light-soleil.json +0 -90
  164. package/src/modes/theme/defaults/light-sunset.json +0 -99
  165. package/src/modes/theme/defaults/light-synthwave.json +0 -98
  166. package/src/modes/theme/defaults/light-tokyo-night.json +0 -111
  167. package/src/modes/theme/defaults/light-wetland.json +0 -91
  168. package/src/modes/theme/defaults/light-zenith.json +0 -89
  169. package/src/modes/theme/defaults/limestone.json +0 -94
  170. package/src/modes/theme/defaults/mahogany.json +0 -97
  171. package/src/modes/theme/defaults/marble.json +0 -93
  172. package/src/modes/theme/defaults/obsidian.json +0 -91
  173. package/src/modes/theme/defaults/onyx.json +0 -91
  174. package/src/modes/theme/defaults/pearl.json +0 -93
  175. package/src/modes/theme/defaults/porcelain.json +0 -91
  176. package/src/modes/theme/defaults/quartz.json +0 -96
  177. package/src/modes/theme/defaults/sandstone.json +0 -95
  178. package/src/modes/theme/defaults/titanium.json +0 -90
  179. package/src/modes/theme/light.json +0 -93
package/src/lsp/render.ts CHANGED
@@ -103,7 +103,7 @@ export function renderResult(
103
103
  args?: LspParams,
104
104
  ): Component {
105
105
  const content = result.content?.[0];
106
- if (!content || content.type !== "text" || !("text" in content) || !content.text) {
106
+ if (content?.type !== "text" || !("text" in content) || !content.text) {
107
107
  const icon = formatStatusIcon("warning", theme, options.spinnerFrame);
108
108
  const header = `${icon} LSP`;
109
109
  return new Text([header, theme.fg("dim", "No result")].join("\n"), 0, 0);
@@ -272,7 +272,7 @@ async function elicitFromAcpClient(
272
272
  finish(undefined);
273
273
  });
274
274
  const response = await promise;
275
- if (!response || response.action !== "accept" || !response.content) {
275
+ if (response?.action !== "accept" || !response.content) {
276
276
  return undefined;
277
277
  }
278
278
  return response.content.value;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ACP-side `ClientBridge` implementation. Wraps `AgentSideConnection` so the
3
- * `read`/`write`/`bash`/`edit` tools (and the permission gate in
3
+ * `read`/`write`/`bash`/`monitor`/`edit` tools (and the permission gate in
4
4
  * `AgentSession`) can route through the client when it advertises the
5
5
  * relevant capabilities at `initialize` time.
6
6
  */
@@ -121,7 +121,7 @@ function matchAgent(agent: DashboardAgent, query: string): boolean {
121
121
  function extractAssistantText(messages: AgentMessage[]): string | null {
122
122
  for (let i = messages.length - 1; i >= 0; i--) {
123
123
  const message = messages[i];
124
- if (!message || message.role !== "assistant") continue;
124
+ if (message?.role !== "assistant") continue;
125
125
  const blocks = message.content;
126
126
  if (!Array.isArray(blocks)) continue;
127
127
  const text = blocks
@@ -151,7 +151,7 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
151
151
  const removedLines: { lineNum: string; content: string }[] = [];
152
152
  while (i < lines.length) {
153
153
  const p = parseDiffLine(lines[i]);
154
- if (!p || p.prefix !== "-") break;
154
+ if (p?.prefix !== "-") break;
155
155
  removedLines.push({ lineNum: p.lineNum, content: p.content });
156
156
  i++;
157
157
  }
@@ -159,7 +159,7 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
159
159
  const addedLines: { lineNum: string; content: string }[] = [];
160
160
  while (i < lines.length) {
161
161
  const p = parseDiffLine(lines[i]);
162
- if (!p || p.prefix !== "+") break;
162
+ if (p?.prefix !== "+") break;
163
163
  addedLines.push({ lineNum: p.lineNum, content: p.content });
164
164
  i++;
165
165
  }
@@ -1,4 +1,8 @@
1
- import type { SkillActiveEntry, WorkflowHudChip } from "../../../skill-state/active-state";
1
+ import {
2
+ collapsePlanningPipeline,
3
+ type SkillActiveEntry,
4
+ type WorkflowHudChip,
5
+ } from "../../../skill-state/active-state";
2
6
  import { workflowReceiptStatus } from "../../../skill-state/workflow-state-contract";
3
7
 
4
8
  const ANSI_RESET_FG = "\x1b[39m";
@@ -69,7 +73,8 @@ function formatEntry(entry: SkillActiveEntry): string {
69
73
  }
70
74
 
71
75
  export function renderSkillHudBar(entries: readonly SkillActiveEntry[], width: number): string | null {
72
- const active = entries.filter(entry => entry.active !== false && sanitizeHudPart(entry.skill)).sort(compareEntries);
76
+ const visible = collapsePlanningPipeline(entries.filter(entry => entry.active !== false));
77
+ const active = visible.filter(entry => sanitizeHudPart(entry.skill)).sort(compareEntries);
73
78
  if (active.length === 0 || width <= 0) return null;
74
79
  const body = active.map(formatEntry).join(" + ");
75
80
  const prefix = `${ANSI_BORDER}◆${ANSI_RESET_FG} ${ANSI_BOLD}${ANSI_ACCENT}hud${ANSI_RESET_FG}${ANSI_RESET_BOLD} `;
@@ -64,6 +64,7 @@ export class InputController {
64
64
  } else if (this.ctx.isBashMode) {
65
65
  this.ctx.editor.setText("");
66
66
  this.ctx.isBashMode = false;
67
+ this.ctx.isBashNoContext = false;
67
68
  this.ctx.updateEditorBorderColor();
68
69
  } else if (this.ctx.session.isEvalRunning) {
69
70
  this.ctx.session.abortEval();
@@ -172,11 +173,17 @@ export class InputController {
172
173
 
173
174
  this.ctx.editor.onChange = (text: string) => {
174
175
  const wasBashMode = this.ctx.isBashMode;
176
+ const wasBashNoContext = this.ctx.isBashNoContext;
175
177
  const wasPythonMode = this.ctx.isPythonMode;
176
178
  const trimmed = text.trimStart();
177
- this.ctx.isBashMode = text.trimStart().startsWith("!");
179
+ this.ctx.isBashMode = trimmed.startsWith("!");
180
+ this.ctx.isBashNoContext = trimmed.startsWith("!!");
178
181
  this.ctx.isPythonMode = trimmed.startsWith("$") && !trimmed.startsWith("${");
179
- if (wasBashMode !== this.ctx.isBashMode || wasPythonMode !== this.ctx.isPythonMode) {
182
+ if (
183
+ wasBashMode !== this.ctx.isBashMode ||
184
+ wasBashNoContext !== this.ctx.isBashNoContext ||
185
+ wasPythonMode !== this.ctx.isPythonMode
186
+ ) {
180
187
  this.ctx.updateEditorBorderColor();
181
188
  }
182
189
  };
@@ -260,6 +267,7 @@ export class InputController {
260
267
  this.ctx.editor.addToHistory(text);
261
268
  await this.ctx.handleBashCommand(command, isExcluded);
262
269
  this.ctx.isBashMode = false;
270
+ this.ctx.isBashNoContext = false;
263
271
  this.ctx.updateEditorBorderColor();
264
272
  return;
265
273
  }
@@ -239,7 +239,7 @@ export class SelectorController {
239
239
  });
240
240
  },
241
241
  );
242
- return { component: selector, focus: selector };
242
+ return { component: selector, focus: selector.getSelectList() };
243
243
  });
244
244
  });
245
245
  }
@@ -112,12 +112,23 @@ const HINT_SHIMMER_PALETTE: ShimmerPalette = {
112
112
  high: "borderAccent",
113
113
  };
114
114
 
115
+ function getDefaultInputPrefix(): string {
116
+ return `${theme.fg("accent", ">")} `;
117
+ }
118
+
119
+ function getShellInputPrefix(isNoContext: boolean): string {
120
+ const shellLabel = isNoContext
121
+ ? theme.fg("warning", theme.bold("shell no-context"))
122
+ : theme.fg("bashMode", theme.bold("shell"));
123
+ return `${shellLabel} ${getDefaultInputPrefix()}`;
124
+ }
125
+
115
126
  function configureDefaultComposerChrome(editor: CustomEditor): void {
116
127
  editor.setBorderVisible(true);
117
128
  editor.setBorderStyle("sharp");
118
129
  editor.setClosedBorderBox(true);
119
130
  editor.setPromptGutter(undefined);
120
- editor.setInputPrefix(`${theme.fg("accent", ">")} `);
131
+ editor.setInputPrefix(getDefaultInputPrefix());
121
132
  editor.setPlaceholder("Type your message...");
122
133
  editor.setPaddingX(1);
123
134
  editor.setTopBorder(undefined);
@@ -236,6 +247,7 @@ export class InteractiveMode implements InteractiveModeContext {
236
247
  isInitialized = false;
237
248
  isBackgrounded = false;
238
249
  isBashMode = false;
250
+ isBashNoContext = false;
239
251
  toolOutputExpanded = false;
240
252
  todoExpanded = false;
241
253
  planModeEnabled = false;
@@ -808,7 +820,10 @@ export class InteractiveMode implements InteractiveModeContext {
808
820
 
809
821
  updateEditorChrome(): void {
810
822
  if (this.isBashMode) {
811
- this.editor.borderColor = theme.getBashModeBorderColor();
823
+ this.editor.borderColor = this.isBashNoContext
824
+ ? (str: string) => theme.fg("warning", str)
825
+ : theme.getBashModeBorderColor();
826
+ this.editor.setInputPrefix(getShellInputPrefix(this.isBashNoContext));
812
827
  } else if (this.isPythonMode) {
813
828
  this.editor.borderColor = theme.getPythonModeBorderColor();
814
829
  } else {
@@ -823,6 +838,9 @@ export class InteractiveMode implements InteractiveModeContext {
823
838
  this.editor.borderColor = theme.getThinkingBorderColor(level);
824
839
  }
825
840
  }
841
+ if (!this.isBashMode) {
842
+ this.editor.setInputPrefix(getDefaultInputPrefix());
843
+ }
826
844
  this.#setComposerTopBorder();
827
845
  this.ui.requestRender();
828
846
  }
@@ -1,203 +1,7 @@
1
- import alabaster from "./alabaster.json" with { type: "json" };
2
- import amethyst from "./amethyst.json" with { type: "json" };
3
- import anthracite from "./anthracite.json" with { type: "json" };
4
- import basalt from "./basalt.json" with { type: "json" };
5
- import birch from "./birch.json" with { type: "json" };
6
1
  import blue_crab from "./blue-crab.json" with { type: "json" };
7
- import dark_abyss from "./dark-abyss.json" with { type: "json" };
8
- import dark_arctic from "./dark-arctic.json" with { type: "json" };
9
- import dark_aurora from "./dark-aurora.json" with { type: "json" };
10
- import dark_catppuccin from "./dark-catppuccin.json" with { type: "json" };
11
- import dark_cavern from "./dark-cavern.json" with { type: "json" };
12
- import dark_copper from "./dark-copper.json" with { type: "json" };
13
- import dark_cosmos from "./dark-cosmos.json" with { type: "json" };
14
- import dark_cyberpunk from "./dark-cyberpunk.json" with { type: "json" };
15
- import dark_dracula from "./dark-dracula.json" with { type: "json" };
16
- import dark_eclipse from "./dark-eclipse.json" with { type: "json" };
17
- import dark_ember from "./dark-ember.json" with { type: "json" };
18
- import dark_equinox from "./dark-equinox.json" with { type: "json" };
19
- import dark_forest from "./dark-forest.json" with { type: "json" };
20
- import dark_github from "./dark-github.json" with { type: "json" };
21
- import dark_gruvbox from "./dark-gruvbox.json" with { type: "json" };
22
- import dark_lavender from "./dark-lavender.json" with { type: "json" };
23
- import dark_lunar from "./dark-lunar.json" with { type: "json" };
24
- import dark_midnight from "./dark-midnight.json" with { type: "json" };
25
- import dark_monochrome from "./dark-monochrome.json" with { type: "json" };
26
- import dark_monokai from "./dark-monokai.json" with { type: "json" };
27
- import dark_nebula from "./dark-nebula.json" with { type: "json" };
28
- import dark_nord from "./dark-nord.json" with { type: "json" };
29
- import dark_ocean from "./dark-ocean.json" with { type: "json" };
30
- import dark_one from "./dark-one.json" with { type: "json" };
31
- import dark_poimandres from "./dark-poimandres.json" with { type: "json" };
32
- import dark_rainforest from "./dark-rainforest.json" with { type: "json" };
33
- import dark_reef from "./dark-reef.json" with { type: "json" };
34
- import dark_retro from "./dark-retro.json" with { type: "json" };
35
- import dark_rose_pine from "./dark-rose-pine.json" with { type: "json" };
36
- import dark_sakura from "./dark-sakura.json" with { type: "json" };
37
- import dark_slate from "./dark-slate.json" with { type: "json" };
38
- import dark_solarized from "./dark-solarized.json" with { type: "json" };
39
- import dark_solstice from "./dark-solstice.json" with { type: "json" };
40
- import dark_starfall from "./dark-starfall.json" with { type: "json" };
41
- import dark_sunset from "./dark-sunset.json" with { type: "json" };
42
- import dark_swamp from "./dark-swamp.json" with { type: "json" };
43
- import dark_synthwave from "./dark-synthwave.json" with { type: "json" };
44
- import dark_taiga from "./dark-taiga.json" with { type: "json" };
45
- import dark_terminal from "./dark-terminal.json" with { type: "json" };
46
- import dark_tokyo_night from "./dark-tokyo-night.json" with { type: "json" };
47
- import dark_tundra from "./dark-tundra.json" with { type: "json" };
48
- import dark_twilight from "./dark-twilight.json" with { type: "json" };
49
- import dark_volcanic from "./dark-volcanic.json" with { type: "json" };
50
- import graphite from "./graphite.json" with { type: "json" };
51
- import light_arctic from "./light-arctic.json" with { type: "json" };
52
- import light_aurora_day from "./light-aurora-day.json" with { type: "json" };
53
- import light_canyon from "./light-canyon.json" with { type: "json" };
54
- import light_catppuccin from "./light-catppuccin.json" with { type: "json" };
55
- import light_cirrus from "./light-cirrus.json" with { type: "json" };
56
- import light_coral from "./light-coral.json" with { type: "json" };
57
- import light_cyberpunk from "./light-cyberpunk.json" with { type: "json" };
58
- import light_dawn from "./light-dawn.json" with { type: "json" };
59
- import light_dunes from "./light-dunes.json" with { type: "json" };
60
- import light_eucalyptus from "./light-eucalyptus.json" with { type: "json" };
61
- import light_forest from "./light-forest.json" with { type: "json" };
62
- import light_frost from "./light-frost.json" with { type: "json" };
63
- import light_github from "./light-github.json" with { type: "json" };
64
- import light_glacier from "./light-glacier.json" with { type: "json" };
65
- import light_gruvbox from "./light-gruvbox.json" with { type: "json" };
66
- import light_haze from "./light-haze.json" with { type: "json" };
67
- import light_honeycomb from "./light-honeycomb.json" with { type: "json" };
68
- import light_lagoon from "./light-lagoon.json" with { type: "json" };
69
- import light_lavender from "./light-lavender.json" with { type: "json" };
70
- import light_meadow from "./light-meadow.json" with { type: "json" };
71
- import light_mint from "./light-mint.json" with { type: "json" };
72
- import light_monochrome from "./light-monochrome.json" with { type: "json" };
73
- import light_ocean from "./light-ocean.json" with { type: "json" };
74
- import light_one from "./light-one.json" with { type: "json" };
75
- import light_opal from "./light-opal.json" with { type: "json" };
76
- import light_orchard from "./light-orchard.json" with { type: "json" };
77
- import light_paper from "./light-paper.json" with { type: "json" };
78
- import light_poimandres from "./light-poimandres.json" with { type: "json" };
79
- import light_prism from "./light-prism.json" with { type: "json" };
80
- import light_retro from "./light-retro.json" with { type: "json" };
81
- import light_sand from "./light-sand.json" with { type: "json" };
82
- import light_savanna from "./light-savanna.json" with { type: "json" };
83
- import light_solarized from "./light-solarized.json" with { type: "json" };
84
- import light_soleil from "./light-soleil.json" with { type: "json" };
85
- import light_sunset from "./light-sunset.json" with { type: "json" };
86
- import light_synthwave from "./light-synthwave.json" with { type: "json" };
87
- import light_tokyo_night from "./light-tokyo-night.json" with { type: "json" };
88
- import light_wetland from "./light-wetland.json" with { type: "json" };
89
- import light_zenith from "./light-zenith.json" with { type: "json" };
90
- import limestone from "./limestone.json" with { type: "json" };
91
- import mahogany from "./mahogany.json" with { type: "json" };
92
- import marble from "./marble.json" with { type: "json" };
93
- import obsidian from "./obsidian.json" with { type: "json" };
94
- import onyx from "./onyx.json" with { type: "json" };
95
- import pearl from "./pearl.json" with { type: "json" };
96
- import porcelain from "./porcelain.json" with { type: "json" };
97
- import quartz from "./quartz.json" with { type: "json" };
98
2
  import red_claw from "./red-claw.json" with { type: "json" };
99
- import sandstone from "./sandstone.json" with { type: "json" };
100
- import titanium from "./titanium.json" with { type: "json" };
101
3
 
102
4
  export const defaultThemes = {
103
- alabaster: alabaster,
104
- amethyst: amethyst,
105
- anthracite: anthracite,
106
- basalt: basalt,
107
- birch: birch,
108
5
  "blue-crab": blue_crab,
109
- "dark-abyss": dark_abyss,
110
- "dark-arctic": dark_arctic,
111
- "dark-aurora": dark_aurora,
112
- "dark-catppuccin": dark_catppuccin,
113
- "dark-cavern": dark_cavern,
114
- "dark-copper": dark_copper,
115
- "dark-cosmos": dark_cosmos,
116
- "dark-cyberpunk": dark_cyberpunk,
117
- "dark-dracula": dark_dracula,
118
- "dark-eclipse": dark_eclipse,
119
- "dark-ember": dark_ember,
120
- "dark-equinox": dark_equinox,
121
- "dark-forest": dark_forest,
122
- "dark-github": dark_github,
123
- "dark-gruvbox": dark_gruvbox,
124
- "dark-lavender": dark_lavender,
125
- "dark-lunar": dark_lunar,
126
- "dark-midnight": dark_midnight,
127
- "dark-monochrome": dark_monochrome,
128
- "dark-monokai": dark_monokai,
129
- "dark-nebula": dark_nebula,
130
- "dark-nord": dark_nord,
131
- "dark-ocean": dark_ocean,
132
- "dark-one": dark_one,
133
- "dark-poimandres": dark_poimandres,
134
- "dark-rainforest": dark_rainforest,
135
- "dark-reef": dark_reef,
136
- "dark-retro": dark_retro,
137
- "dark-rose-pine": dark_rose_pine,
138
- "dark-sakura": dark_sakura,
139
- "dark-slate": dark_slate,
140
- "dark-solarized": dark_solarized,
141
- "dark-solstice": dark_solstice,
142
- "dark-starfall": dark_starfall,
143
- "dark-sunset": dark_sunset,
144
- "dark-swamp": dark_swamp,
145
- "dark-synthwave": dark_synthwave,
146
- "dark-taiga": dark_taiga,
147
- "dark-terminal": dark_terminal,
148
- "dark-tokyo-night": dark_tokyo_night,
149
- "dark-tundra": dark_tundra,
150
- "dark-twilight": dark_twilight,
151
- "dark-volcanic": dark_volcanic,
152
- graphite: graphite,
153
- "light-arctic": light_arctic,
154
- "light-aurora-day": light_aurora_day,
155
- "light-canyon": light_canyon,
156
- "light-catppuccin": light_catppuccin,
157
- "light-cirrus": light_cirrus,
158
- "light-coral": light_coral,
159
- "light-cyberpunk": light_cyberpunk,
160
- "light-dawn": light_dawn,
161
- "light-dunes": light_dunes,
162
- "light-eucalyptus": light_eucalyptus,
163
- "light-forest": light_forest,
164
- "light-frost": light_frost,
165
- "light-github": light_github,
166
- "light-glacier": light_glacier,
167
- "light-gruvbox": light_gruvbox,
168
- "light-haze": light_haze,
169
- "light-honeycomb": light_honeycomb,
170
- "light-lagoon": light_lagoon,
171
- "light-lavender": light_lavender,
172
- "light-meadow": light_meadow,
173
- "light-mint": light_mint,
174
- "light-monochrome": light_monochrome,
175
- "light-ocean": light_ocean,
176
- "light-one": light_one,
177
- "light-opal": light_opal,
178
- "light-orchard": light_orchard,
179
- "light-paper": light_paper,
180
- "light-poimandres": light_poimandres,
181
- "light-prism": light_prism,
182
- "light-retro": light_retro,
183
- "light-sand": light_sand,
184
- "light-savanna": light_savanna,
185
- "light-solarized": light_solarized,
186
- "light-soleil": light_soleil,
187
- "light-sunset": light_sunset,
188
- "light-synthwave": light_synthwave,
189
- "light-tokyo-night": light_tokyo_night,
190
- "light-wetland": light_wetland,
191
- "light-zenith": light_zenith,
192
- limestone: limestone,
193
- mahogany: mahogany,
194
- marble: marble,
195
- obsidian: obsidian,
196
- onyx: onyx,
197
- pearl: pearl,
198
- porcelain: porcelain,
199
- quartz: quartz,
200
6
  "red-claw": red_claw,
201
- sandstone: sandstone,
202
- titanium: titanium,
203
7
  };
@@ -14,9 +14,7 @@ import { adjustHsv, getCustomThemesDir, isEnoent, logger } from "@gajae-code/uti
14
14
  import chalk from "chalk";
15
15
  import * as z from "zod/v4";
16
16
  // Embed theme JSON files at build time
17
- import darkThemeJson from "./dark.json" with { type: "json" };
18
17
  import { defaultThemes } from "./defaults";
19
- import lightThemeJson from "./light.json" with { type: "json" };
20
18
  import { resolveMermaidAscii } from "./mermaid-cache";
21
19
 
22
20
  export { getLanguageFromPath } from "../../utils/lang-from-path";
@@ -1547,8 +1545,6 @@ export class Theme {
1547
1545
  // ============================================================================
1548
1546
 
1549
1547
  const BUILTIN_THEMES: Record<string, ThemeJson> = {
1550
- dark: darkThemeJson as ThemeJson,
1551
- light: lightThemeJson as ThemeJson,
1552
1548
  ...(defaultThemes as Record<string, ThemeJson>),
1553
1549
  };
1554
1550
 
@@ -1646,7 +1642,7 @@ async function loadThemeJson(name: string): Promise<ThemeJson> {
1646
1642
  errorMessage += `\nMissing required color tokens:\n`;
1647
1643
  errorMessage += missingColors.map(c => ` - ${c}`).join("\n");
1648
1644
  errorMessage += `\n\nPlease add these colors to your theme's "colors" object.`;
1649
- errorMessage += `\nSee the built-in themes (dark.json, light.json) for reference values.`;
1645
+ errorMessage += `\nSee the built-in themes (red-claw.json, blue-crab.json) for reference values.`;
1650
1646
  }
1651
1647
  if (otherErrors.length > 0) {
1652
1648
  errorMessage += `\n\nOther errors:\n${otherErrors.join("\n")}`;
@@ -1780,7 +1776,7 @@ var themeReloadTimer: NodeJS.Timeout | undefined;
1780
1776
  var sigwinchHandler: (() => void) | undefined;
1781
1777
  var autoDetectedTheme: boolean = false;
1782
1778
  var autoDarkTheme: string = "red-claw";
1783
- var autoLightTheme: string = "light";
1779
+ var autoLightTheme: string = "blue-crab";
1784
1780
  var onThemeChangeCallback: (() => void) | undefined;
1785
1781
  var themeLoadRequestId: number = 0;
1786
1782
  var previewThemeActive: boolean = false;
@@ -1801,7 +1797,7 @@ export async function initTheme(
1801
1797
  ): Promise<void> {
1802
1798
  autoDetectedTheme = true;
1803
1799
  autoDarkTheme = darkTheme ?? "red-claw";
1804
- autoLightTheme = lightTheme ?? "light";
1800
+ autoLightTheme = lightTheme ?? "blue-crab";
1805
1801
  const name = getDefaultTheme();
1806
1802
  previewThemeActive = false;
1807
1803
  currentThemeName = name;
@@ -1814,9 +1810,9 @@ export async function initTheme(
1814
1810
  startSigwinchListener();
1815
1811
  }
1816
1812
  } catch (err) {
1817
- logger.debug("Theme loading failed, falling back to dark theme", { error: String(err) });
1818
- currentThemeName = "dark";
1819
- theme = await loadTheme("dark", getCurrentThemeOptions());
1813
+ logger.debug("Theme loading failed, falling back to red-claw theme", { error: String(err) });
1814
+ currentThemeName = "red-claw";
1815
+ theme = await loadTheme("red-claw", getCurrentThemeOptions());
1820
1816
  // Don't start watcher for fallback theme
1821
1817
  }
1822
1818
  }
@@ -1846,9 +1842,9 @@ export async function setTheme(
1846
1842
  if (requestId !== themeLoadRequestId) {
1847
1843
  return { success: false, error: "Theme change superseded by a newer request" };
1848
1844
  }
1849
- // Theme is invalid - fall back to dark theme
1850
- currentThemeName = "dark";
1851
- theme = await loadTheme("dark", getCurrentThemeOptions());
1845
+ // Theme is invalid - fall back to red-claw theme
1846
+ currentThemeName = "red-claw";
1847
+ theme = await loadTheme("red-claw", getCurrentThemeOptions());
1852
1848
  // Don't start watcher for fallback theme
1853
1849
  return {
1854
1850
  success: false,
@@ -1956,8 +1952,8 @@ export async function setSymbolPreset(preset: SymbolPreset): Promise<void> {
1956
1952
  try {
1957
1953
  theme = await loadTheme(currentThemeName, getCurrentThemeOptions());
1958
1954
  } catch {
1959
- // Fall back to dark theme with new preset
1960
- theme = await loadTheme("dark", getCurrentThemeOptions());
1955
+ // Fall back to red-claw theme with new preset
1956
+ theme = await loadTheme("red-claw", getCurrentThemeOptions());
1961
1957
  }
1962
1958
  if (onThemeChangeCallback) {
1963
1959
  onThemeChangeCallback();
@@ -1982,8 +1978,8 @@ export async function setColorBlindMode(enabled: boolean): Promise<void> {
1982
1978
  try {
1983
1979
  theme = await loadTheme(currentThemeName, getCurrentThemeOptions());
1984
1980
  } catch {
1985
- // Fall back to dark theme
1986
- theme = await loadTheme("dark", getCurrentThemeOptions());
1981
+ // Fall back to red-claw theme
1982
+ theme = await loadTheme("red-claw", getCurrentThemeOptions());
1987
1983
  }
1988
1984
  if (onThemeChangeCallback) {
1989
1985
  onThemeChangeCallback();
@@ -2019,8 +2015,8 @@ export function isValidSymbolPreset(preset: string): preset is SymbolPreset {
2019
2015
  async function startThemeWatcher(): Promise<void> {
2020
2016
  stopThemeWatcher();
2021
2017
 
2022
- // Only watch if it's a custom theme (not built-in)
2023
- if (!currentThemeName || currentThemeName === "dark" || currentThemeName === "light") {
2018
+ // Only watch custom themes (not built-ins)
2019
+ if (!currentThemeName || currentThemeName in getBuiltinThemes()) {
2024
2020
  return;
2025
2021
  }
2026
2022
 
@@ -2230,8 +2226,8 @@ function ansi256ToHex(index: number): string {
2230
2226
  */
2231
2227
  export async function getResolvedThemeColors(themeName?: string): Promise<Record<string, string>> {
2232
2228
  const name = themeName ?? getDefaultTheme();
2233
- const isLight = name === "light";
2234
2229
  const themeJson = await loadThemeJson(name);
2230
+ const isLight = isThemeJsonLight(themeJson);
2235
2231
  const resolved = resolveThemeColors(themeJson.colors, themeJson.vars);
2236
2232
 
2237
2233
  // Default text color for empty values (terminal uses default fg color)
@@ -2255,21 +2251,7 @@ export async function getResolvedThemeColors(themeName?: string): Promise<Record
2255
2251
  * Check if a theme is a "light" theme by analyzing its background color luminance.
2256
2252
  * Loads theme JSON synchronously (built-in or custom file) and resolves userMessageBg.
2257
2253
  */
2258
- export function isLightTheme(themeName?: string): boolean {
2259
- const name = themeName ?? "dark";
2260
- const builtinThemes = getBuiltinThemes();
2261
- let themeJson: ThemeJson | undefined;
2262
- if (name in builtinThemes) {
2263
- themeJson = builtinThemes[name];
2264
- } else {
2265
- try {
2266
- const customPath = path.join(getCustomThemesDir(), `${name}.json`);
2267
- const content = fs.readFileSync(customPath, "utf-8");
2268
- themeJson = JSON.parse(content) as ThemeJson;
2269
- } catch {
2270
- return false;
2271
- }
2272
- }
2254
+ function isThemeJsonLight(themeJson: ThemeJson): boolean {
2273
2255
  try {
2274
2256
  const resolved = resolveVarRefs(themeJson.colors.userMessageBg, themeJson.vars ?? {});
2275
2257
  if (typeof resolved !== "string" || !resolved.startsWith("#") || resolved.length !== 7) return false;
@@ -2284,6 +2266,24 @@ export function isLightTheme(themeName?: string): boolean {
2284
2266
  }
2285
2267
  }
2286
2268
 
2269
+ export function isLightTheme(themeName?: string, agentDir?: string): boolean {
2270
+ const name = themeName ?? "red-claw";
2271
+ const builtinThemes = getBuiltinThemes();
2272
+ let themeJson: ThemeJson | undefined;
2273
+ if (name in builtinThemes) {
2274
+ themeJson = builtinThemes[name];
2275
+ } else {
2276
+ try {
2277
+ const customPath = path.join(getCustomThemesDir(agentDir), `${name}.json`);
2278
+ const content = fs.readFileSync(customPath, "utf-8");
2279
+ themeJson = JSON.parse(content) as ThemeJson;
2280
+ } catch {
2281
+ return false;
2282
+ }
2283
+ }
2284
+ return isThemeJsonLight(themeJson);
2285
+ }
2286
+
2287
2287
  /**
2288
2288
  * Get explicit export colors from theme JSON, if specified.
2289
2289
  * Returns undefined for each color that isn't explicitly set.
@@ -86,6 +86,7 @@ export interface InteractiveModeContext {
86
86
  isInitialized: boolean;
87
87
  isBackgrounded: boolean;
88
88
  isBashMode: boolean;
89
+ isBashNoContext: boolean;
89
90
  toolOutputExpanded: boolean;
90
91
  todoExpanded: boolean;
91
92
  planModeEnabled: boolean;
@@ -1,10 +1,13 @@
1
1
  ---
2
2
  name: architect
3
3
  description: Read-only architecture and code-review agent with severity-rated findings and status verdicts
4
- tools: read, search, find, lsp, ast_grep, web_search, report_finding
4
+ tools: read, search, find, lsp, ast_grep, web_search, bash, report_finding
5
5
  thinking-level: high
6
6
  blocking: true
7
7
  forkContext: allowed
8
+ bashAllowedPrefixes:
9
+ - gjc ralplan --write
10
+ - gjc state
8
11
  ---
9
12
  <identity>
10
13
  You are Architect. You combine system architecture review with code-review discipline. Diagnose, analyze, and recommend with file-backed evidence. You are read-only.
@@ -22,6 +25,7 @@ You may receive a forked parent-conversation snapshot as background. Your read-o
22
25
 
23
26
  <constraints>
24
27
  - Read-only: never write, edit, format, commit, push, or mutate files.
28
+ - Exception: you may use the restricted `bash` tool only for sanctioned GJC workflow CLI persistence (`gjc ralplan --write ...`) and GJC workflow state read/write/contract commands (`gjc state ...`). For `gjc ralplan --write`, pass the verdict markdown inline in `--artifact`, not as a file path. Do not use bash for product-source writes, direct handoffs, state clears, or general shell work.
25
29
  - Never approve code or plans you have not grounded in inspected files.
26
30
  - Never give generic advice detached from this codebase.
27
31
  - Never approve CRITICAL or HIGH severity issues.
@@ -1,8 +1,11 @@
1
1
  ---
2
2
  name: critic
3
3
  description: Read-only plan critic that approves only actionable, verifiable execution plans
4
- tools: read, search, find, lsp, ast_grep, web_search
4
+ tools: read, search, find, lsp, ast_grep, web_search, bash
5
5
  thinking-level: high
6
+ bashAllowedPrefixes:
7
+ - gjc ralplan --write
8
+ - gjc state
6
9
  ---
7
10
  <identity>
8
11
  You are Critic. Decide whether a work plan is actionable before execution begins.
@@ -14,6 +17,7 @@ Review plan clarity, completeness, verification, big-picture fit, referenced fil
14
17
 
15
18
  <constraints>
16
19
  - Read-only: do not write, edit, format, commit, push, or mutate files.
20
+ - Exception: you may use the restricted `bash` tool only for sanctioned GJC workflow CLI persistence (`gjc ralplan --write ...`) and GJC workflow state read/write/contract commands (`gjc state ...`). For `gjc ralplan --write`, pass the evaluation markdown inline in `--artifact`, not as a file path. Do not use bash for product-source writes, direct handoffs, state clears, or general shell work.
17
21
  - A lone file path is valid input; read and evaluate it.
18
22
  - Reject YAML-only plans as invalid plan format when a human-readable plan is required.
19
23
  - Do not invent problems; report no issues found when the plan passes.
@@ -9,5 +9,6 @@ description: {{jsonStringify description}}
9
9
  {{/if}}{{#if hide}}hide: true
10
10
  {{/if}}{{#if autoloadSkills}}autoloadSkills: {{jsonStringify autoloadSkills}}
11
11
  {{/if}}{{#if forkContext}}forkContext: {{jsonStringify forkContext}}
12
+ {{/if}}{{#if bashAllowedPrefixes}}bashAllowedPrefixes: {{jsonStringify bashAllowedPrefixes}}
12
13
  {{/if}}---
13
14
  {{body}}
@@ -1,8 +1,11 @@
1
1
  ---
2
2
  name: planner
3
3
  description: Read-only planning agent for sequencing, acceptance criteria, risks, and handoff shape
4
- tools: read, search, find, lsp, ast_grep, web_search
4
+ tools: read, search, find, lsp, ast_grep, web_search, bash
5
5
  thinking-level: medium
6
+ bashAllowedPrefixes:
7
+ - gjc ralplan --write
8
+ - gjc state
6
9
  ---
7
10
  <identity>
8
11
  You are Planner. Turn requests into actionable work plans. You plan; you do not implement.
@@ -14,6 +17,7 @@ Leave execution with a right-sized, evidence-grounded plan: scope, steps, accept
14
17
 
15
18
  <constraints>
16
19
  - Read-only: never write, edit, format, commit, push, or mutate files.
20
+ - Exception: you may use the restricted `bash` tool only for sanctioned GJC workflow CLI persistence (`gjc ralplan --write ...`) and GJC workflow state read/write/contract commands (`gjc state ...`). For `gjc ralplan --write`, pass the plan markdown inline in `--artifact`, not as a file path. Do not use bash for product-source writes, direct handoffs, state clears, or general shell work.
17
21
  - Inspect the repository before asking about code facts.
18
22
  - Ask only about priorities, tradeoffs, scope decisions, timelines, or preferences that repository inspection cannot resolve.
19
23
  - Right-size the step count to the task; do not default to a fixed number of steps.
@@ -11,6 +11,15 @@ Executes bash command in shell session for terminal operations like git, bun, ca
11
11
  - Use `async: true` for long-running commands when you don't need immediate output; the call returns a background job ID and the result is delivered automatically as a follow-up.
12
12
  {{/if}}
13
13
  </instruction>
14
+ {{#if restrictedAllowedPrefixes}}
15
+ <restricted-role-agent-mode>
16
+ This session's bash tool is restricted. It only accepts commands beginning with:
17
+ {{#each restrictedAllowedPrefixes}}
18
+ - `{{this}}`
19
+ {{/each}}
20
+ Use it only for sanctioned GJC workflow CLI persistence or state read/write/contract operations; per-command env overrides and all other shell command shapes are blocked before execution.
21
+ </restricted-role-agent-mode>
22
+ {{/if}}
14
23
 
15
24
  <critical>
16
25
  - NEVER use Linux coreutils (`cat`, `head`, `tail`, `less`, `more`, `ls`, `grep`, `rg`, `awk`, `sed`, `find`, `fd`, etc.) when a dedicated tool suffices — ALWAYS prefer `read`, `search`, `find`, `edit`, `write`.