@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.
- package/CHANGELOG.md +48 -0
- package/dist/types/cli/notify-cli.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +4 -0
- package/dist/types/commands/sdk.d.ts +23 -0
- package/dist/types/config/file-lock.d.ts +3 -0
- package/dist/types/config/model-discovery-manager.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/config/resolve-config-value.d.ts +2 -1
- package/dist/types/config/settings-schema.d.ts +17 -1
- package/dist/types/discovery/agents-md.d.ts +11 -1
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/hook-selector.d.ts +20 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/sdk/broker/broker.d.ts +5 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
- package/dist/types/sdk/bus/config.d.ts +75 -113
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
- package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
- package/dist/types/sdk/bus/index.d.ts +1 -1
- package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
- package/dist/types/sdk/bus/notification-service.d.ts +54 -4
- package/dist/types/sdk/bus/session-control.d.ts +8 -3
- package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
- package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +8 -1
- package/dist/types/sdk/client/discovery.d.ts +2 -1
- package/dist/types/sdk/host/query/handlers.d.ts +2 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/providers.d.ts +22 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +19 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +3 -2
- package/dist/types/session/internal/session-open-errors.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/session/session-storage.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +12 -1
- package/dist/types/tools/composer-bash-policy.d.ts +9 -1
- package/package.json +10 -8
- package/scripts/build-sdk-package-smoke.ts +51 -0
- package/scripts/capture-notifications-settings-showcase.ts +1 -1
- package/scripts/verify-gjc-sdk-canonicalization.ts +14 -1
- package/src/cli/notify-cli.ts +215 -99
- package/src/commands/notify.ts +19 -8
- package/src/commands/sdk.ts +54 -0
- package/src/config/file-lock.ts +6 -0
- package/src/config/model-discovery-manager.ts +50 -8
- package/src/config/model-profiles.ts +8 -0
- package/src/config/model-registry.ts +866 -76
- package/src/config/resolve-config-value.ts +10 -8
- package/src/config/settings-schema.ts +25 -15
- package/src/config/settings.ts +15 -15
- package/src/discovery/agents-md.ts +76 -19
- package/src/gjc-runtime/psmux-detect.ts +12 -16
- package/src/gjc-runtime/session-state-sidecar.ts +12 -2
- package/src/gjc-runtime/tmux-sessions.ts +8 -1
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/main.ts +43 -7
- package/src/modes/DESIGN.md +11 -11
- package/src/modes/components/extensions/inspector-panel.ts +9 -5
- package/src/modes/components/hook-selector.ts +155 -44
- package/src/modes/components/model-selector.ts +242 -49
- package/src/modes/components/notifications-settings-editor.ts +631 -85
- package/src/modes/controllers/extension-ui-controller.ts +114 -15
- package/src/modes/controllers/selector-controller.ts +525 -93
- package/src/modes/print-mode.ts +3 -0
- package/src/modes/types.ts +6 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/sdk/broker/broker.ts +54 -10
- package/src/sdk/broker/lifecycle-ledger.ts +34 -1
- package/src/sdk/broker/lifecycle.ts +47 -50
- package/src/sdk/bus/chat-daemon-cli.ts +12 -5
- package/src/sdk/bus/chat-daemon-control.ts +35 -6
- package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
- package/src/sdk/bus/config.ts +534 -286
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/discord-live-provider.ts +57 -2
- package/src/sdk/bus/discord-provider.ts +21 -0
- package/src/sdk/bus/index.ts +97 -35
- package/src/sdk/bus/notification-orchestration.ts +371 -68
- package/src/sdk/bus/notification-service.ts +571 -137
- package/src/sdk/bus/session-control.ts +119 -44
- package/src/sdk/bus/slack-live-provider.ts +78 -2
- package/src/sdk/bus/slack-provider.ts +24 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +8 -1
- package/src/sdk/bus/telegram-daemon-control.ts +3 -3
- package/src/sdk/bus/telegram-daemon.ts +23 -15
- package/src/sdk/client/discovery.ts +10 -4
- package/src/sdk/host/query/handlers.ts +20 -1
- package/src/sdk/index.ts +1 -0
- package/src/sdk/protocol/operation-inventory.generated.json +18 -0
- package/src/sdk/protocol/operation-registry.ts +5 -2
- package/src/sdk/providers.ts +51 -0
- package/src/sdk/session-directory.ts +1 -0
- package/src/sdk/session.ts +34 -14
- package/src/session/agent-session.ts +109 -37
- package/src/session/internal/managed-session-scope.ts +981 -450
- package/src/session/internal/managed-session-storage.ts +418 -47
- package/src/session/internal/native-publish-outcome.ts +15 -3
- package/src/session/internal/session-open-errors.ts +11 -0
- package/src/session/session-manager.ts +27 -3
- package/src/session/session-storage.ts +72 -5
- package/src/slash-commands/builtin-registry.ts +117 -15
- package/src/system-prompt.ts +28 -15
- package/src/task/index.ts +9 -3
- package/src/tools/ask.ts +1 -1
- package/src/tools/bash.ts +4 -2
- package/src/tools/composer-bash-policy.ts +21 -5
- package/src/tools/telegram-send.ts +4 -2
- package/src/utils/command-args.ts +19 -30
- package/src/utils/lang-from-path.ts +2 -4
package/src/system-prompt.ts
CHANGED
|
@@ -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
|
|
264
|
+
export async function loadProjectContextFilesResult(
|
|
261
265
|
options: LoadContextFilesOptions = {},
|
|
262
|
-
): Promise<
|
|
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
|
|
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:
|
|
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",
|
|
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,
|
|
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)
|
|
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)
|
|
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:
|
|
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?.() ??
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
56
|
+
if (lengthRaw === undefined || lengthRaw === "") {
|
|
57
|
+
return args.slice(startIndex).join(" ");
|
|
58
|
+
}
|
|
74
59
|
|
|
75
|
-
|
|
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
|
|
213
|
+
return EXTENSION_LANG[themeExtensionKey(filePath)]?.[0];
|
|
216
214
|
}
|
|
217
215
|
|
|
218
216
|
/**
|