@gajae-code/coding-agent 0.7.7 → 0.7.9
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 +38 -1
- package/README.md +14 -0
- package/dist/types/cli/local-provider-smoke.d.ts +59 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/update-cli.d.ts +24 -1
- package/dist/types/commands/local-provider.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +1 -1
- package/dist/types/config/model-profile-activation.d.ts +19 -2
- package/dist/types/config/model-profiles.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +9 -0
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +45 -0
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/coordinator-mcp/server.d.ts +8 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +14 -5
- package/dist/types/gjc-runtime/team-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/tmux-common.d.ts +12 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +3 -1
- package/dist/types/modes/components/custom-model-preset-wizard.d.ts +1 -1
- package/dist/types/modes/components/hook-input.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +11 -0
- package/dist/types/modes/components/settings-selector.d.ts +4 -1
- package/dist/types/modes/components/status-line/types.d.ts +5 -7
- package/dist/types/modes/components/status-line.d.ts +7 -3
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +3 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +3 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +21 -1
- package/dist/types/modes/tmux-scroll.d.ts +18 -0
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/terminal-bell.d.ts +3 -0
- package/dist/types/notifications/daemon-paths.d.ts +9 -0
- package/dist/types/notifications/telegram-daemon-cli.d.ts +21 -4
- package/dist/types/notifications/telegram-daemon.d.ts +1 -10
- package/dist/types/tools/ask.d.ts +24 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +9 -2
- package/package.json +7 -7
- package/src/cli/local-provider-smoke.ts +632 -0
- package/src/cli/notify-cli.ts +5 -3
- package/src/cli/update-cli.ts +155 -13
- package/src/cli.ts +19 -0
- package/src/commands/launch.ts +46 -1
- package/src/commands/local-provider.ts +62 -0
- package/src/commands/notify.ts +1 -1
- package/src/config/keybindings.ts +1 -1
- package/src/config/model-profile-activation.ts +92 -11
- package/src/config/model-profiles.ts +15 -2
- package/src/config/model-registry.ts +136 -6
- package/src/config/models-config-schema.ts +9 -0
- package/src/config/settings-schema.ts +48 -0
- package/src/config/settings.ts +11 -0
- package/src/coordinator-mcp/server.ts +280 -36
- package/src/deep-interview/render-middleware.ts +1 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +14 -6
- package/src/defaults/gjc/skills/ralplan/SKILL.md +14 -7
- package/src/gjc-runtime/deep-interview-runtime.ts +1 -1
- package/src/gjc-runtime/launch-tmux.ts +321 -38
- package/src/gjc-runtime/launch-worktree.ts +16 -2
- package/src/gjc-runtime/team-runtime.ts +87 -7
- package/src/gjc-runtime/tmux-common.ts +16 -0
- package/src/gjc-runtime/tmux-sessions.ts +13 -9
- package/src/gjc-runtime/ultragoal-guard.ts +11 -5
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/modes/components/agent-dashboard.ts +2 -1
- package/src/modes/components/custom-model-preset-wizard.ts +31 -192
- package/src/modes/components/hook-editor.ts +7 -4
- package/src/modes/components/hook-input.ts +4 -0
- package/src/modes/components/model-selector.ts +260 -12
- package/src/modes/components/session-observer-overlay.ts +3 -3
- package/src/modes/components/settings-selector.ts +506 -5
- package/src/modes/components/status-line/segments.ts +16 -19
- package/src/modes/components/status-line/types.ts +6 -2
- package/src/modes/components/status-line.ts +70 -35
- package/src/modes/controllers/event-controller.ts +98 -4
- package/src/modes/controllers/extension-ui-controller.ts +5 -0
- package/src/modes/controllers/input-controller.ts +15 -0
- package/src/modes/controllers/selector-controller.ts +142 -11
- package/src/modes/interactive-mode.ts +7 -2
- package/src/modes/prompt-action-autocomplete.ts +8 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +11 -4
- package/src/modes/tmux-scroll.ts +92 -0
- package/src/modes/types.ts +6 -1
- package/src/modes/utils/terminal-bell.ts +48 -0
- package/src/notifications/daemon-paths.ts +22 -0
- package/src/notifications/telegram-daemon-cli.ts +125 -13
- package/src/notifications/telegram-daemon.ts +6 -21
- package/src/prompts/system/system-prompt.md +5 -0
- package/src/sdk.ts +10 -1
- package/src/session/agent-session.ts +84 -6
- package/src/slash-commands/builtin-registry.ts +106 -13
- package/src/tools/ask.ts +11 -1
- package/src/tools/eval.ts +2 -2
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/ultragoal-ask-guard.ts +39 -5
|
@@ -83,7 +83,8 @@ export const MODEL_ROLES: Record<ModelRole, ModelRoleInfo> = {
|
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
export const MODEL_ROLE_IDS: ModelRole[] = ["default"];
|
|
86
|
-
|
|
86
|
+
export const MODEL_PROFILE_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
87
|
+
export const MODEL_PROFILE_NAME_PATTERN_DESCRIPTION = "lowercase letters, numbers, dots, underscores, or hyphens";
|
|
87
88
|
export type GjcModelAssignmentTargetId = "default" | "executor" | "architect" | "planner" | "critic";
|
|
88
89
|
|
|
89
90
|
export interface GjcModelAssignmentTargetInfo extends ModelRoleInfo {
|
|
@@ -243,6 +244,7 @@ interface ProviderValidationConfig {
|
|
|
243
244
|
requestTransform?: ModelRequestTransform;
|
|
244
245
|
disableStrictTools?: boolean;
|
|
245
246
|
cacheRetention?: CacheRetention;
|
|
247
|
+
openaiCompat?: { baseUrl: string; apiKey?: string; apiKeyEnv?: string };
|
|
246
248
|
modelOverrides?: Record<string, unknown>;
|
|
247
249
|
models: ProviderValidationModel[];
|
|
248
250
|
}
|
|
@@ -271,11 +273,12 @@ function validateProviderConfiguration(
|
|
|
271
273
|
!config.disableStrictTools &&
|
|
272
274
|
!config.requestTransform &&
|
|
273
275
|
!config.cacheRetention &&
|
|
276
|
+
!config.openaiCompat &&
|
|
274
277
|
!hasModelOverrides &&
|
|
275
278
|
!config.discovery
|
|
276
279
|
) {
|
|
277
280
|
throw new Error(
|
|
278
|
-
`Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "compat", "requestTransform", "cacheRetention", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
|
|
281
|
+
`Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "compat", "requestTransform", "cacheRetention", "disableStrictTools", "modelOverrides", "discovery", "openaiCompat", or "models"`,
|
|
279
282
|
);
|
|
280
283
|
}
|
|
281
284
|
}
|
|
@@ -389,6 +392,7 @@ export const ModelsConfigFile = new ConfigFile<ModelsConfig>("models", ModelsCon
|
|
|
389
392
|
requestTransform: providerConfig.requestTransform,
|
|
390
393
|
disableStrictTools: providerConfig.disableStrictTools,
|
|
391
394
|
cacheRetention: providerConfig.cacheRetention,
|
|
395
|
+
openaiCompat: providerConfig.openaiCompat,
|
|
392
396
|
modelOverrides: providerConfig.modelOverrides,
|
|
393
397
|
models: (providerConfig.models ?? []) as ProviderValidationModel[],
|
|
394
398
|
},
|
|
@@ -436,6 +440,18 @@ function resolveProviderBaseUrlFromEnv(provider: string): string | undefined {
|
|
|
436
440
|
return $pickenv(...getProviderBaseUrlEnvKeys(provider));
|
|
437
441
|
}
|
|
438
442
|
|
|
443
|
+
function normalizeLocalOpenAICompatBaseUrl(baseUrl: string): string {
|
|
444
|
+
try {
|
|
445
|
+
const parsed = new URL(baseUrl);
|
|
446
|
+
const trimmedPath = parsed.pathname.replace(/\/+$/g, "");
|
|
447
|
+
parsed.pathname = trimmedPath.endsWith("/v1") ? trimmedPath || "/v1" : `${trimmedPath}/v1`;
|
|
448
|
+
return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
449
|
+
} catch {
|
|
450
|
+
const trimmed = baseUrl.replace(/\/+$/g, "");
|
|
451
|
+
return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
439
455
|
/**
|
|
440
456
|
* Merge a freshly discovered model with the matching bundled/configured entry
|
|
441
457
|
* (or a runtime provider override when no bundled entry exists).
|
|
@@ -1409,6 +1425,45 @@ export class ModelRegistry {
|
|
|
1409
1425
|
for (const [providerName, providerConfig] of providerEntries) {
|
|
1410
1426
|
if (providerConfig.webSearch) this.#providerWebSearchModes.set(providerName, providerConfig.webSearch);
|
|
1411
1427
|
const providerApiKeyConfig = providerConfig.apiKey ?? resolveApiKeyEnvConfig(providerConfig.apiKeyEnv);
|
|
1428
|
+
const localOpenAICompat = providerConfig.openaiCompat;
|
|
1429
|
+
const localOpenAICompatApiKeyConfig = localOpenAICompat
|
|
1430
|
+
? (localOpenAICompat.apiKey ?? resolveApiKeyEnvConfig(localOpenAICompat.apiKeyEnv))
|
|
1431
|
+
: undefined;
|
|
1432
|
+
if (localOpenAICompat) {
|
|
1433
|
+
const localOpenAICompatBaseUrl = normalizeLocalOpenAICompatBaseUrl(localOpenAICompat.baseUrl);
|
|
1434
|
+
const localCompatResolvedKey = localOpenAICompat.apiKeyEnv
|
|
1435
|
+
? resolveApiKeyEnvConfig(localOpenAICompat.apiKeyEnv)
|
|
1436
|
+
: localOpenAICompat.apiKey
|
|
1437
|
+
? resolveApiKeyConfig(localOpenAICompat.apiKey)
|
|
1438
|
+
: undefined;
|
|
1439
|
+
overrides.set(providerName, {
|
|
1440
|
+
baseUrl: localOpenAICompatBaseUrl,
|
|
1441
|
+
apiKey: localOpenAICompatApiKeyConfig,
|
|
1442
|
+
compat: {
|
|
1443
|
+
supportsStore: false,
|
|
1444
|
+
supportsDeveloperRole: false,
|
|
1445
|
+
supportsReasoningEffort: false,
|
|
1446
|
+
},
|
|
1447
|
+
});
|
|
1448
|
+
discoverableProviders.push({
|
|
1449
|
+
provider: providerName,
|
|
1450
|
+
api: "openai-completions",
|
|
1451
|
+
baseUrl: localOpenAICompatBaseUrl,
|
|
1452
|
+
compat: {
|
|
1453
|
+
supportsStore: false,
|
|
1454
|
+
supportsDeveloperRole: false,
|
|
1455
|
+
supportsReasoningEffort: false,
|
|
1456
|
+
},
|
|
1457
|
+
discovery: { type: "openai-models-list" },
|
|
1458
|
+
optional: false,
|
|
1459
|
+
});
|
|
1460
|
+
if (localCompatResolvedKey) {
|
|
1461
|
+
this.#customProviderApiKeys.set(providerName, localCompatResolvedKey);
|
|
1462
|
+
this.authStorage.setConfigApiKey(providerName, localCompatResolvedKey);
|
|
1463
|
+
} else {
|
|
1464
|
+
keylessProviders.add(providerName);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1412
1467
|
// Always set overrides when baseUrl/headers/apiKey/authHeader/compat/disableStrictTools/transport are present
|
|
1413
1468
|
if (
|
|
1414
1469
|
providerConfig.baseUrl ||
|
|
@@ -1536,10 +1591,7 @@ export class ModelRegistry {
|
|
|
1536
1591
|
const where = first?.path.length ? `/${first.path.map(String).join("/")}` : "root";
|
|
1537
1592
|
throw new Error(`Generated models config is invalid at ${where}: ${first?.message ?? "unknown schema error"}`);
|
|
1538
1593
|
}
|
|
1539
|
-
await
|
|
1540
|
-
await Bun.write(this.#modelsConfigFile.path(), Bun.YAML.stringify(checkedConfig.data, null, 2));
|
|
1541
|
-
this.#modelsConfigFile.invalidate();
|
|
1542
|
-
this.#reloadStaticModels();
|
|
1594
|
+
await this.#writeCheckedModelsConfig(checkedConfig.data);
|
|
1543
1595
|
const modelMapping = { ...definition.model_mapping };
|
|
1544
1596
|
const profile: ModelProfileDefinition = {
|
|
1545
1597
|
name: normalizedName,
|
|
@@ -1550,6 +1602,84 @@ export class ModelRegistry {
|
|
|
1550
1602
|
};
|
|
1551
1603
|
return profile;
|
|
1552
1604
|
}
|
|
1605
|
+
|
|
1606
|
+
async renameCustomModelProfile(name: string, displayName: string): Promise<ModelProfileDefinition> {
|
|
1607
|
+
const normalizedName = name.trim();
|
|
1608
|
+
const nextDisplayName = displayName.trim();
|
|
1609
|
+
if (!normalizedName) throw new Error("Profile name is required.");
|
|
1610
|
+
if (!nextDisplayName) throw new Error("Profile display name is required.");
|
|
1611
|
+
const { current, profile } = this.#loadCustomProfileForMutation(normalizedName, "rename");
|
|
1612
|
+
const nextProfiles = {
|
|
1613
|
+
...(current.profiles ?? {}),
|
|
1614
|
+
[normalizedName]: {
|
|
1615
|
+
...profile,
|
|
1616
|
+
display_name: nextDisplayName,
|
|
1617
|
+
},
|
|
1618
|
+
};
|
|
1619
|
+
const checkedConfig = ModelsConfigSchema.safeParse({ ...current, profiles: nextProfiles });
|
|
1620
|
+
if (!checkedConfig.success) {
|
|
1621
|
+
const first = checkedConfig.error.issues[0];
|
|
1622
|
+
const where = first?.path.length ? `/${first.path.map(String).join("/")}` : "root";
|
|
1623
|
+
throw new Error(`Generated models config is invalid at ${where}: ${first?.message ?? "unknown schema error"}`);
|
|
1624
|
+
}
|
|
1625
|
+
await this.#writeCheckedModelsConfig(checkedConfig.data);
|
|
1626
|
+
const modelMapping = { ...profile.model_mapping };
|
|
1627
|
+
return {
|
|
1628
|
+
name: normalizedName,
|
|
1629
|
+
displayName: nextDisplayName,
|
|
1630
|
+
requiredProviders: aggregateModelProfileRequiredProviders(profile.required_providers, { modelMapping }),
|
|
1631
|
+
modelMapping,
|
|
1632
|
+
source: "user",
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
async deleteCustomModelProfile(name: string): Promise<ModelProfileConfig> {
|
|
1637
|
+
const normalizedName = name.trim();
|
|
1638
|
+
if (!normalizedName) throw new Error("Profile name is required.");
|
|
1639
|
+
const { current, profile } = this.#loadCustomProfileForMutation(normalizedName, "delete");
|
|
1640
|
+
const nextProfiles = { ...(current.profiles ?? {}) };
|
|
1641
|
+
delete nextProfiles[normalizedName];
|
|
1642
|
+
const checkedConfig = ModelsConfigSchema.safeParse({
|
|
1643
|
+
...current,
|
|
1644
|
+
profiles: Object.keys(nextProfiles).length > 0 ? nextProfiles : undefined,
|
|
1645
|
+
});
|
|
1646
|
+
if (!checkedConfig.success) {
|
|
1647
|
+
const first = checkedConfig.error.issues[0];
|
|
1648
|
+
const where = first?.path.length ? `/${first.path.map(String).join("/")}` : "root";
|
|
1649
|
+
throw new Error(`Generated models config is invalid at ${where}: ${first?.message ?? "unknown schema error"}`);
|
|
1650
|
+
}
|
|
1651
|
+
await this.#writeCheckedModelsConfig(checkedConfig.data);
|
|
1652
|
+
return profile;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
#loadCustomProfileForMutation(
|
|
1656
|
+
normalizedName: string,
|
|
1657
|
+
action: "rename" | "delete",
|
|
1658
|
+
): { current: ModelsConfig; profile: ModelProfileConfig } {
|
|
1659
|
+
const loaded = this.#modelsConfigFile.tryLoad();
|
|
1660
|
+
if (loaded.status === "error") {
|
|
1661
|
+
throw new Error(
|
|
1662
|
+
`Cannot ${action} custom model profile because ${this.#modelsConfigFile.path()} is invalid. Fix the existing config before modifying presets.`,
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
const current = loaded.value ?? this.#modelsConfigFile.createDefault();
|
|
1666
|
+
const profile = current.profiles?.[normalizedName];
|
|
1667
|
+
if (!profile) {
|
|
1668
|
+
const existing = this.#modelProfiles.get(normalizedName);
|
|
1669
|
+
if (existing && existing.source !== "user") {
|
|
1670
|
+
throw new Error(`Cannot ${action} bundled model profile: ${normalizedName}.`);
|
|
1671
|
+
}
|
|
1672
|
+
throw new Error(`Custom model profile does not exist: ${normalizedName}.`);
|
|
1673
|
+
}
|
|
1674
|
+
return { current, profile };
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
async #writeCheckedModelsConfig(config: ModelsConfig): Promise<void> {
|
|
1678
|
+
await fs.mkdir(path.dirname(this.#modelsConfigFile.path()), { recursive: true });
|
|
1679
|
+
await Bun.write(this.#modelsConfigFile.path(), Bun.YAML.stringify(config, null, 2));
|
|
1680
|
+
this.#modelsConfigFile.invalidate();
|
|
1681
|
+
this.#reloadStaticModels();
|
|
1682
|
+
}
|
|
1553
1683
|
applyConfiguredModelBindings(targetSettings: Settings): void {
|
|
1554
1684
|
this.#modelBindingsTargetSettings = targetSettings;
|
|
1555
1685
|
this.#applyConfiguredModelBindingsToTarget();
|
|
@@ -193,6 +193,14 @@ export const ProviderDiscoverySchema = z.object({
|
|
|
193
193
|
type: z.enum(["ollama", "llama.cpp", "lm-studio", "openai-models-list"]),
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
+
const LocalOpenAICompatSchema = z
|
|
197
|
+
.object({
|
|
198
|
+
baseUrl: z.string().min(1),
|
|
199
|
+
apiKey: z.string().min(1).optional(),
|
|
200
|
+
apiKeyEnv: z.string().min(1).optional(),
|
|
201
|
+
})
|
|
202
|
+
.strict();
|
|
203
|
+
|
|
196
204
|
export const ProviderAuthSchema = z.enum(["apiKey", "none", "oauth"]);
|
|
197
205
|
|
|
198
206
|
export type ProviderAuthMode = z.infer<typeof ProviderAuthSchema>;
|
|
@@ -237,6 +245,7 @@ const ProviderConfigSchema = z
|
|
|
237
245
|
*/
|
|
238
246
|
transport: z.literal("pi-native").optional(),
|
|
239
247
|
cacheRetention: CacheRetentionSchema.optional(),
|
|
248
|
+
openaiCompat: LocalOpenAICompatSchema.optional(),
|
|
240
249
|
})
|
|
241
250
|
.strict();
|
|
242
251
|
|
|
@@ -274,6 +274,44 @@ export const SETTINGS_SCHEMA = {
|
|
|
274
274
|
validate: (value: number) => Number.isFinite(value) && value > 0,
|
|
275
275
|
},
|
|
276
276
|
|
|
277
|
+
"notifications.terminalBell": {
|
|
278
|
+
type: "boolean",
|
|
279
|
+
default: false,
|
|
280
|
+
ui: {
|
|
281
|
+
tab: "interaction",
|
|
282
|
+
label: "Terminal Bell",
|
|
283
|
+
description:
|
|
284
|
+
"Emit a BEL character for local terminal notifications. Windows Terminal may keep BEL silent depending on profile/system sound settings; use completion.notifyCommand for a PowerShell Console.Beep workaround.",
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
"notifications.bellOnComplete": {
|
|
288
|
+
type: "boolean",
|
|
289
|
+
default: true,
|
|
290
|
+
ui: {
|
|
291
|
+
tab: "interaction",
|
|
292
|
+
label: "Bell on Completion",
|
|
293
|
+
description: "Ring the terminal bell when an agent turn completes",
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
"notifications.bellOnApproval": {
|
|
297
|
+
type: "boolean",
|
|
298
|
+
default: true,
|
|
299
|
+
ui: {
|
|
300
|
+
tab: "interaction",
|
|
301
|
+
label: "Bell on Approval",
|
|
302
|
+
description: "Ring the terminal bell when a plan or approval prompt needs attention",
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
"notifications.bellOnAsk": {
|
|
306
|
+
type: "boolean",
|
|
307
|
+
default: true,
|
|
308
|
+
ui: {
|
|
309
|
+
tab: "interaction",
|
|
310
|
+
label: "Bell on Ask",
|
|
311
|
+
description: "Ring the terminal bell when an ask/user-input prompt needs attention",
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
|
|
277
315
|
autoResume: {
|
|
278
316
|
type: "boolean",
|
|
279
317
|
default: false,
|
|
@@ -1135,6 +1173,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
1135
1173
|
default: "on",
|
|
1136
1174
|
ui: { tab: "interaction", label: "Completion Notification", description: "Notify when the agent completes" },
|
|
1137
1175
|
},
|
|
1176
|
+
"completion.notifyCommand": {
|
|
1177
|
+
type: "string",
|
|
1178
|
+
default: "",
|
|
1179
|
+
ui: {
|
|
1180
|
+
tab: "interaction",
|
|
1181
|
+
label: "Completion Notification Command",
|
|
1182
|
+
description:
|
|
1183
|
+
"Optional user-level shell command to run when an agent turn completes; receives GJC_NOTIFICATION_* environment variables. On Windows, this can call PowerShell [Console]::Beep when terminal BEL is silent.",
|
|
1184
|
+
},
|
|
1185
|
+
},
|
|
1138
1186
|
|
|
1139
1187
|
"ask.timeout": {
|
|
1140
1188
|
type: "number",
|
package/src/config/settings.ts
CHANGED
|
@@ -297,6 +297,17 @@ export class Settings {
|
|
|
297
297
|
return getDefault(path);
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Get a setting value from the user/global config only.
|
|
302
|
+
*
|
|
303
|
+
* Use for machine-local command hooks and other settings that must not be
|
|
304
|
+
* activated by project-scoped config files.
|
|
305
|
+
*/
|
|
306
|
+
getGlobal<P extends SettingPath>(path: P): SettingValue<P> | undefined {
|
|
307
|
+
const value = getByPath(this.#global, path.split("."));
|
|
308
|
+
return value === undefined ? undefined : (value as SettingValue<P>);
|
|
309
|
+
}
|
|
310
|
+
|
|
300
311
|
/** Check whether a setting is present in loaded settings/overrides rather than coming from schema defaults. */
|
|
301
312
|
has(path: SettingPath): boolean {
|
|
302
313
|
return getByPath(this.#merged, path.split(".")) !== undefined;
|