@gajae-code/coding-agent 0.12.7 → 0.12.10

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 (117) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/types/cli/notify-cli.d.ts +1 -0
  3. package/dist/types/commands/notify.d.ts +4 -0
  4. package/dist/types/commands/sdk.d.ts +23 -0
  5. package/dist/types/config/file-lock.d.ts +3 -0
  6. package/dist/types/config/model-discovery-manager.d.ts +6 -1
  7. package/dist/types/config/model-registry.d.ts +2 -0
  8. package/dist/types/config/resolve-config-value.d.ts +2 -1
  9. package/dist/types/config/settings-schema.d.ts +17 -1
  10. package/dist/types/discovery/agents-md.d.ts +11 -1
  11. package/dist/types/main.d.ts +1 -0
  12. package/dist/types/modes/components/hook-selector.d.ts +20 -0
  13. package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
  14. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  15. package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
  16. package/dist/types/modes/types.d.ts +6 -0
  17. package/dist/types/sdk/broker/broker.d.ts +5 -1
  18. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
  19. package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
  20. package/dist/types/sdk/bus/config.d.ts +75 -113
  21. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  22. package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
  23. package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
  24. package/dist/types/sdk/bus/index.d.ts +1 -1
  25. package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
  26. package/dist/types/sdk/bus/notification-service.d.ts +54 -4
  27. package/dist/types/sdk/bus/session-control.d.ts +8 -3
  28. package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
  29. package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
  30. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +8 -1
  31. package/dist/types/sdk/client/discovery.d.ts +2 -1
  32. package/dist/types/sdk/host/query/handlers.d.ts +2 -0
  33. package/dist/types/sdk/index.d.ts +1 -0
  34. package/dist/types/sdk/providers.d.ts +22 -0
  35. package/dist/types/sdk/session-directory.d.ts +1 -1
  36. package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
  37. package/dist/types/session/internal/managed-session-storage.d.ts +19 -1
  38. package/dist/types/session/internal/native-publish-outcome.d.ts +3 -2
  39. package/dist/types/session/internal/session-open-errors.d.ts +5 -0
  40. package/dist/types/session/session-manager.d.ts +3 -1
  41. package/dist/types/session/session-storage.d.ts +2 -0
  42. package/dist/types/system-prompt.d.ts +12 -1
  43. package/dist/types/tools/composer-bash-policy.d.ts +9 -1
  44. package/package.json +10 -8
  45. package/scripts/build-sdk-package-smoke.ts +51 -0
  46. package/scripts/capture-notifications-settings-showcase.ts +1 -1
  47. package/scripts/verify-gjc-sdk-canonicalization.ts +14 -1
  48. package/src/cli/notify-cli.ts +215 -99
  49. package/src/commands/notify.ts +19 -8
  50. package/src/commands/sdk.ts +54 -0
  51. package/src/config/file-lock.ts +6 -0
  52. package/src/config/model-discovery-manager.ts +50 -8
  53. package/src/config/model-profiles.ts +8 -0
  54. package/src/config/model-registry.ts +866 -76
  55. package/src/config/resolve-config-value.ts +10 -8
  56. package/src/config/settings-schema.ts +25 -15
  57. package/src/config/settings.ts +15 -15
  58. package/src/discovery/agents-md.ts +76 -19
  59. package/src/gjc-runtime/psmux-detect.ts +12 -16
  60. package/src/gjc-runtime/session-state-sidecar.ts +12 -2
  61. package/src/gjc-runtime/tmux-sessions.ts +8 -1
  62. package/src/internal-urls/docs-index.generated.ts +10 -9
  63. package/src/main.ts +43 -7
  64. package/src/modes/DESIGN.md +11 -11
  65. package/src/modes/components/extensions/inspector-panel.ts +9 -5
  66. package/src/modes/components/hook-selector.ts +155 -44
  67. package/src/modes/components/model-selector.ts +242 -49
  68. package/src/modes/components/notifications-settings-editor.ts +631 -85
  69. package/src/modes/controllers/extension-ui-controller.ts +114 -15
  70. package/src/modes/controllers/selector-controller.ts +525 -93
  71. package/src/modes/print-mode.ts +3 -0
  72. package/src/modes/types.ts +6 -0
  73. package/src/prompts/system/system-prompt.md +1 -0
  74. package/src/sdk/broker/broker.ts +54 -10
  75. package/src/sdk/broker/lifecycle-ledger.ts +34 -1
  76. package/src/sdk/broker/lifecycle.ts +47 -50
  77. package/src/sdk/bus/chat-daemon-cli.ts +12 -5
  78. package/src/sdk/bus/chat-daemon-control.ts +35 -6
  79. package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
  80. package/src/sdk/bus/config.ts +534 -286
  81. package/src/sdk/bus/daemon-paths.ts +2 -0
  82. package/src/sdk/bus/discord-live-provider.ts +57 -2
  83. package/src/sdk/bus/discord-provider.ts +21 -0
  84. package/src/sdk/bus/index.ts +97 -35
  85. package/src/sdk/bus/notification-orchestration.ts +371 -68
  86. package/src/sdk/bus/notification-service.ts +571 -137
  87. package/src/sdk/bus/session-control.ts +119 -44
  88. package/src/sdk/bus/slack-live-provider.ts +78 -2
  89. package/src/sdk/bus/slack-provider.ts +24 -0
  90. package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
  91. package/src/sdk/bus/telegram-daemon-contract.ts +8 -1
  92. package/src/sdk/bus/telegram-daemon-control.ts +3 -3
  93. package/src/sdk/bus/telegram-daemon.ts +23 -15
  94. package/src/sdk/client/discovery.ts +10 -4
  95. package/src/sdk/host/query/handlers.ts +20 -1
  96. package/src/sdk/index.ts +1 -0
  97. package/src/sdk/protocol/operation-inventory.generated.json +18 -0
  98. package/src/sdk/protocol/operation-registry.ts +5 -2
  99. package/src/sdk/providers.ts +51 -0
  100. package/src/sdk/session-directory.ts +1 -0
  101. package/src/sdk/session.ts +34 -14
  102. package/src/session/agent-session.ts +109 -37
  103. package/src/session/internal/managed-session-scope.ts +981 -450
  104. package/src/session/internal/managed-session-storage.ts +418 -47
  105. package/src/session/internal/native-publish-outcome.ts +15 -3
  106. package/src/session/internal/session-open-errors.ts +11 -0
  107. package/src/session/session-manager.ts +27 -3
  108. package/src/session/session-storage.ts +72 -5
  109. package/src/slash-commands/builtin-registry.ts +117 -15
  110. package/src/system-prompt.ts +28 -15
  111. package/src/task/index.ts +9 -3
  112. package/src/tools/ask.ts +1 -1
  113. package/src/tools/bash.ts +4 -2
  114. package/src/tools/composer-bash-policy.ts +21 -5
  115. package/src/tools/telegram-send.ts +4 -2
  116. package/src/utils/command-args.ts +19 -30
  117. package/src/utils/lang-from-path.ts +2 -4
@@ -245,10 +245,14 @@ function dedupeExactContextFiles(
245
245
  // Keep the closest matching context entry when content is byte-for-byte identical.
246
246
  lastIndexByContent.set(file.content, index);
247
247
  }
248
-
249
248
  return contextFiles.filter((file, index) => lastIndexByContent.get(file.content) === index);
250
249
  }
251
250
 
251
+ export interface ProjectContextFilesResult {
252
+ contextFiles: Array<{ path: string; content: string; depth?: number }>;
253
+ warnings: string[];
254
+ }
255
+
252
256
  /**
253
257
  * Load all context files using the capability API.
254
258
  * Returns {path, content, depth} entries for all discovered context files.
@@ -257,11 +261,10 @@ function dedupeExactContextFiles(
257
261
  * prominent. User-home files from foreign providers (`~/.claude/CLAUDE.md`,
258
262
  * `~/.codex/AGENTS.md`, …) stay excluded — only gjc's own user config applies.
259
263
  */
260
- export async function loadProjectContextFiles(
264
+ export async function loadProjectContextFilesResult(
261
265
  options: LoadContextFilesOptions = {},
262
- ): Promise<Array<{ path: string; content: string; depth?: number }>> {
266
+ ): Promise<ProjectContextFilesResult> {
263
267
  const resolvedCwd = options.cwd ?? getProjectDir();
264
-
265
268
  const result = await loadCapability(contextFileCapability.id, { cwd: resolvedCwd });
266
269
  const items = result.items as ContextFile[];
267
270
 
@@ -288,7 +291,17 @@ export async function loadProjectContextFiles(
288
291
  return depthB - depthA;
289
292
  });
290
293
 
291
- return dedupeExactContextFiles([...userFiles, ...projectFiles]);
294
+ return {
295
+ contextFiles: dedupeExactContextFiles([...userFiles, ...projectFiles]),
296
+ warnings: result.warnings,
297
+ };
298
+ }
299
+
300
+ /** Load project context files without exposing discovery diagnostics. */
301
+ export async function loadProjectContextFiles(
302
+ options: LoadContextFilesOptions = {},
303
+ ): Promise<Array<{ path: string; content: string; depth?: number }>> {
304
+ return (await loadProjectContextFilesResult(options)).contextFiles;
292
305
  }
293
306
 
294
307
  /**
@@ -386,10 +399,11 @@ export interface BuildSystemPromptOptions {
386
399
  subagent?: boolean;
387
400
  }
388
401
 
389
- /** Result of building provider-facing system prompt messages. */
390
402
  export interface BuildSystemPromptResult {
391
403
  /** Ordered system prompt blocks. Providers should preserve entries as distinct messages/blocks. */
392
404
  systemPrompt: string[];
405
+ /** Context-file discovery warnings visible to SDK and session callers. */
406
+ warnings: string[];
393
407
  }
394
408
  export interface BuildVolatileProjectContextOptions {
395
409
  cwd?: string;
@@ -421,7 +435,7 @@ export function buildVolatileProjectContext(options: BuildVolatileProjectContext
421
435
  /** Build the system prompt with tools, guidelines, and context */
422
436
  export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}): Promise<BuildSystemPromptResult> {
423
437
  if ($env.NULL_PROMPT === "true") {
424
- return { systemPrompt: [] };
438
+ return { systemPrompt: [], warnings: [] };
425
439
  }
426
440
 
427
441
  const {
@@ -448,7 +462,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
448
462
  resolvedCustomPrompt: undefined as string | undefined,
449
463
  resolvedAppendPrompt: undefined as string | undefined,
450
464
  systemPromptCustomization: null as string | null,
451
- contextFiles: dedupeExactContextFiles(providedContextFiles ?? []),
465
+ contextFiles: { contextFiles: [] as Array<{ path: string; content: string; depth?: number }>, warnings: [] },
452
466
  workspaceTree: {
453
467
  rootPath: resolvedCwd,
454
468
  rendered: "",
@@ -490,8 +504,8 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
490
504
  cwd: resolvedCwd,
491
505
  });
492
506
  const contextFilesPromise = providedContextFiles
493
- ? Promise.resolve(providedContextFiles)
494
- : logger.time("loadProjectContextFiles", loadProjectContextFiles, { cwd: resolvedCwd });
507
+ ? Promise.resolve({ contextFiles: providedContextFiles, warnings: [] })
508
+ : logger.time("loadProjectContextFiles", loadProjectContextFilesResult, { cwd: resolvedCwd });
495
509
  const workspaceTreePromise =
496
510
  providedWorkspaceTree !== undefined
497
511
  ? Promise.resolve(providedWorkspaceTree)
@@ -499,7 +513,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
499
513
  buildWorkspaceTree(resolvedCwd, { timeoutMs: SYSTEM_PROMPT_PREP_TIMEOUT_MS }),
500
514
  );
501
515
 
502
- const [resolvedCustomPrompt, resolvedAppendPrompt, systemPromptCustomization, contextFiles, workspaceTree] =
516
+ const [resolvedCustomPrompt, resolvedAppendPrompt, systemPromptCustomization, contextFileResult, workspaceTree] =
503
517
  await Promise.all([
504
518
  withDeadline(
505
519
  "customPrompt",
@@ -516,11 +530,10 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
516
530
  systemPromptCustomizationPromise,
517
531
  prepDefaults.systemPromptCustomization,
518
532
  ),
519
- withDeadline("loadProjectContextFiles", contextFilesPromise, prepDefaults.contextFiles).then(
520
- dedupeExactContextFiles,
521
- ),
533
+ withDeadline("loadProjectContextFiles", contextFilesPromise, prepDefaults.contextFiles),
522
534
  withDeadline("buildWorkspaceTree", workspaceTreePromise, prepDefaults.workspaceTree),
523
535
  ]);
536
+ const contextFiles = dedupeExactContextFiles(contextFileResult.contextFiles);
524
537
  const agentsMdFiles = Array.from(new Set(workspaceTree.agentsMdFiles)).sort().slice(0, AGENTS_MD_LIMIT);
525
538
 
526
539
  if (timedOut.length > 0) {
@@ -628,5 +641,5 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
628
641
  systemPrompt.push(pluginAppendices.trim());
629
642
  }
630
643
 
631
- return { systemPrompt };
644
+ return { systemPrompt, warnings: contextFileResult.warnings };
632
645
  }
package/src/task/index.ts CHANGED
@@ -935,8 +935,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
935
935
  ): { ok: boolean; disposition?: DuplicateDisposition; error?: string; identity: DuplicateIdentity } => {
936
936
  const identity = duplicateIdentityForTask(task, params.agent, ownerId, parentSession);
937
937
  const key = duplicateIdentityKey(identity);
938
- const predecessors = manager
939
- .getSubagentRecords({ ownerId })
938
+ const predecessors = (manager.getSubagentRecords?.({ ownerId }) ?? [])
940
939
  .filter(record => {
941
940
  if (record.subagentId === selfSubagentId || record.subagentId === subagentId) return false;
942
941
  if (record.status !== "running" && record.status !== "paused" && record.status !== "queued")
@@ -1139,7 +1138,14 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
1139
1138
  }
1140
1139
  break;
1141
1140
  }
1142
- if (frozenForkSeed) frozenForkSeeds.set(uniqueId, frozenForkSeed);
1141
+ if (frozenForkSeed) {
1142
+ frozenForkSeeds.set(uniqueId, frozenForkSeed);
1143
+ // The async callback may execute through a compatibility path that retains
1144
+ // the caller's task id instead of the allocated artifact id. Preserve the
1145
+ // same immutable seed under both identities so detached execution never
1146
+ // rebuilds context after dispatch.
1147
+ frozenForkSeeds.set(taskItem.id, frozenForkSeed);
1148
+ }
1143
1149
  const singleParams: TaskParams = { ...params, tasks: [taskItem] };
1144
1150
  const label = uniqueId;
1145
1151
  try {
package/src/tools/ask.ts CHANGED
@@ -1613,7 +1613,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
1613
1613
  signal,
1614
1614
  initialSelection,
1615
1615
  navigation: options?.navigation,
1616
- scrollTitleRows: isDeepInterviewQuestion ? DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS : undefined,
1616
+ scrollTitleRows: DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS,
1617
1617
  otherOptionLabel,
1618
1618
  autoSelectOnTimeout: !intentContract(q.deepInterview) && !intentReview(q.deepInterview),
1619
1619
  clarificationOptionLabel,
package/src/tools/bash.ts CHANGED
@@ -201,7 +201,7 @@ function formatBashFailureMessage(
201
201
  const bodyText = removeTrailingFailureCause(text, statusCause);
202
202
  const suffixParts: string[] = [];
203
203
  const reference = artifactReferenceForResult(result);
204
- if (reference) suffixParts.push(reference);
204
+ if (reference && !artifactReferenceIsReachable(text, result)) suffixParts.push(reference);
205
205
  const writerNotice = artifactWriterFailureNotice(result);
206
206
  if (writerNotice) suffixParts.push(writerNotice);
207
207
  if (statusCause) suffixParts.push(statusCause);
@@ -982,8 +982,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
982
982
  }
983
983
  }
984
984
 
985
+ const activeModel = this.session.model;
985
986
  const composerPolicy = checkComposerBashPolicy({
986
- modelId: this.session.getActiveModelString?.() ?? this.session.getModelString?.() ?? this.session.model?.id,
987
+ modelId: this.session.getActiveModelString?.() ?? this.session.getModelString?.() ?? activeModel?.id,
988
+ provider: activeModel?.provider,
987
989
  commands: rawCommand === command ? [command] : [rawCommand, command],
988
990
  });
989
991
  if (!composerPolicy.allowed) {
@@ -1,7 +1,11 @@
1
- import { isComposerHarnessModel } from "@gajae-code/ai/providers/composer-discipline";
1
+ import {
2
+ type ComposerBashPolicyToolSurface,
3
+ formatComposerBashPolicyError,
4
+ isComposerHarnessModel,
5
+ } from "@gajae-code/ai/providers/composer-discipline";
2
6
 
3
- export const COMPOSER_BASH_POLICY_ERROR =
4
- "Composer bash policy blocked repository file I/O. Use find, search, read, and edit tools for file discovery, file inspection, and file mutation.";
7
+ export const COMPOSER_BASH_POLICY_ERROR = formatComposerBashPolicyError("generic");
8
+ export const CURSOR_COMPOSER_BASH_POLICY_ERROR = formatComposerBashPolicyError("cursor");
5
9
 
6
10
  type ComposerBashPolicyResult =
7
11
  | { allowed: true }
@@ -9,6 +13,7 @@ type ComposerBashPolicyResult =
9
13
  allowed: false;
10
14
  reason: string;
11
15
  message: string;
16
+ surface: ComposerBashPolicyToolSurface;
12
17
  };
13
18
 
14
19
  const BLOCK_PATTERNS: Array<{ id: string; pattern: RegExp }> = [
@@ -77,19 +82,30 @@ export function isComposerBashPolicyModel(modelId: string | undefined): boolean
77
82
  return Boolean(modelId && isComposerHarnessModel(modelId));
78
83
  }
79
84
 
85
+ export function resolveComposerBashPolicyToolSurface(input: {
86
+ modelId?: string;
87
+ provider?: string;
88
+ }): ComposerBashPolicyToolSurface {
89
+ if (input.provider?.toLowerCase() === "cursor") return "cursor";
90
+ return /(?:^|[/:._-])cursor(?:[/:._-]|$)/i.test(input.modelId ?? "") ? "cursor" : "generic";
91
+ }
92
+
80
93
  export function checkComposerBashPolicy(input: {
81
94
  modelId?: string;
95
+ provider?: string;
82
96
  commands: readonly string[];
83
97
  }): ComposerBashPolicyResult {
84
98
  if (!isComposerBashPolicyModel(input.modelId)) return { allowed: true };
99
+ const surface = resolveComposerBashPolicyToolSurface(input);
100
+ const message = formatComposerBashPolicyError(surface);
85
101
  for (const command of input.commands) {
86
102
  for (const block of BLOCK_PATTERNS) {
87
103
  if (block.pattern.test(command)) {
88
- return { allowed: false, reason: block.id, message: COMPOSER_BASH_POLICY_ERROR };
104
+ return { allowed: false, reason: block.id, message, surface };
89
105
  }
90
106
  }
91
107
  if (!isAllowedComposerTerminalCommand(command)) {
92
- return { allowed: false, reason: "not-allowlisted", message: COMPOSER_BASH_POLICY_ERROR };
108
+ return { allowed: false, reason: "not-allowlisted", message, surface };
93
109
  }
94
110
  }
95
111
  return { allowed: true };
@@ -3,7 +3,7 @@ import * as path from "node:path";
3
3
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
4
4
  import { z } from "zod/v4";
5
5
  import { getTelegramFileSink } from "../sdk/bus/attachment-registry";
6
- import { getNotificationConfig, isGloballyConfigured } from "../sdk/bus/config";
6
+ import { getNotificationConfig, isProviderEffectivelyEnabled } from "../sdk/bus/config";
7
7
  import type { ToolSession } from "./index";
8
8
 
9
9
  const TELEGRAM_SEND_MAX_FILE_BYTES = 50 * 1024 * 1024;
@@ -41,7 +41,9 @@ export class TelegramSendTool implements AgentTool<typeof telegramSendSchema, Te
41
41
  constructor(private readonly session: ToolSession) {}
42
42
 
43
43
  static createIf(session: ToolSession): TelegramSendTool | null {
44
- return isGloballyConfigured(getNotificationConfig(session.settings)) ? new TelegramSendTool(session) : null;
44
+ return isProviderEffectivelyEnabled(getNotificationConfig(session.settings), "telegram")
45
+ ? new TelegramSendTool(session)
46
+ : null;
45
47
  }
46
48
 
47
49
  /**
@@ -39,38 +39,27 @@ export function parseCommandArgs(argsString: string): string[] {
39
39
  * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.
40
40
  */
41
41
  export function substituteArgs(content: string, args: string[]): string {
42
- let result = content;
43
-
44
- // Replace $1, $2, etc. with positional args FIRST (before wildcards)
45
- // This prevents wildcard replacement values containing $<digit> patterns from being re-substituted
46
- result = result.replace(/\$(\d+)/g, (_, num) => {
47
- const index = parseInt(num, 10) - 1;
48
- return args[index] ?? "";
49
- });
50
-
51
- result = result.replace(/\$@\[(\d+)(?::(\d*)?)?\]/g, (_, startRaw: string, lengthRaw?: string) => {
52
- const start = Number.parseInt(startRaw, 10);
53
- if (!Number.isFinite(start) || start < 1) return "";
54
- const startIndex = start - 1;
55
- if (startIndex >= args.length) return "";
56
-
57
- if (lengthRaw === undefined || lengthRaw === "") {
58
- return args.slice(startIndex).join(" ");
59
- }
60
-
61
- const length = Number.parseInt(lengthRaw, 10);
62
- if (!Number.isFinite(length) || length <= 0) return "";
63
- return args.slice(startIndex, startIndex + length).join(" ");
64
- });
65
-
66
- // Pre-compute all args joined (optimization)
67
42
  const allArgs = args.join(" ");
43
+ return content.replace(
44
+ /\$@\[(\d+)(?::(\d*)?)?\]|\$ARGUMENTS|\$@|\$(\d+)/g,
45
+ (_, startRaw: string | undefined, lengthRaw: string | undefined, positionRaw: string | undefined) => {
46
+ if (positionRaw !== undefined) {
47
+ return args[Number.parseInt(positionRaw, 10) - 1] ?? "";
48
+ }
49
+ if (startRaw === undefined) return allArgs;
68
50
 
69
- // Replace $ARGUMENTS with all args joined (new syntax, aligns with Anthropic model, OpenAI code backend, OpenCode)
70
- result = result.replaceAll("$ARGUMENTS", allArgs);
51
+ const start = Number.parseInt(startRaw, 10);
52
+ if (!Number.isFinite(start) || start < 1) return "";
53
+ const startIndex = start - 1;
54
+ if (startIndex >= args.length) return "";
71
55
 
72
- // Replace $@ with all args joined (existing syntax)
73
- result = result.replaceAll("$@", allArgs);
56
+ if (lengthRaw === undefined || lengthRaw === "") {
57
+ return args.slice(startIndex).join(" ");
58
+ }
74
59
 
75
- return result;
60
+ const length = Number.parseInt(lengthRaw, 10);
61
+ if (!Number.isFinite(length) || length <= 0) return "";
62
+ return args.slice(startIndex, startIndex + length).join(" ");
63
+ },
64
+ );
76
65
  }
@@ -201,18 +201,16 @@ function lspExtensionKey(filePath: string): string {
201
201
  * Language id for syntax highlighting and UI (icons, read tool), or undefined if unknown.
202
202
  */
203
203
  export function getLanguageFromPath(filePath: string): string | undefined {
204
- const pair = EXTENSION_LANG[themeExtensionKey(filePath)];
205
- if (pair) return pair[0];
206
-
207
204
  const baseName = path.basename(filePath).toLowerCase();
208
205
  if (baseName.startsWith(".env.")) return "env";
209
206
  if (baseName === "dockerfile" || baseName.startsWith("dockerfile.") || baseName === "containerfile") {
210
207
  return "dockerfile";
211
208
  }
209
+ if (baseName === "makefile" || baseName === "gnumakefile") return "make";
212
210
  if (baseName === "justfile") return "just";
213
211
  if (baseName === "cmakelists.txt") return "cmake";
214
212
 
215
- return undefined;
213
+ return EXTENSION_LANG[themeExtensionKey(filePath)]?.[0];
216
214
  }
217
215
 
218
216
  /**