@sayknow-cli/coding-agent 0.3.5 → 0.3.7

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 (238) hide show
  1. package/README.md +76 -0
  2. package/dist/types/capability/mcp.d.ts +2 -0
  3. package/dist/types/cli/local-provider-smoke.d.ts +59 -0
  4. package/dist/types/cli/notify-cli.d.ts +1 -1
  5. package/dist/types/cli/telegram-cli.d.ts +13 -0
  6. package/dist/types/cli/update-cli.d.ts +24 -1
  7. package/dist/types/commands/local-provider.d.ts +34 -0
  8. package/dist/types/commands/telegram.d.ts +22 -0
  9. package/dist/types/config/keybindings.d.ts +12 -6
  10. package/dist/types/config/model-registry.d.ts +5 -0
  11. package/dist/types/config/models-config-schema.d.ts +5 -0
  12. package/dist/types/config/settings-schema.d.ts +284 -4
  13. package/dist/types/config/settings.d.ts +7 -0
  14. package/dist/types/config/telegram-autostart.d.ts +14 -0
  15. package/dist/types/config/telegram-env-bridge.d.ts +18 -0
  16. package/dist/types/coordinator-mcp/server.d.ts +8 -0
  17. package/dist/types/defaults/skc-defaults.d.ts +7 -0
  18. package/dist/types/discovery/cursor.d.ts +4 -1
  19. package/dist/types/discovery/index.d.ts +0 -1
  20. package/dist/types/discovery/windsurf.d.ts +3 -1
  21. package/dist/types/extensibility/extensions/types.d.ts +11 -0
  22. package/dist/types/i18n/messages/en.d.ts +1 -0
  23. package/dist/types/modes/components/custom-editor.d.ts +6 -1
  24. package/dist/types/modes/components/custom-model-preset-wizard.d.ts +1 -1
  25. package/dist/types/modes/components/hook-selector.d.ts +5 -0
  26. package/dist/types/modes/components/model-selector.d.ts +2 -0
  27. package/dist/types/modes/components/settings-selector.d.ts +5 -1
  28. package/dist/types/modes/components/skill-message.d.ts +1 -0
  29. package/dist/types/modes/components/status-line/types.d.ts +5 -7
  30. package/dist/types/modes/components/status-line.d.ts +15 -3
  31. package/dist/types/modes/components/two-column-body.d.ts +9 -0
  32. package/dist/types/modes/components/welcome.d.ts +9 -1
  33. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  34. package/dist/types/modes/controllers/input-controller.d.ts +6 -1
  35. package/dist/types/modes/controllers/selector-controller.d.ts +10 -1
  36. package/dist/types/modes/interactive-mode.d.ts +2 -0
  37. package/dist/types/modes/prompt-action-autocomplete.d.ts +3 -0
  38. package/dist/types/modes/rpc/rpc-types.d.ts +1 -0
  39. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +41 -7
  40. package/dist/types/modes/slash-command-visibility.d.ts +1 -0
  41. package/dist/types/modes/theme/theme.d.ts +1 -1
  42. package/dist/types/modes/tmux-scroll.d.ts +18 -0
  43. package/dist/types/modes/types.d.ts +5 -1
  44. package/dist/types/modes/utils/terminal-bell.d.ts +3 -0
  45. package/dist/types/notifications/daemon-paths.d.ts +9 -0
  46. package/dist/types/notifications/html-format.d.ts +2 -0
  47. package/dist/types/notifications/recent-activity.d.ts +5 -1
  48. package/dist/types/notifications/telegram-daemon-cli.d.ts +21 -4
  49. package/dist/types/notifications/telegram-daemon-control.d.ts +8 -0
  50. package/dist/types/notifications/telegram-daemon.d.ts +4 -10
  51. package/dist/types/notifications/telegram-reference.d.ts +4 -0
  52. package/dist/types/notifications/threaded-render.d.ts +1 -0
  53. package/dist/types/runtime-mcp/config-writer.d.ts +9 -0
  54. package/dist/types/runtime-mcp/config.d.ts +2 -0
  55. package/dist/types/runtime-mcp/loader.d.ts +2 -0
  56. package/dist/types/runtime-mcp/manager.d.ts +2 -0
  57. package/dist/types/runtime-mcp/types.d.ts +6 -0
  58. package/dist/types/sdk.d.ts +1 -1
  59. package/dist/types/skc-runtime/launch-tmux.d.ts +2 -2
  60. package/dist/types/skc-runtime/ralplan-runtime.d.ts +3 -2
  61. package/dist/types/skc-runtime/restricted-role-agent-bash.d.ts +1 -0
  62. package/dist/types/skc-runtime/session-state-sidecar.d.ts +5 -0
  63. package/dist/types/skc-runtime/state-writer.d.ts +1 -0
  64. package/dist/types/skc-runtime/team-runtime.d.ts +1 -1
  65. package/dist/types/skc-runtime/tmux-common.d.ts +12 -0
  66. package/dist/types/skc-runtime/ultragoal-guard.d.ts +7 -1
  67. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +0 -4
  68. package/dist/types/slash-commands/types.d.ts +2 -0
  69. package/dist/types/system-prompt.d.ts +7 -0
  70. package/dist/types/tools/skill.d.ts +1 -1
  71. package/dist/types/utils/cmux-workspace.d.ts +52 -0
  72. package/dist/types/web/search/index.d.ts +4 -2
  73. package/dist/types/web/search/provider.d.ts +14 -0
  74. package/dist/types/web/search/providers/utils.d.ts +42 -13
  75. package/dist/types/web/search/render.d.ts +1 -0
  76. package/dist/types/workflow/workflow-intent-diff.d.ts +19 -0
  77. package/package.json +8 -8
  78. package/scripts/build-binary.ts +3 -0
  79. package/scripts/verify-insane-vendor.ts +0 -5
  80. package/src/capability/mcp.ts +2 -0
  81. package/src/cli/local-provider-smoke.ts +632 -0
  82. package/src/cli/notify-cli.ts +4 -2
  83. package/src/cli/telegram-cli.ts +156 -0
  84. package/src/cli/update-cli.ts +179 -13
  85. package/src/cli/web-search-cli.ts +5 -7
  86. package/src/cli.ts +20 -1
  87. package/src/commands/launch.ts +47 -2
  88. package/src/commands/local-provider.ts +62 -0
  89. package/src/commands/notify.ts +1 -1
  90. package/src/commands/ralplan.ts +1 -0
  91. package/src/commands/telegram.ts +47 -0
  92. package/src/config/keybindings.ts +15 -6
  93. package/src/config/mcp-schema.json +4 -0
  94. package/src/config/model-registry.ts +55 -1
  95. package/src/config/models-config-schema.ts +9 -0
  96. package/src/config/settings-schema.ts +256 -4
  97. package/src/config/settings.ts +11 -0
  98. package/src/config/telegram-autostart.ts +107 -0
  99. package/src/config/telegram-env-bridge.ts +92 -0
  100. package/src/coordinator-mcp/server.ts +276 -34
  101. package/src/defaults/skc/skills/ralplan/SKILL.md +29 -20
  102. package/src/defaults/skc/skills/ultragoal/SKILL.md +37 -112
  103. package/src/defaults/skc-defaults.ts +16 -0
  104. package/src/discovery/builtin.ts +12 -0
  105. package/src/discovery/cursor.ts +5 -84
  106. package/src/discovery/gemini.ts +3 -89
  107. package/src/discovery/index.ts +4 -1
  108. package/src/discovery/mcp-json.ts +11 -0
  109. package/src/discovery/opencode.ts +5 -122
  110. package/src/discovery/windsurf.ts +4 -82
  111. package/src/edit/read-file.ts +9 -1
  112. package/src/extensibility/extensions/runner.ts +2 -1
  113. package/src/extensibility/extensions/types.ts +11 -0
  114. package/src/extensibility/skills.ts +30 -2
  115. package/src/hooks/skill-state.ts +33 -73
  116. package/src/i18n/messages/de.ts +1 -0
  117. package/src/i18n/messages/en.ts +1 -0
  118. package/src/i18n/messages/es.ts +1 -0
  119. package/src/i18n/messages/fr.ts +1 -0
  120. package/src/i18n/messages/ja.settings.ts +28 -0
  121. package/src/i18n/messages/ja.ts +1 -0
  122. package/src/i18n/messages/ko.settings.ts +28 -0
  123. package/src/i18n/messages/ko.ts +1 -0
  124. package/src/i18n/messages/zh.settings.ts +28 -0
  125. package/src/i18n/messages/zh.ts +1 -0
  126. package/src/internal-urls/docs-index.generated.ts +12 -7
  127. package/src/main.ts +5 -2
  128. package/src/migrate/executor.ts +23 -6
  129. package/src/modes/acp/acp-agent.ts +7 -6
  130. package/src/modes/components/agent-dashboard.ts +3 -35
  131. package/src/modes/components/custom-editor.ts +26 -0
  132. package/src/modes/components/custom-model-preset-wizard.ts +30 -189
  133. package/src/modes/components/extensions/extension-dashboard.ts +2 -48
  134. package/src/modes/components/hook-selector.ts +21 -3
  135. package/src/modes/components/model-selector.ts +246 -11
  136. package/src/modes/components/session-observer-overlay.ts +3 -3
  137. package/src/modes/components/session-selector.ts +19 -9
  138. package/src/modes/components/settings-selector.ts +551 -11
  139. package/src/modes/components/skill-message.ts +18 -10
  140. package/src/modes/components/status-line/presets.ts +11 -0
  141. package/src/modes/components/status-line/segments.ts +16 -19
  142. package/src/modes/components/status-line/types.ts +6 -2
  143. package/src/modes/components/status-line.ts +281 -117
  144. package/src/modes/components/tree-selector.ts +28 -8
  145. package/src/modes/components/two-column-body.ts +36 -0
  146. package/src/modes/components/welcome.ts +165 -30
  147. package/src/modes/controllers/command-controller.ts +26 -2
  148. package/src/modes/controllers/event-controller.ts +102 -4
  149. package/src/modes/controllers/extension-ui-controller.ts +21 -2
  150. package/src/modes/controllers/input-controller.ts +214 -47
  151. package/src/modes/controllers/selector-controller.ts +35 -33
  152. package/src/modes/interactive-mode.ts +36 -30
  153. package/src/modes/prompt-action-autocomplete.ts +48 -8
  154. package/src/modes/rpc/rpc-types.ts +1 -0
  155. package/src/modes/shared/agent-wire/deep-interview-gate.ts +72 -15
  156. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +20 -0
  157. package/src/modes/slash-command-visibility.ts +9 -0
  158. package/src/modes/theme/theme.ts +5 -1
  159. package/src/modes/tmux-scroll.ts +92 -0
  160. package/src/modes/types.ts +8 -1
  161. package/src/modes/utils/hotkeys-markdown.ts +5 -2
  162. package/src/modes/utils/terminal-bell.ts +48 -0
  163. package/src/modes/utils/ui-helpers.ts +76 -3
  164. package/src/notifications/daemon-paths.ts +22 -0
  165. package/src/notifications/html-format.ts +75 -1
  166. package/src/notifications/index.ts +32 -2
  167. package/src/notifications/lifecycle-control-runtime.ts +12 -3
  168. package/src/notifications/recent-activity.ts +32 -8
  169. package/src/notifications/telegram-daemon-cli.ts +125 -13
  170. package/src/notifications/telegram-daemon-control.ts +9 -0
  171. package/src/notifications/telegram-daemon.ts +88 -46
  172. package/src/notifications/telegram-reference.ts +36 -14
  173. package/src/notifications/threaded-render.ts +14 -6
  174. package/src/prompts/agents/architect.md +7 -4
  175. package/src/prompts/agents/critic.md +15 -6
  176. package/src/prompts/agents/planner.md +8 -3
  177. package/src/prompts/system/system-prompt.md +20 -2
  178. package/src/prompts/tools/bash.md +1 -1
  179. package/src/runtime-mcp/config-writer.ts +28 -0
  180. package/src/runtime-mcp/config.ts +7 -0
  181. package/src/runtime-mcp/loader.ts +3 -0
  182. package/src/runtime-mcp/manager.ts +3 -0
  183. package/src/runtime-mcp/types.ts +6 -0
  184. package/src/sdk.ts +10 -11
  185. package/src/session/agent-session.ts +103 -13
  186. package/src/session/session-manager.ts +58 -18
  187. package/src/skc-runtime/launch-tmux.ts +34 -8
  188. package/src/skc-runtime/launch-worktree.ts +16 -2
  189. package/src/skc-runtime/ralplan-runtime.ts +22 -6
  190. package/src/skc-runtime/restricted-role-agent-bash.ts +1 -0
  191. package/src/skc-runtime/session-state-sidecar.ts +167 -24
  192. package/src/skc-runtime/state-runtime.ts +8 -5
  193. package/src/skc-runtime/state-writer.ts +5 -5
  194. package/src/skc-runtime/team-runtime.ts +21 -6
  195. package/src/skc-runtime/tmux-common.ts +16 -0
  196. package/src/skc-runtime/tmux-sessions.ts +13 -9
  197. package/src/skc-runtime/ultragoal-guard.ts +150 -24
  198. package/src/skc-runtime/ultragoal-runtime.ts +16 -124
  199. package/src/skc-runtime/workflow-manifest.generated.json +0 -8
  200. package/src/skc-runtime/workflow-manifest.ts +0 -1
  201. package/src/slash-commands/builtin-registry.ts +136 -23
  202. package/src/slash-commands/types.ts +2 -0
  203. package/src/system-prompt.ts +8 -0
  204. package/src/task/executor.ts +5 -0
  205. package/src/tools/ast-grep.ts +10 -7
  206. package/src/tools/bash.ts +39 -14
  207. package/src/tools/index.ts +8 -1
  208. package/src/tools/skill.ts +1 -1
  209. package/src/utils/cmux-workspace.ts +233 -0
  210. package/src/utils/title-generator.ts +2 -0
  211. package/src/web/kagi.ts +1 -1
  212. package/src/web/parallel.ts +2 -2
  213. package/src/web/search/index.ts +137 -52
  214. package/src/web/search/provider.ts +80 -0
  215. package/src/web/search/providers/anthropic.ts +1 -1
  216. package/src/web/search/providers/brave.ts +1 -1
  217. package/src/web/search/providers/codex.ts +1 -1
  218. package/src/web/search/providers/duckduckgo.ts +1 -1
  219. package/src/web/search/providers/exa.ts +1 -1
  220. package/src/web/search/providers/gemini.ts +7 -3
  221. package/src/web/search/providers/jina.ts +1 -1
  222. package/src/web/search/providers/kimi.ts +6 -1
  223. package/src/web/search/providers/openai-compatible.ts +1 -1
  224. package/src/web/search/providers/parallel.ts +1 -1
  225. package/src/web/search/providers/perplexity.ts +2 -2
  226. package/src/web/search/providers/searxng.ts +1 -1
  227. package/src/web/search/providers/synthetic.ts +1 -1
  228. package/src/web/search/providers/tavily.ts +1 -1
  229. package/src/web/search/providers/utils.ts +67 -17
  230. package/src/web/search/providers/xai.ts +1 -1
  231. package/src/web/search/providers/zai.ts +1 -1
  232. package/src/web/search/render.ts +3 -0
  233. package/src/workflow/workflow-intent-diff.ts +199 -0
  234. package/vendor/insane-search/MANIFEST.json +1 -3
  235. package/vendor/insane-search/engine/tests/test_u1.py +0 -28
  236. package/vendor/insane-search/engine/transport.py +2 -3
  237. package/vendor/insane-search/engine/validators.py +0 -14
  238. package/src/defaults/skc/rules/ponytail.md +0 -68
@@ -12,7 +12,12 @@ import {
12
12
  SKC_MODEL_ASSIGNMENT_TARGETS,
13
13
  type SkcModelAssignmentTargetId,
14
14
  } from "../config/model-registry";
15
- import { extractExplicitThinkingSelector, formatModelSelectorValue, parseModelPattern } from "../config/model-resolver";
15
+ import {
16
+ extractExplicitThinkingSelector,
17
+ formatModelSelectorValue,
18
+ parseModelPattern,
19
+ parseModelString,
20
+ } from "../config/model-resolver";
16
21
  import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../discovery/helpers.js";
17
22
  import { td } from "../i18n";
18
23
  import { resolveMemoryBackend } from "../memory-backend";
@@ -170,11 +175,32 @@ function splitExplicitThinkingSelector(selector: string): { baseSelector: string
170
175
  return thinkingLevel ? { baseSelector: trimmed.slice(0, colonIndex), thinkingLevel } : { baseSelector: trimmed };
171
176
  }
172
177
 
173
- function resolveModelCommandSelection(
178
+ interface ModelCommandSelection {
179
+ model: Model;
180
+ selector: string;
181
+ thinkingLevel?: ThinkingLevel;
182
+ }
183
+
184
+ interface ModelCommandResolutionFailure {
185
+ message: string;
186
+ }
187
+
188
+ type ModelCommandResolution =
189
+ | { ok: true; selection: ModelCommandSelection }
190
+ | { ok: false; failure: ModelCommandResolutionFailure };
191
+
192
+ function parseProviderQualifiedSelector(selector: string): { provider: string; modelId: string } | undefined {
193
+ const splitSelector = splitExplicitThinkingSelector(selector);
194
+ const parsed = parseModelString(splitSelector.baseSelector);
195
+ if (!parsed) return undefined;
196
+ return { provider: parsed.provider, modelId: parsed.id };
197
+ }
198
+
199
+ function resolveModelCommandSelectionFromAvailable(
174
200
  runtime: SlashCommandRuntime,
175
201
  selector: string,
176
- ): { model: Model; selector: string; thinkingLevel?: ThinkingLevel } | undefined {
177
- const availableModels = runtime.session.getAvailableModels?.() ?? [];
202
+ availableModels: Model[],
203
+ ): ModelCommandSelection | undefined {
178
204
  const matchPreferences = { usageOrder: runtime.settings.getStorage()?.getModelUsageOrder() };
179
205
  const resolved = parseModelPattern(selector, availableModels, matchPreferences, {
180
206
  modelRegistry: runtime.session.modelRegistry,
@@ -199,6 +225,78 @@ function resolveModelCommandSelection(
199
225
  };
200
226
  }
201
227
 
228
+ function formatDiscoverableProviderFailure(
229
+ selector: string,
230
+ provider: string,
231
+ modelId: string,
232
+ runtime: SlashCommandRuntime,
233
+ ): string {
234
+ const state = runtime.session.modelRegistry.getProviderDiscoveryState?.(provider);
235
+ const discovered = state?.models ?? [];
236
+ const base = `Unknown model: ${selector}.`;
237
+ if (!modelId.trim()) {
238
+ return `${base} Local provider model selectors must use provider/model-id syntax with a non-empty model id.`;
239
+ }
240
+ if (!state) {
241
+ return `${base} Provider ${provider} is configured for discovery but has not reported models yet.`;
242
+ }
243
+ if (state.status === "unavailable") {
244
+ const details = state.error ? ` (${state.error})` : "";
245
+ return `${base} Provider ${provider} discovery is unavailable${details}. Check the local endpoint and run /model again.`;
246
+ }
247
+ if (state.status === "unauthenticated") {
248
+ return `${base} Provider ${provider} requires authentication before model discovery.`;
249
+ }
250
+ if (state.status === "empty") {
251
+ return `${base} Provider ${provider} discovery succeeded but returned no models.`;
252
+ }
253
+ if (discovered.length > 0) {
254
+ const preview = discovered.slice(0, 8).join(", ");
255
+ const suffix = discovered.length > 8 ? ", …" : "";
256
+ return `${base} Provider ${provider} did not report model ${modelId}. Available local models: ${preview}${suffix}.`;
257
+ }
258
+ return `${base} Provider ${provider} did not report model ${modelId}.`;
259
+ }
260
+
261
+ async function resolveModelCommandSelection(
262
+ runtime: SlashCommandRuntime,
263
+ selector: string,
264
+ ): Promise<ModelCommandResolution> {
265
+ let availableModels = runtime.session.getAvailableModels?.() ?? [];
266
+ const initialSelection = resolveModelCommandSelectionFromAvailable(runtime, selector, availableModels as Model[]);
267
+ if (initialSelection) {
268
+ return { ok: true, selection: initialSelection };
269
+ }
270
+
271
+ const providerRef = parseProviderQualifiedSelector(selector);
272
+ const discoverableProviders = runtime.session.modelRegistry?.getDiscoverableProviders?.() ?? [];
273
+ if (providerRef && discoverableProviders.includes(providerRef.provider)) {
274
+ await runtime.session.modelRegistry.refreshProvider?.(providerRef.provider, "online");
275
+ availableModels = runtime.session.getAvailableModels?.() ?? [];
276
+ const refreshedSelection = resolveModelCommandSelectionFromAvailable(
277
+ runtime,
278
+ selector,
279
+ availableModels as Model[],
280
+ );
281
+ if (refreshedSelection) {
282
+ return { ok: true, selection: refreshedSelection };
283
+ }
284
+ return {
285
+ ok: false,
286
+ failure: {
287
+ message: formatDiscoverableProviderFailure(selector, providerRef.provider, providerRef.modelId, runtime),
288
+ },
289
+ };
290
+ }
291
+
292
+ return {
293
+ ok: false,
294
+ failure: {
295
+ message: `Unknown model: ${selector}. Configure or login to a provider first, then list/select models with /model.`,
296
+ },
297
+ };
298
+ }
299
+
202
300
  function modelSelectionUsage(runtime: SlashCommandRuntime, currentModelLine?: string): string {
203
301
  return [
204
302
  currentModelLine,
@@ -225,7 +323,8 @@ const shutdownHandlerTui = (_command: ParsedSlashCommand, runtime: TuiSlashComma
225
323
  const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
226
324
  {
227
325
  name: "settings",
228
- description: "Open settings menu",
326
+ priority: 40,
327
+ description: "Open settings and preferences",
229
328
  handleTui: (_command, runtime) => {
230
329
  runtime.ctx.showSettingsSelector();
231
330
  runtime.ctx.editor.setText("");
@@ -241,7 +340,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
241
340
  },
242
341
  {
243
342
  name: "goal",
244
- description: "Toggle goal mode (persistent autonomous objective for this session)",
343
+ priority: 84,
344
+ description: "Plan and track an autonomous goal",
245
345
  subcommands: [
246
346
  { name: "set", description: "Set or replace the goal", usage: "<objective>" },
247
347
  { name: "show", description: "Show current goal details" },
@@ -283,16 +383,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
283
383
  runtime,
284
384
  );
285
385
  }
286
- const selection = resolveModelCommandSelection(runtime, modelId);
287
- if (!selection) {
288
- return usage(
289
- modelSelectionUsage(
290
- runtime,
291
- `Unknown model: ${modelId}. Configure or login to a provider first, then list/select models with /model.`,
292
- ),
293
- runtime,
294
- );
386
+ const resolution = await resolveModelCommandSelection(runtime, modelId);
387
+ if (!resolution.ok) {
388
+ return usage(modelSelectionUsage(runtime, resolution.failure.message), runtime);
295
389
  }
390
+ const { selection } = resolution;
296
391
  try {
297
392
  const persistedSelector = formatModelSelectorValue(selection.selector, selection.thinkingLevel);
298
393
  if (parsedArgs.targetId === "default") {
@@ -449,7 +544,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
449
544
  },
450
545
  {
451
546
  name: "export",
452
- description: "Export session to HTML file",
547
+ priority: 50,
548
+ description: "Export this session to an HTML file",
453
549
  inlineHint: "[path]",
454
550
  allowArgs: true,
455
551
  handle: async (command, runtime) => {
@@ -476,7 +572,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
476
572
  },
477
573
  {
478
574
  name: "copy",
479
- description: "Copy last response as markdown",
575
+ priority: 55,
576
+ description: "Copy the last response for review or sharing",
480
577
  // Public `/copy` is strict zero-argument, but `allowArgs` lets the
481
578
  // TUI dispatcher route `/copy <arg>` here so it can be rejected locally
482
579
  // instead of falling through as a model prompt.
@@ -493,7 +590,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
493
590
  },
494
591
  {
495
592
  name: "dump",
496
- description: "Copy session transcript to clipboard",
593
+ priority: 54,
594
+ description: "Dump the full transcript for review or sharing",
497
595
  acpDescription: "Return full transcript as plain text",
498
596
  handle: async (_command, runtime) => {
499
597
  const text = runtime.session.formatSessionAsText();
@@ -507,11 +605,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
507
605
  },
508
606
  {
509
607
  name: "session",
510
- description: "Session management commands",
608
+ priority: 88,
609
+ description: "Show current session info or delete current session",
511
610
  acpDescription: "Show session information",
512
611
  acpInputHint: "info|delete",
513
612
  subcommands: [
514
- { name: "info", description: "Show session info and stats" },
613
+ { name: "info", description: "Show current session id, title, and workspace" },
515
614
  { name: "delete", description: "Delete current session and return to selector" },
516
615
  ],
517
616
  allowArgs: true,
@@ -621,6 +720,15 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
621
720
  runtime.ctx.editor.setText("");
622
721
  },
623
722
  },
723
+ {
724
+ name: "help",
725
+ priority: 100,
726
+ description: "Learn commands and beginner workflows",
727
+ handleTui: (_command, runtime) => {
728
+ runtime.ctx.handleHelpCommand();
729
+ runtime.ctx.editor.setText("");
730
+ },
731
+ },
624
732
  {
625
733
  name: "hotkeys",
626
734
  description: "Show all keyboard shortcuts",
@@ -861,6 +969,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
861
969
  },
862
970
  {
863
971
  name: "new",
972
+ priority: 96,
864
973
  description: "Start a new session",
865
974
  handleTui: async (_command, runtime) => {
866
975
  runtime.ctx.editor.setText("");
@@ -877,7 +986,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
877
986
  },
878
987
  {
879
988
  name: "compact",
880
- description: "Manually compact the session context",
989
+ priority: 72,
990
+ description: "Compact context and continue this session",
881
991
  acpDescription: "Compact the conversation",
882
992
  inlineHint: "[focus instructions]",
883
993
  allowArgs: true,
@@ -934,7 +1044,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
934
1044
  },
935
1045
  {
936
1046
  name: "resume",
937
- description: "Resume a different session",
1047
+ priority: 92,
1048
+ description: "Resume a previous session",
938
1049
  handleTui: (_command, runtime) => {
939
1050
  runtime.ctx.showSessionSelector();
940
1051
  runtime.ctx.editor.setText("");
@@ -953,7 +1064,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
953
1064
  },
954
1065
  {
955
1066
  name: "retry",
956
- description: "Retry the last failed agent turn",
1067
+ priority: 70,
1068
+ description: "Retry or continue the last interrupted turn",
957
1069
  handleTui: async (_command, runtime) => {
958
1070
  const didRetry = await runtime.ctx.session.retry();
959
1071
  if (!didRetry) {
@@ -1153,6 +1265,7 @@ export const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = AC
1153
1265
  },
1154
1266
  subcommands: command.subcommands,
1155
1267
  inlineHint: command.inlineHint,
1268
+ priority: command.priority,
1156
1269
  }),
1157
1270
  );
1158
1271
 
@@ -19,6 +19,8 @@ export interface BuiltinSlashCommand {
19
19
  subcommands?: SubcommandDef[];
20
20
  /** Static inline hint when command takes a simple argument (no subcommands). */
21
21
  inlineHint?: string;
22
+ /** Higher values sort earlier in TUI slash-command autocomplete. */
23
+ priority?: number;
22
24
  }
23
25
 
24
26
  /** Parsed slash-command text after stripping the leading "/". */
@@ -356,6 +356,10 @@ export interface BuildSystemPromptOptions {
356
356
  mcpDiscoveryMode?: boolean;
357
357
  /** Discoverable MCP server summaries to advertise when discovery mode is active. */
358
358
  mcpDiscoveryServerSummaries?: string[];
359
+ /** Whether built-in tool discovery ("all" mode) is active; enables the tool-discovery prompt block. */
360
+ toolDiscoveryActive?: boolean;
361
+ /** Hidden discoverable tools to advertise (name + one-line summary) when tool discovery is active. */
362
+ discoverableTools?: Array<{ name: string; summary: string }>;
359
363
  /** Encourage the agent to delegate via tasks unless changes are trivial. */
360
364
  eagerTasks?: boolean;
361
365
  /** Rules with alwaysApply=true — their full content is injected into the prompt. */
@@ -394,6 +398,8 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
394
398
  intentField,
395
399
  mcpDiscoveryMode = false,
396
400
  mcpDiscoveryServerSummaries = [],
401
+ toolDiscoveryActive = false,
402
+ discoverableTools = [],
397
403
  eagerTasks = false,
398
404
  secretsEnabled = false,
399
405
  workspaceTree: providedWorkspaceTree,
@@ -572,6 +578,8 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
572
578
  mcpDiscoveryMode,
573
579
  hasMCPDiscoveryServers: mcpDiscoveryServerSummaries.length > 0,
574
580
  mcpDiscoveryServerSummaries,
581
+ toolDiscoveryActive,
582
+ discoverableTools,
575
583
  eagerTasks,
576
584
  secretsEnabled,
577
585
  };
@@ -1535,6 +1535,11 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1535
1535
  );
1536
1536
  await awaitAbortable(session.waitForIdle());
1537
1537
  } catch (err) {
1538
+ // An abort (parent cancel, session shutdown, timeout) rejects
1539
+ // awaitAbortable with ToolAbortError — a cancellation, not a prompt
1540
+ // failure. Mirror the outer catch's `!abortSignal.aborted` guard so
1541
+ // expected cancellations aren't logged at error level.
1542
+ if (abortSignal.aborted || err instanceof ToolAbortError) break;
1538
1543
  logger.error("Subagent prompt failed", {
1539
1544
  error: err instanceof Error ? err.message : String(err),
1540
1545
  });
@@ -223,13 +223,16 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
223
223
  const modelOut: string[] = [];
224
224
  const displayOut: string[] = [];
225
225
  const fileMatches = matchesByFile.get(relativePath) ?? [];
226
- const lineNumberWidth = fileMatches.reduce((width, match) => {
227
- const lineCount = match.text.split("\n").length;
228
- const endLine = match.startLine + lineCount - 1;
229
- return Math.max(width, String(match.startLine).length, String(endLine).length);
230
- }, 0);
231
- for (const match of fileMatches) {
232
- const matchLines = match.text.split("\n");
226
+ const preparedMatches = fileMatches.map(match => {
227
+ const lines = match.text.split("\n");
228
+ const endLine = match.startLine + lines.length - 1;
229
+ return { match, lines, endLine };
230
+ });
231
+ const lineNumberWidth = preparedMatches.reduce(
232
+ (width, entry) => Math.max(width, String(entry.match.startLine).length, String(entry.endLine).length),
233
+ 0,
234
+ );
235
+ for (const { match, lines: matchLines } of preparedMatches) {
233
236
  for (let index = 0; index < matchLines.length; index++) {
234
237
  const lineNumber = match.startLine + index;
235
238
  const isMatch = index === 0;
package/src/tools/bash.ts CHANGED
@@ -14,7 +14,10 @@ import bashDescription from "../prompts/tools/bash.md" with { type: "text" };
14
14
  import type { ClientBridgeTerminalExitStatus, ClientBridgeTerminalOutput } from "../session/client-bridge";
15
15
  import { DEFAULT_MAX_BYTES, streamTailUpdates, TailBuffer } from "../session/streaming-output";
16
16
  import { buildSkcRuntimeSessionEnv } from "../skc-runtime/goal-mode-request";
17
- import { SKC_RESTRICTED_ROLE_AGENT_BASH_ENV } from "../skc-runtime/restricted-role-agent-bash";
17
+ import {
18
+ SKC_RALPLAN_ARTIFACT_ENV,
19
+ SKC_RESTRICTED_ROLE_AGENT_BASH_ENV,
20
+ } from "../skc-runtime/restricted-role-agent-bash";
18
21
  import { renderStatusLine } from "../tui";
19
22
  import { CachedOutputBlock } from "../tui/output-block";
20
23
  import { getSixelLineMask } from "../utils/sixel";
@@ -530,13 +533,24 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
530
533
 
531
534
  const rawCommand = input.command;
532
535
  const allowedPrefixes = this.session.bashAllowedPrefixes;
536
+ const isRestrictedRalplanArtifactEnv =
537
+ allowedPrefixes &&
538
+ allowedPrefixes.length > 0 &&
539
+ this.session.bashRestrictionProfile !== "read-only" &&
540
+ env &&
541
+ Object.keys(env).length === 1 &&
542
+ Object.hasOwn(env, SKC_RALPLAN_ARTIFACT_ENV) &&
543
+ rawCommand.includes(`--artifact-env ${SKC_RALPLAN_ARTIFACT_ENV}`);
533
544
  if (
534
545
  (this.session.bashRestrictionProfile === "read-only" || (allowedPrefixes && allowedPrefixes.length > 0)) &&
535
546
  env &&
536
- Object.keys(env).length > 0
547
+ Object.keys(env).length > 0 &&
548
+ !isRestrictedRalplanArtifactEnv
537
549
  ) {
538
550
  const mode = this.session.bashRestrictionProfile === "read-only" ? "Read-only" : "Restricted role-agent";
539
- throw new ToolError(`${mode} bash does not allow per-command env overrides.`);
551
+ throw new ToolError(
552
+ `${mode} bash only allows the ${SKC_RALPLAN_ARTIFACT_ENV} env override for --artifact-env.`,
553
+ );
540
554
  }
541
555
  if (allowedPrefixes && allowedPrefixes.length > 0) {
542
556
  const commandsToCheck = rawCommand === command ? [command] : [rawCommand, command];
@@ -597,11 +611,13 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
597
611
  await Promise.all(
598
612
  Object.entries(env).map(async ([key, value]) => [
599
613
  key,
600
- await expandInternalUrls(value, {
601
- ...internalUrlOptions,
602
- ensureLocalParentDirs: true,
603
- noEscape: true,
604
- }),
614
+ key === SKC_RALPLAN_ARTIFACT_ENV
615
+ ? value
616
+ : await expandInternalUrls(value, {
617
+ ...internalUrlOptions,
618
+ ensureLocalParentDirs: true,
619
+ noEscape: true,
620
+ }),
605
621
  ]),
606
622
  ),
607
623
  )
@@ -817,9 +833,22 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
817
833
  });
818
834
  }
819
835
 
836
+ // Route through the client terminal when the client advertises the terminal capability.
837
+ // Skip when pty=true (PTY needs the local terminal UI).
838
+ const clientBridge =
839
+ this.session.bashRestrictionProfile === "read-only" ? undefined : this.session.getClientBridge?.();
840
+ const clientTerminalActive = Boolean(clientBridge?.capabilities.terminal && clientBridge.createTerminal && !pty);
841
+
820
842
  const autoBgManager = AsyncJobManager.instance();
821
- if (this.#autoBackgroundEnabled && !pty && autoBgManager) {
822
- const autoBackgroundWaitMs = this.#resolveAutoBackgroundWaitMs(timeoutMs);
843
+ // Run non-PTY bash through the managed job path so Ctrl+B-twice fold-on-demand works
844
+ // even when auto-background is disabled. When a client terminal will handle the
845
+ // command, keep the existing bridge path unless auto-background is enabled.
846
+ if (!pty && autoBgManager && (this.#autoBackgroundEnabled || !clientTerminalActive)) {
847
+ // With auto-background off, wait past the command's own timeout so the job only
848
+ // leaves the foreground on an explicit Ctrl+B fold, never on an auto-background timer.
849
+ const autoBackgroundWaitMs = this.#autoBackgroundEnabled
850
+ ? this.#resolveAutoBackgroundWaitMs(timeoutMs)
851
+ : timeoutMs + 1_000;
823
852
  const startBackgrounded = autoBackgroundWaitMs === 0;
824
853
  const job = this.#startManagedBashJob({
825
854
  command,
@@ -875,10 +904,6 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
875
904
  });
876
905
  }
877
906
 
878
- // Route through the client terminal when the client advertises the terminal capability.
879
- // Skip when pty=true (PTY needs the local terminal UI).
880
- const clientBridge =
881
- this.session.bashRestrictionProfile === "read-only" ? undefined : this.session.getClientBridge?.();
882
907
  if (clientBridge?.capabilities.terminal && clientBridge.createTerminal && !pty) {
883
908
  const handle = await clientBridge.createTerminal({
884
909
  command,
@@ -325,7 +325,14 @@ export type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool |
325
325
  export type BuiltinToolLoadMode = "essential" | "discoverable";
326
326
 
327
327
  /** Default essential tool names when tools.essentialOverride is empty. */
328
- export const DEFAULT_ESSENTIAL_TOOL_NAMES: readonly string[] = ["read", "bash", "edit"] as const;
328
+ export const DEFAULT_ESSENTIAL_TOOL_NAMES: readonly string[] = [
329
+ "read",
330
+ "bash",
331
+ "edit",
332
+ "write",
333
+ "search",
334
+ "find",
335
+ ] as const;
329
336
 
330
337
  /**
331
338
  * Resolve the active essential built-in tool names from settings.
@@ -50,7 +50,7 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
50
50
  readonly name = "skill";
51
51
  readonly label = "Skill";
52
52
  readonly summary = "Chain into another available skill in the current turn";
53
- readonly loadMode = "discoverable";
53
+ readonly loadMode = "essential";
54
54
  readonly description: string;
55
55
  readonly parameters = skillSchema;
56
56
  readonly strict = true;