@kolisachint/hoocode-agent 0.4.113 → 0.4.115
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 +16 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/plugins/authoring.d.ts +51 -0
- package/dist/core/extensions/plugins/authoring.d.ts.map +1 -0
- package/dist/core/extensions/plugins/authoring.js +91 -0
- package/dist/core/extensions/plugins/authoring.js.map +1 -0
- package/dist/core/extensions/plugins/default-marketplace/.agents-plugin/marketplace.json +12 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/.agents-plugin/plugin.json +6 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/commands/hello.md +5 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/skills/greet/SKILL.md +7 -0
- package/dist/core/extensions/plugins/formats/agents.d.ts +7 -0
- package/dist/core/extensions/plugins/formats/agents.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/agents.js +14 -0
- package/dist/core/extensions/plugins/formats/agents.js.map +1 -0
- package/dist/core/extensions/plugins/formats/claude.d.ts +10 -0
- package/dist/core/extensions/plugins/formats/claude.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/claude.js +17 -0
- package/dist/core/extensions/plugins/formats/claude.js.map +1 -0
- package/dist/core/extensions/plugins/formats/copilot.d.ts +23 -0
- package/dist/core/extensions/plugins/formats/copilot.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/copilot.js +130 -0
- package/dist/core/extensions/plugins/formats/copilot.js.map +1 -0
- package/dist/core/extensions/plugins/formats/index.d.ts +41 -0
- package/dist/core/extensions/plugins/formats/index.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/index.js +76 -0
- package/dist/core/extensions/plugins/formats/index.js.map +1 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts +21 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.js +97 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.js.map +1 -0
- package/dist/core/extensions/plugins/formats/shared.d.ts +44 -0
- package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/shared.js +107 -0
- package/dist/core/extensions/plugins/formats/shared.js.map +1 -0
- package/dist/core/extensions/plugins/formats/types.d.ts +118 -0
- package/dist/core/extensions/plugins/formats/types.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/types.js +19 -0
- package/dist/core/extensions/plugins/formats/types.js.map +1 -0
- package/dist/core/extensions/plugins/install.d.ts +73 -0
- package/dist/core/extensions/plugins/install.d.ts.map +1 -0
- package/dist/core/extensions/plugins/install.js +173 -0
- package/dist/core/extensions/plugins/install.js.map +1 -0
- package/dist/core/extensions/plugins/manifest.d.ts +25 -7
- package/dist/core/extensions/plugins/manifest.d.ts.map +1 -1
- package/dist/core/extensions/plugins/manifest.js +16 -91
- package/dist/core/extensions/plugins/manifest.js.map +1 -1
- package/dist/core/extensions/plugins/marketplace.d.ts +5 -4
- package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
- package/dist/core/extensions/plugins/marketplace.js +8 -13
- package/dist/core/extensions/plugins/marketplace.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +2 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +2 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +16 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +2 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/subagent-depth.d.ts +13 -0
- package/dist/core/subagent-depth.d.ts.map +1 -1
- package/dist/core/subagent-depth.js +15 -0
- package/dist/core/subagent-depth.js.map +1 -1
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +4 -1
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/tools/find.d.ts +8 -3
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +189 -122
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/index.d.ts +0 -4
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +2 -5
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/plugin-tool-names.d.ts +21 -0
- package/dist/core/tools/plugin-tool-names.d.ts.map +1 -0
- package/dist/core/tools/plugin-tool-names.js +31 -0
- package/dist/core/tools/plugin-tool-names.js.map +1 -0
- package/dist/core/tools/plugins.d.ts +49 -0
- package/dist/core/tools/plugins.d.ts.map +1 -0
- package/dist/core/tools/plugins.js +186 -0
- package/dist/core/tools/plugins.js.map +1 -0
- package/dist/core/tools/propose-plugin.d.ts +36 -0
- package/dist/core/tools/propose-plugin.d.ts.map +1 -0
- package/dist/core/tools/propose-plugin.js +219 -0
- package/dist/core/tools/propose-plugin.js.map +1 -0
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +29 -3
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/core/warm-subagent-pool.d.ts.map +1 -1
- package/dist/core/warm-subagent-pool.js +4 -1
- package/dist/core/warm-subagent-pool.js.map +1 -1
- package/dist/extensions/core/marketplace.d.ts +7 -3
- package/dist/extensions/core/marketplace.d.ts.map +1 -1
- package/dist/extensions/core/marketplace.js +23 -67
- package/dist/extensions/core/marketplace.js.map +1 -1
- package/dist/extensions/core/mcp-deferred.d.ts +28 -0
- package/dist/extensions/core/mcp-deferred.d.ts.map +1 -0
- package/dist/extensions/core/mcp-deferred.js +53 -0
- package/dist/extensions/core/mcp-deferred.js.map +1 -0
- package/dist/extensions/core/mcp-loader.d.ts.map +1 -1
- package/dist/extensions/core/mcp-loader.js +132 -78
- package/dist/extensions/core/mcp-loader.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/brand.d.ts +41 -0
- package/dist/modes/interactive/brand.d.ts.map +1 -0
- package/dist/modes/interactive/brand.js +40 -0
- package/dist/modes/interactive/brand.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +102 -131
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts +6 -5
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +13 -9
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/dist/modes/interactive/resource-display.d.ts.map +1 -1
- package/dist/modes/interactive/resource-display.js +131 -86
- package/dist/modes/interactive/resource-display.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -1
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/voice/voice-controller.d.ts.map +1 -1
- package/dist/modes/interactive/voice/voice-controller.js +15 -7
- package/dist/modes/interactive/voice/voice-controller.js.map +1 -1
- package/dist/modes/interactive/voice/voice-panel.d.ts +27 -8
- package/dist/modes/interactive/voice/voice-panel.d.ts.map +1 -1
- package/dist/modes/interactive/voice/voice-panel.js +89 -27
- package/dist/modes/interactive/voice/voice-panel.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +8 -2
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +15 -6
- package/dist/utils/tools-manager.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +5 -5
- package/dist/core/tools/glob.d.ts +0 -42
- package/dist/core/tools/glob.d.ts.map +0 -1
- package/dist/core/tools/glob.js +0 -432
- package/dist/core/tools/glob.js.map +0 -1
|
@@ -17,9 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import * as fs from "node:fs";
|
|
19
19
|
import * as path from "node:path";
|
|
20
|
-
|
|
21
|
-
export const CLAUDE_MARKETPLACE_FILE = path.join(".claude-plugin", "marketplace.json");
|
|
22
|
-
export const COPILOT_MARKETPLACE_FILE = path.join(".github", "marketplace.json");
|
|
20
|
+
import { PLUGIN_FORMATS } from "./formats/index.js";
|
|
23
21
|
/** Map an internal marketplace `format` to its public platform token. */
|
|
24
22
|
function formatToPlatform(format) {
|
|
25
23
|
return format === "copilot" ? "github" : format;
|
|
@@ -58,18 +56,15 @@ function readJson(file) {
|
|
|
58
56
|
* or it is invalid.
|
|
59
57
|
*/
|
|
60
58
|
export function parseMarketplaceDir(dir) {
|
|
61
|
-
const agentsPath = path.join(dir, AGENTS_MARKETPLACE_FILE);
|
|
62
|
-
const claudePath = path.join(dir, CLAUDE_MARKETPLACE_FILE);
|
|
63
|
-
const copilotPath = path.join(dir, COPILOT_MARKETPLACE_FILE);
|
|
64
59
|
// Record every index format present so a conflict (more than one file) is
|
|
65
|
-
// visible via supportPlatform, not silently dropped.
|
|
60
|
+
// visible via supportPlatform, not silently dropped. PLUGIN_FORMATS is
|
|
61
|
+
// precedence-ordered (native > Claude > Copilot), so `present` inherits it.
|
|
66
62
|
const present = [];
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
present.push({ format: "copilot", path: copilotPath });
|
|
63
|
+
for (const fmt of PLUGIN_FORMATS) {
|
|
64
|
+
const p = path.join(dir, fmt.marketplaceFile);
|
|
65
|
+
if (fs.existsSync(p))
|
|
66
|
+
present.push({ format: fmt.id, path: p });
|
|
67
|
+
}
|
|
73
68
|
if (present.length === 0)
|
|
74
69
|
return null;
|
|
75
70
|
// Precedence winner (present is built in agents > claude > copilot order).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin marketplaces — install plugins from a curated index hosted in a git repo\n * (or a local directory).\n *\n * A marketplace is a directory containing an index manifest in one of three formats:\n * - Native: `.agents-plugin/marketplace.json` (preferred; the `.agents` surface)\n * - Claude: `.claude-plugin/marketplace.json`\n * - Copilot: `.github/marketplace.json` (Copilot-style git index)\n *\n * All three use the same shape: `{ name?, owner?, plugins: [{ name, source, description? }] }`.\n * A plugin `source` is either a path relative to the marketplace root (a plugin\n * directory inside the repo), a git URL, or an `npm:<spec>` reference.\n *\n * When more than one is present the native `.agents-plugin` format wins, then\n * Claude, then Copilot (no merge) — matching the \"`.agents/` first\" policy used\n * across hoocode's resource surfaces.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport const AGENTS_MARKETPLACE_FILE = path.join(\".agents-plugin\", \"marketplace.json\");\nexport const CLAUDE_MARKETPLACE_FILE = path.join(\".claude-plugin\", \"marketplace.json\");\nexport const COPILOT_MARKETPLACE_FILE = path.join(\".github\", \"marketplace.json\");\n\n/**\n * Canonical platform token used by the optional `supportPlatform` field. The\n * `.github/marketplace.json` (Copilot-style) format is surfaced as `\"github\"` —\n * friendlier than the internal `format: \"copilot\"` and matching where the file\n * lives. Authored aliases (`copilot`, `gh`) normalize to `github`.\n */\nexport type MarketplacePlatform = \"agents\" | \"claude\" | \"github\";\n\n/** Map an internal marketplace `format` to its public platform token. */\nfunction formatToPlatform(format: NormalizedMarketplace[\"format\"]): MarketplacePlatform {\n\treturn format === \"copilot\" ? \"github\" : format;\n}\n\n/** Normalize an authored `supportPlatform` (string | string[]) to canonical tokens. */\nexport function normalizePlatforms(value: unknown): MarketplacePlatform[] {\n\tconst raw = Array.isArray(value) ? value : value == null ? [] : [value];\n\tconst out: MarketplacePlatform[] = [];\n\tfor (const v of raw) {\n\t\tif (typeof v !== \"string\") continue;\n\t\tconst t = v.trim().toLowerCase();\n\t\tconst canonical: MarketplacePlatform | undefined =\n\t\t\tt === \"agents\" || t === \"native\"\n\t\t\t\t? \"agents\"\n\t\t\t\t: t === \"claude\"\n\t\t\t\t\t? \"claude\"\n\t\t\t\t\t: t === \"github\" || t === \"copilot\" || t === \"gh\"\n\t\t\t\t\t\t? \"github\"\n\t\t\t\t\t\t: undefined;\n\t\tif (canonical && !out.includes(canonical)) out.push(canonical);\n\t}\n\treturn out;\n}\n\nexport interface MarketplacePluginEntry {\n\tname: string;\n\t/** Relative path, git URL, or `npm:<spec>`. */\n\tsource: string;\n\tdescription?: string;\n\t/**\n\t * Optional platform(s) this entry targets. Only set when authored on the\n\t * entry; absent means \"no per-entry restriction\" (the marketplace's\n\t * platforms apply). Purely informational today — nothing filters on it.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n}\n\nexport interface NormalizedMarketplace {\n\tname: string;\n\t/** Precedence winner when several index files conflict (agents > claude > copilot). */\n\tformat: \"agents\" | \"claude\" | \"copilot\";\n\t/**\n\t * Every platform this marketplace directory offers, so a conflict between\n\t * multiple index files (e.g. both `.github/` and `.claude-plugin/`) is\n\t * recorded rather than hidden. Always includes the resolved `format`'s\n\t * platform; also folds in any authored top-level `supportPlatform`. Never\n\t * empty.\n\t */\n\tsupportPlatform: MarketplacePlatform[];\n\t/** Absolute path to the marketplace directory. */\n\troot: string;\n\tmanifestPath: string;\n\tplugins: MarketplacePluginEntry[];\n}\n\ninterface RawMarketplace {\n\tname?: string;\n\towner?: string;\n\t/** Optional authored platform hint (string | string[]); folded into supportPlatform. */\n\tsupportPlatform?: unknown;\n\tplugins?: Array<{ name?: string; source?: string; description?: string; supportPlatform?: unknown }>;\n}\n\n/** A resolved, installable plugin source. */\nexport type ResolvedPluginSource =\n\t| { kind: \"local\"; path: string }\n\t| { kind: \"git\"; url: string }\n\t| { kind: \"npm\"; spec: string };\n\nfunction readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a marketplace directory. Native `.agents-plugin` wins, then Claude, then\n * Copilot when more than one file is present. Returns null if no manifest exists\n * or it is invalid.\n */\nexport function parseMarketplaceDir(dir: string): NormalizedMarketplace | null {\n\tconst agentsPath = path.join(dir, AGENTS_MARKETPLACE_FILE);\n\tconst claudePath = path.join(dir, CLAUDE_MARKETPLACE_FILE);\n\tconst copilotPath = path.join(dir, COPILOT_MARKETPLACE_FILE);\n\n\t// Record every index format present so a conflict (more than one file) is\n\t// visible via supportPlatform, not silently dropped.\n\tconst present: Array<{ format: NormalizedMarketplace[\"format\"]; path: string }> = [];\n\tif (fs.existsSync(agentsPath)) present.push({ format: \"agents\", path: agentsPath });\n\tif (fs.existsSync(claudePath)) present.push({ format: \"claude\", path: claudePath });\n\tif (fs.existsSync(copilotPath)) present.push({ format: \"copilot\", path: copilotPath });\n\tif (present.length === 0) return null;\n\n\t// Precedence winner (present is built in agents > claude > copilot order).\n\tconst { format, path: manifestPath } = present[0];\n\n\tconst raw = readJson<RawMarketplace>(manifestPath);\n\tif (!raw) return null;\n\n\tconst plugins: MarketplacePluginEntry[] = [];\n\tfor (const entry of raw.plugins ?? []) {\n\t\tif (entry?.name && entry.source) {\n\t\t\tconst entryPlatforms = normalizePlatforms(entry.supportPlatform);\n\t\t\tplugins.push({\n\t\t\t\tname: entry.name,\n\t\t\t\tsource: entry.source,\n\t\t\t\tdescription: entry.description,\n\t\t\t\t...(entryPlatforms.length > 0 ? { supportPlatform: entryPlatforms } : {}),\n\t\t\t});\n\t\t}\n\t}\n\n\t// supportPlatform = platforms of all present index files, plus any authored\n\t// top-level hint, deduped. Always non-empty (at least the resolved format).\n\tconst supportPlatform: MarketplacePlatform[] = [];\n\tfor (const p of [...present.map((e) => formatToPlatform(e.format)), ...normalizePlatforms(raw.supportPlatform)]) {\n\t\tif (!supportPlatform.includes(p)) supportPlatform.push(p);\n\t}\n\n\treturn {\n\t\tname: (raw.name ?? path.basename(dir)).trim() || path.basename(dir),\n\t\tformat,\n\t\tsupportPlatform,\n\t\troot: dir,\n\t\tmanifestPath,\n\t\tplugins,\n\t};\n}\n\n/** Classify and resolve a plugin `source` against its marketplace root. */\nexport function resolvePluginSource(source: string, marketplaceRoot: string): ResolvedPluginSource {\n\tconst s = source.trim();\n\tif (s.startsWith(\"npm:\")) return { kind: \"npm\", spec: s.slice(4) };\n\tif (/^https?:\\/\\//.test(s) || s.startsWith(\"git@\") || s.endsWith(\".git\")) return { kind: \"git\", url: s };\n\t// Otherwise a path relative to (or absolute within) the marketplace repo.\n\treturn { kind: \"local\", path: path.isAbsolute(s) ? s : path.resolve(marketplaceRoot, s) };\n}\n\n// ============================================================================\n// Marketplace registry (persisted list of added marketplaces)\n// ============================================================================\n\nexport interface MarketplaceRecord {\n\t/** Original location the user added (git URL or local path). */\n\tlocation: string;\n\t/** Local directory to read the manifest from (clone dir for git, else location). */\n\tdir: string;\n}\n\ninterface MarketplaceStoreFile {\n\tmarketplaces?: MarketplaceRecord[];\n}\n\n/** Read the persisted marketplace list from `<storePath>`. */\nexport function readMarketplaceStore(storePath: string): MarketplaceRecord[] {\n\tconst parsed = readJson<MarketplaceStoreFile>(storePath);\n\treturn Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : [];\n}\n\n/** Write the marketplace list to `<storePath>`. */\nexport function writeMarketplaceStore(storePath: string, records: MarketplaceRecord[]): void {\n\tfs.mkdirSync(path.dirname(storePath), { recursive: true });\n\tfs.writeFileSync(storePath, `${JSON.stringify({ marketplaces: records }, null, 2)}\\n`, \"utf8\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin marketplaces — install plugins from a curated index hosted in a git repo\n * (or a local directory).\n *\n * A marketplace is a directory containing an index manifest in one of three formats:\n * - Native: `.agents-plugin/marketplace.json` (preferred; the `.agents` surface)\n * - Claude: `.claude-plugin/marketplace.json`\n * - Copilot: `.github/marketplace.json` (Copilot-style git index)\n *\n * All three use the same shape: `{ name?, owner?, plugins: [{ name, source, description? }] }`.\n * A plugin `source` is either a path relative to the marketplace root (a plugin\n * directory inside the repo), a git URL, or an `npm:<spec>` reference.\n *\n * When more than one is present the native `.agents-plugin` format wins, then\n * Claude, then Copilot (no merge) — matching the \"`.agents/` first\" policy used\n * across hoocode's resource surfaces.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { PLUGIN_FORMATS } from \"./formats/index.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/**\n * Canonical platform token used by the optional `supportPlatform` field. The\n * `.github/marketplace.json` (Copilot-style) format is surfaced as `\"github\"` —\n * friendlier than the internal `format: \"copilot\"` and matching where the file\n * lives. Authored aliases (`copilot`, `gh`) normalize to `github`.\n *\n * Re-exported from the format registry so the platform vocabulary has a single\n * source of truth.\n */\nexport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/** Map an internal marketplace `format` to its public platform token. */\nfunction formatToPlatform(format: NormalizedMarketplace[\"format\"]): MarketplacePlatform {\n\treturn format === \"copilot\" ? \"github\" : format;\n}\n\n/** Normalize an authored `supportPlatform` (string | string[]) to canonical tokens. */\nexport function normalizePlatforms(value: unknown): MarketplacePlatform[] {\n\tconst raw = Array.isArray(value) ? value : value == null ? [] : [value];\n\tconst out: MarketplacePlatform[] = [];\n\tfor (const v of raw) {\n\t\tif (typeof v !== \"string\") continue;\n\t\tconst t = v.trim().toLowerCase();\n\t\tconst canonical: MarketplacePlatform | undefined =\n\t\t\tt === \"agents\" || t === \"native\"\n\t\t\t\t? \"agents\"\n\t\t\t\t: t === \"claude\"\n\t\t\t\t\t? \"claude\"\n\t\t\t\t\t: t === \"github\" || t === \"copilot\" || t === \"gh\"\n\t\t\t\t\t\t? \"github\"\n\t\t\t\t\t\t: undefined;\n\t\tif (canonical && !out.includes(canonical)) out.push(canonical);\n\t}\n\treturn out;\n}\n\nexport interface MarketplacePluginEntry {\n\tname: string;\n\t/** Relative path, git URL, or `npm:<spec>`. */\n\tsource: string;\n\tdescription?: string;\n\t/**\n\t * Optional platform(s) this entry targets. Only set when authored on the\n\t * entry; absent means \"no per-entry restriction\" (the marketplace's\n\t * platforms apply). Purely informational today — nothing filters on it.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n}\n\nexport interface NormalizedMarketplace {\n\tname: string;\n\t/** Precedence winner when several index files conflict (agents > claude > copilot). */\n\tformat: \"agents\" | \"claude\" | \"copilot\";\n\t/**\n\t * Every platform this marketplace directory offers, so a conflict between\n\t * multiple index files (e.g. both `.github/` and `.claude-plugin/`) is\n\t * recorded rather than hidden. Always includes the resolved `format`'s\n\t * platform; also folds in any authored top-level `supportPlatform`. Never\n\t * empty.\n\t */\n\tsupportPlatform: MarketplacePlatform[];\n\t/** Absolute path to the marketplace directory. */\n\troot: string;\n\tmanifestPath: string;\n\tplugins: MarketplacePluginEntry[];\n}\n\ninterface RawMarketplace {\n\tname?: string;\n\towner?: string;\n\t/** Optional authored platform hint (string | string[]); folded into supportPlatform. */\n\tsupportPlatform?: unknown;\n\tplugins?: Array<{ name?: string; source?: string; description?: string; supportPlatform?: unknown }>;\n}\n\n/** A resolved, installable plugin source. */\nexport type ResolvedPluginSource =\n\t| { kind: \"local\"; path: string }\n\t| { kind: \"git\"; url: string }\n\t| { kind: \"npm\"; spec: string };\n\nfunction readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a marketplace directory. Native `.agents-plugin` wins, then Claude, then\n * Copilot when more than one file is present. Returns null if no manifest exists\n * or it is invalid.\n */\nexport function parseMarketplaceDir(dir: string): NormalizedMarketplace | null {\n\t// Record every index format present so a conflict (more than one file) is\n\t// visible via supportPlatform, not silently dropped. PLUGIN_FORMATS is\n\t// precedence-ordered (native > Claude > Copilot), so `present` inherits it.\n\tconst present: Array<{ format: NormalizedMarketplace[\"format\"]; path: string }> = [];\n\tfor (const fmt of PLUGIN_FORMATS) {\n\t\tconst p = path.join(dir, fmt.marketplaceFile);\n\t\tif (fs.existsSync(p)) present.push({ format: fmt.id, path: p });\n\t}\n\tif (present.length === 0) return null;\n\n\t// Precedence winner (present is built in agents > claude > copilot order).\n\tconst { format, path: manifestPath } = present[0];\n\n\tconst raw = readJson<RawMarketplace>(manifestPath);\n\tif (!raw) return null;\n\n\tconst plugins: MarketplacePluginEntry[] = [];\n\tfor (const entry of raw.plugins ?? []) {\n\t\tif (entry?.name && entry.source) {\n\t\t\tconst entryPlatforms = normalizePlatforms(entry.supportPlatform);\n\t\t\tplugins.push({\n\t\t\t\tname: entry.name,\n\t\t\t\tsource: entry.source,\n\t\t\t\tdescription: entry.description,\n\t\t\t\t...(entryPlatforms.length > 0 ? { supportPlatform: entryPlatforms } : {}),\n\t\t\t});\n\t\t}\n\t}\n\n\t// supportPlatform = platforms of all present index files, plus any authored\n\t// top-level hint, deduped. Always non-empty (at least the resolved format).\n\tconst supportPlatform: MarketplacePlatform[] = [];\n\tfor (const p of [...present.map((e) => formatToPlatform(e.format)), ...normalizePlatforms(raw.supportPlatform)]) {\n\t\tif (!supportPlatform.includes(p)) supportPlatform.push(p);\n\t}\n\n\treturn {\n\t\tname: (raw.name ?? path.basename(dir)).trim() || path.basename(dir),\n\t\tformat,\n\t\tsupportPlatform,\n\t\troot: dir,\n\t\tmanifestPath,\n\t\tplugins,\n\t};\n}\n\n/** Classify and resolve a plugin `source` against its marketplace root. */\nexport function resolvePluginSource(source: string, marketplaceRoot: string): ResolvedPluginSource {\n\tconst s = source.trim();\n\tif (s.startsWith(\"npm:\")) return { kind: \"npm\", spec: s.slice(4) };\n\tif (/^https?:\\/\\//.test(s) || s.startsWith(\"git@\") || s.endsWith(\".git\")) return { kind: \"git\", url: s };\n\t// Otherwise a path relative to (or absolute within) the marketplace repo.\n\treturn { kind: \"local\", path: path.isAbsolute(s) ? s : path.resolve(marketplaceRoot, s) };\n}\n\n// ============================================================================\n// Marketplace registry (persisted list of added marketplaces)\n// ============================================================================\n\nexport interface MarketplaceRecord {\n\t/** Original location the user added (git URL or local path). */\n\tlocation: string;\n\t/** Local directory to read the manifest from (clone dir for git, else location). */\n\tdir: string;\n}\n\ninterface MarketplaceStoreFile {\n\tmarketplaces?: MarketplaceRecord[];\n}\n\n/** Read the persisted marketplace list from `<storePath>`. */\nexport function readMarketplaceStore(storePath: string): MarketplaceRecord[] {\n\tconst parsed = readJson<MarketplaceStoreFile>(storePath);\n\treturn Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : [];\n}\n\n/** Write the marketplace list to `<storePath>`. */\nexport function writeMarketplaceStore(storePath: string, records: MarketplaceRecord[]): void {\n\tfs.mkdirSync(path.dirname(storePath), { recursive: true });\n\tfs.writeFileSync(storePath, `${JSON.stringify({ marketplaces: records }, null, 2)}\\n`, \"utf8\");\n}\n"]}
|
|
@@ -29,6 +29,8 @@ export declare const DEFAULT_SETTINGS: {
|
|
|
29
29
|
maxSubagentDepth: number;
|
|
30
30
|
nestedSubagentConcurrency: number;
|
|
31
31
|
enableTodoWrite: true;
|
|
32
|
+
enablePluginTools: true;
|
|
33
|
+
deferMcpSchemas: false;
|
|
32
34
|
enableWebTools: false;
|
|
33
35
|
enableBrowserTools: false;
|
|
34
36
|
enableBrowserLivePreview: false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-defaults.d.ts","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"settings-defaults.d.ts","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+DT,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t},\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: true,\n\tdeferMcpSchemas: false,\n\tenableWebTools: false,\n\tenableBrowserTools: false,\n\tenableBrowserLivePreview: false,\n\tenableFileTools: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorPaddingX: 0,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
|
|
@@ -29,6 +29,8 @@ export const DEFAULT_SETTINGS = {
|
|
|
29
29
|
maxSubagentDepth: 2,
|
|
30
30
|
nestedSubagentConcurrency: 2,
|
|
31
31
|
enableTodoWrite: true,
|
|
32
|
+
enablePluginTools: true,
|
|
33
|
+
deferMcpSchemas: false,
|
|
32
34
|
enableWebTools: false,
|
|
33
35
|
enableBrowserTools: false,
|
|
34
36
|
enableBrowserLivePreview: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-defaults.js","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE;QACX,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,KAAK;QACpB,gBAAgB,EAAE,KAAK;KACvB;IACD,aAAa,EAAE;QACd,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACN,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE;YACT,eAAe,EAAE,KAAK;SACtB;KACD;IACD,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,KAAK;IACxB,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,IAAI;IACzB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,CAAC;IACnB,yBAAyB,EAAE,CAAC;IAC5B,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,KAAK;IACrB,kBAAkB,EAAE,KAAK;IACzB,wBAAwB,EAAE,KAAK;IAC/B,eAAe,EAAE,KAAK;IACtB,QAAQ,EAAE;QACT,UAAU,EAAE,IAAI;QAChB,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,KAAK;QACpB,oBAAoB,EAAE,KAAK;KAC3B;IACD,MAAM,EAAE;QACP,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,KAAK;KAClB;IACD,kBAAkB,EAAE,MAAM;IAC1B,cAAc,EAAE,SAAS;IACzB,cAAc,EAAE,CAAC;IACjB,sBAAsB,EAAE,CAAC;IACzB,QAAQ,EAAE;QACT,eAAe,EAAE,IAAI;KACrB;IACD,QAAQ,EAAE;QACT,mBAAmB,EAAE,IAAI;KACzB;IACD,QAAQ,EAAE,EAAE;IACZ,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,aAAa,EAAE,EAAE;IACjB,MAAM,EAAE,EAAE;CACS,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t},\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenableWebTools: false,\n\tenableBrowserTools: false,\n\tenableBrowserLivePreview: false,\n\tenableFileTools: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorPaddingX: 0,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
|
|
1
|
+
{"version":3,"file":"settings-defaults.js","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE;QACX,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,KAAK;QACpB,gBAAgB,EAAE,KAAK;KACvB;IACD,aAAa,EAAE;QACd,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACN,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE;YACT,eAAe,EAAE,KAAK;SACtB;KACD;IACD,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,KAAK;IACxB,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,IAAI;IACzB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,CAAC;IACnB,yBAAyB,EAAE,CAAC;IAC5B,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,IAAI;IACvB,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,KAAK;IACrB,kBAAkB,EAAE,KAAK;IACzB,wBAAwB,EAAE,KAAK;IAC/B,eAAe,EAAE,KAAK;IACtB,QAAQ,EAAE;QACT,UAAU,EAAE,IAAI;QAChB,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,KAAK;QACpB,oBAAoB,EAAE,KAAK;KAC3B;IACD,MAAM,EAAE;QACP,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,KAAK;KAClB;IACD,kBAAkB,EAAE,MAAM;IAC1B,cAAc,EAAE,SAAS;IACzB,cAAc,EAAE,CAAC;IACjB,sBAAsB,EAAE,CAAC;IACzB,QAAQ,EAAE;QACT,eAAe,EAAE,IAAI;KACrB;IACD,QAAQ,EAAE;QACT,mBAAmB,EAAE,IAAI;KACzB;IACD,QAAQ,EAAE,EAAE;IACZ,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,aAAa,EAAE,EAAE;IACjB,MAAM,EAAE,EAAE;CACS,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t},\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: true,\n\tdeferMcpSchemas: false,\n\tenableWebTools: false,\n\tenableBrowserTools: false,\n\tenableBrowserLivePreview: false,\n\tenableFileTools: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorPaddingX: 0,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
|
|
@@ -128,6 +128,10 @@ export declare class SettingsManager {
|
|
|
128
128
|
getNestedSubagentConcurrency(): number;
|
|
129
129
|
getEnableTodoWrite(): boolean;
|
|
130
130
|
setEnableTodoWrite(enabled: boolean): void;
|
|
131
|
+
getEnablePluginTools(): boolean;
|
|
132
|
+
setEnablePluginTools(enabled: boolean): void;
|
|
133
|
+
getDeferMcpSchemas(): boolean;
|
|
134
|
+
setDeferMcpSchemas(enabled: boolean): void;
|
|
131
135
|
getEnableWebTools(): boolean;
|
|
132
136
|
setEnableWebTools(enabled: boolean): void;
|
|
133
137
|
getEnableBrowserTools(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-manager.d.ts","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { homedir } from \"os\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.js\";\nimport { DEFAULT_SETTINGS } from \"./settings-defaults.js\";\nimport {\n\tFileSettingsStorage,\n\tInMemorySettingsStorage,\n\ttype SettingsError,\n\ttype SettingsScope,\n\ttype SettingsStorage,\n} from \"./settings-storage.js\";\nimport type {\n\tBranchSummarySettings,\n\tCompactionSettings,\n\tPackageSource,\n\tSettings,\n\tThinkingBudgetsSettings,\n\tTransportSetting,\n\tWarningSettings,\n} from \"./settings-types.js\";\n\n/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */\nfunction deepMergeSettings(base: Settings, overrides: Settings): Settings {\n\tconst result: Settings = { ...base };\n\n\tfor (const key of Object.keys(overrides) as (keyof Settings)[]) {\n\t\tconst overrideValue = overrides[key];\n\t\tconst baseValue = base[key];\n\n\t\tif (overrideValue === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// For nested objects, merge recursively\n\t\tif (\n\t\t\ttypeof overrideValue === \"object\" &&\n\t\t\toverrideValue !== null &&\n\t\t\t!Array.isArray(overrideValue) &&\n\t\t\ttypeof baseValue === \"object\" &&\n\t\t\tbaseValue !== null &&\n\t\t\t!Array.isArray(baseValue)\n\t\t) {\n\t\t\t(result as Record<string, unknown>)[key] = { ...baseValue, ...overrideValue };\n\t\t} else {\n\t\t\t// For primitives and arrays, override value wins\n\t\t\t(result as Record<string, unknown>)[key] = overrideValue;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport { FileSettingsStorage, InMemorySettingsStorage };\nexport type { SettingsError, SettingsScope, SettingsStorage };\nexport type {\n\tBranchSummarySettings,\n\tCompactionSettings,\n\tImageSettings,\n\tMarkdownSettings,\n\tModelCategories,\n\tPackageSource,\n\tProviderRetrySettings,\n\tRetrySettings,\n\tSettings,\n\tTerminalSettings,\n\tThinkingBudgetsSettings,\n\tTransportSetting,\n\tWarningSettings,\n} from \"./settings-types.js\";\n\nexport class SettingsManager {\n\tprivate storage: SettingsStorage;\n\tprivate globalSettings: Settings;\n\tprivate projectSettings: Settings;\n\tprivate settings: Settings;\n\tprivate modifiedFields = new Set<keyof Settings>(); // Track global fields modified during session\n\tprivate modifiedNestedFields = new Map<keyof Settings, Set<string>>(); // Track global nested field modifications\n\tprivate modifiedProjectFields = new Set<keyof Settings>(); // Track project fields modified during session\n\tprivate modifiedProjectNestedFields = new Map<keyof Settings, Set<string>>(); // Track project nested field modifications\n\tprivate globalSettingsLoadError: Error | null = null; // Track if global settings file had parse errors\n\tprivate projectSettingsLoadError: Error | null = null; // Track if project settings file had parse errors\n\tprivate writeQueue: Promise<void> = Promise.resolve();\n\tprivate errors: SettingsError[];\n\n\tprivate constructor(\n\t\tstorage: SettingsStorage,\n\t\tinitialGlobal: Settings,\n\t\tinitialProject: Settings,\n\t\tglobalLoadError: Error | null = null,\n\t\tprojectLoadError: Error | null = null,\n\t\tinitialErrors: SettingsError[] = [],\n\t) {\n\t\tthis.storage = storage;\n\t\tthis.globalSettings = initialGlobal;\n\t\tthis.projectSettings = initialProject;\n\t\tthis.globalSettingsLoadError = globalLoadError;\n\t\tthis.projectSettingsLoadError = projectLoadError;\n\t\tthis.errors = [...initialErrors];\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\t}\n\n\t/** Create a SettingsManager that loads from files */\n\tstatic create(cwd: string, agentDir: string = getAgentDir()): SettingsManager {\n\t\tconst storage = new FileSettingsStorage(cwd, agentDir);\n\t\treturn SettingsManager.fromStorage(storage);\n\t}\n\n\t/** Create a SettingsManager from an arbitrary storage backend */\n\tstatic fromStorage(storage: SettingsStorage): SettingsManager {\n\t\tconst globalLoad = SettingsManager.tryLoadFromStorage(storage, \"global\");\n\t\tconst projectLoad = SettingsManager.tryLoadFromStorage(storage, \"project\");\n\t\tconst initialErrors: SettingsError[] = [];\n\t\tif (globalLoad.error) {\n\t\t\tinitialErrors.push({ scope: \"global\", error: globalLoad.error });\n\t\t}\n\t\tif (projectLoad.error) {\n\t\t\tinitialErrors.push({ scope: \"project\", error: projectLoad.error });\n\t\t}\n\n\t\treturn new SettingsManager(\n\t\t\tstorage,\n\t\t\tglobalLoad.settings,\n\t\t\tprojectLoad.settings,\n\t\t\tglobalLoad.error,\n\t\t\tprojectLoad.error,\n\t\t\tinitialErrors,\n\t\t);\n\t}\n\n\t/** Create an in-memory SettingsManager (no file I/O) */\n\tstatic inMemory(settings: Partial<Settings> = {}): SettingsManager {\n\t\tconst storage = new InMemorySettingsStorage();\n\t\tconst initialSettings = SettingsManager.migrateSettings(structuredClone(settings) as Record<string, unknown>);\n\t\tstorage.withLock(\"global\", () => JSON.stringify(initialSettings, null, 2));\n\t\treturn SettingsManager.fromStorage(storage);\n\t}\n\n\tprivate static loadFromStorage(storage: SettingsStorage, scope: SettingsScope): Settings {\n\t\tlet content: string | undefined;\n\t\tstorage.withLock(scope, (current) => {\n\t\t\tcontent = current;\n\t\t\treturn undefined;\n\t\t});\n\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\tconst settings = JSON.parse(content);\n\t\treturn SettingsManager.migrateSettings(settings);\n\t}\n\n\tprivate static tryLoadFromStorage(\n\t\tstorage: SettingsStorage,\n\t\tscope: SettingsScope,\n\t): { settings: Settings; error: Error | null } {\n\t\ttry {\n\t\t\treturn { settings: SettingsManager.loadFromStorage(storage, scope), error: null };\n\t\t} catch (error) {\n\t\t\treturn { settings: {}, error: error as Error };\n\t\t}\n\t}\n\n\t/** Migrate old settings format to new format */\n\tprivate static migrateSettings(settings: Record<string, unknown>): Settings {\n\t\t// Migrate queueMode -> steeringMode\n\t\tif (\"queueMode\" in settings && !(\"steeringMode\" in settings)) {\n\t\t\tsettings.steeringMode = settings.queueMode;\n\t\t\tdelete settings.queueMode;\n\t\t}\n\n\t\t// Migrate legacy websockets boolean -> transport enum\n\t\tif (!(\"transport\" in settings) && typeof settings.websockets === \"boolean\") {\n\t\t\tsettings.transport = settings.websockets ? \"websocket\" : \"sse\";\n\t\t\tdelete settings.websockets;\n\t\t}\n\n\t\t// Migrate old skills object format to new array format\n\t\tif (\n\t\t\t\"skills\" in settings &&\n\t\t\ttypeof settings.skills === \"object\" &&\n\t\t\tsettings.skills !== null &&\n\t\t\t!Array.isArray(settings.skills)\n\t\t) {\n\t\t\tconst skillsSettings = settings.skills as {\n\t\t\t\tenableSkillCommands?: boolean;\n\t\t\t\tcustomDirectories?: unknown;\n\t\t\t};\n\t\t\tif (skillsSettings.enableSkillCommands !== undefined && settings.enableSkillCommands === undefined) {\n\t\t\t\tsettings.enableSkillCommands = skillsSettings.enableSkillCommands;\n\t\t\t}\n\t\t\tif (Array.isArray(skillsSettings.customDirectories) && skillsSettings.customDirectories.length > 0) {\n\t\t\t\tsettings.skills = skillsSettings.customDirectories;\n\t\t\t} else {\n\t\t\t\tdelete settings.skills;\n\t\t\t}\n\t\t}\n\n\t\t// Migrate retry.maxDelayMs -> retry.provider.maxRetryDelayMs\n\t\tif (\n\t\t\t\"retry\" in settings &&\n\t\t\ttypeof settings.retry === \"object\" &&\n\t\t\tsettings.retry !== null &&\n\t\t\t!Array.isArray(settings.retry)\n\t\t) {\n\t\t\tconst retrySettings = settings.retry as Record<string, unknown>;\n\t\t\tconst providerSettings =\n\t\t\t\ttypeof retrySettings.provider === \"object\" && retrySettings.provider !== null\n\t\t\t\t\t? (retrySettings.provider as Record<string, unknown>)\n\t\t\t\t\t: undefined;\n\t\t\tif (\n\t\t\t\ttypeof retrySettings.maxDelayMs === \"number\" &&\n\t\t\t\t(providerSettings?.maxRetryDelayMs === undefined || providerSettings?.maxRetryDelayMs === null)\n\t\t\t) {\n\t\t\t\tretrySettings.provider = {\n\t\t\t\t\t...(providerSettings ?? {}),\n\t\t\t\t\tmaxRetryDelayMs: retrySettings.maxDelayMs,\n\t\t\t\t};\n\t\t\t}\n\t\t\tdelete retrySettings.maxDelayMs;\n\t\t}\n\n\t\treturn settings as Settings;\n\t}\n\n\tgetGlobalSettings(): Settings {\n\t\treturn structuredClone(this.globalSettings);\n\t}\n\n\tgetProjectSettings(): Settings {\n\t\treturn structuredClone(this.projectSettings);\n\t}\n\n\tgetDefaultSettings(): Settings {\n\t\treturn structuredClone(DEFAULT_SETTINGS);\n\t}\n\n\tasync reload(): Promise<void> {\n\t\tawait this.writeQueue;\n\t\tconst globalLoad = SettingsManager.tryLoadFromStorage(this.storage, \"global\");\n\t\tif (!globalLoad.error) {\n\t\t\tthis.globalSettings = globalLoad.settings;\n\t\t\tthis.globalSettingsLoadError = null;\n\t\t} else {\n\t\t\tthis.globalSettingsLoadError = globalLoad.error;\n\t\t\tthis.recordError(\"global\", globalLoad.error);\n\t\t}\n\n\t\tthis.modifiedFields.clear();\n\t\tthis.modifiedNestedFields.clear();\n\t\tthis.modifiedProjectFields.clear();\n\t\tthis.modifiedProjectNestedFields.clear();\n\n\t\tconst projectLoad = SettingsManager.tryLoadFromStorage(this.storage, \"project\");\n\t\tif (!projectLoad.error) {\n\t\t\tthis.projectSettings = projectLoad.settings;\n\t\t\tthis.projectSettingsLoadError = null;\n\t\t} else {\n\t\t\tthis.projectSettingsLoadError = projectLoad.error;\n\t\t\tthis.recordError(\"project\", projectLoad.error);\n\t\t}\n\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\t}\n\n\t/** Apply additional overrides on top of current settings */\n\tapplyOverrides(overrides: Partial<Settings>): void {\n\t\tthis.settings = deepMergeSettings(this.settings, overrides);\n\t}\n\n\t/** Mark a global field as modified during this session */\n\tprivate markModified(field: keyof Settings, nestedKey?: string): void {\n\t\tthis.modifiedFields.add(field);\n\t\tif (nestedKey) {\n\t\t\tif (!this.modifiedNestedFields.has(field)) {\n\t\t\t\tthis.modifiedNestedFields.set(field, new Set());\n\t\t\t}\n\t\t\tthis.modifiedNestedFields.get(field)!.add(nestedKey);\n\t\t}\n\t}\n\n\t/** Mark a project field as modified during this session */\n\tprivate markProjectModified(field: keyof Settings, nestedKey?: string): void {\n\t\tthis.modifiedProjectFields.add(field);\n\t\tif (nestedKey) {\n\t\t\tif (!this.modifiedProjectNestedFields.has(field)) {\n\t\t\t\tthis.modifiedProjectNestedFields.set(field, new Set());\n\t\t\t}\n\t\t\tthis.modifiedProjectNestedFields.get(field)!.add(nestedKey);\n\t\t}\n\t}\n\n\tprivate recordError(scope: SettingsScope, error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push({ scope, error: normalizedError });\n\t}\n\n\tprivate clearModifiedScope(scope: SettingsScope): void {\n\t\tif (scope === \"global\") {\n\t\t\tthis.modifiedFields.clear();\n\t\t\tthis.modifiedNestedFields.clear();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.modifiedProjectFields.clear();\n\t\tthis.modifiedProjectNestedFields.clear();\n\t}\n\n\tprivate enqueueWrite(scope: SettingsScope, task: () => void): void {\n\t\tthis.writeQueue = this.writeQueue\n\t\t\t.then(() => {\n\t\t\t\ttask();\n\t\t\t\tthis.clearModifiedScope(scope);\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthis.recordError(scope, error);\n\t\t\t});\n\t}\n\n\tprivate cloneModifiedNestedFields(source: Map<keyof Settings, Set<string>>): Map<keyof Settings, Set<string>> {\n\t\tconst snapshot = new Map<keyof Settings, Set<string>>();\n\t\tfor (const [key, value] of source.entries()) {\n\t\t\tsnapshot.set(key, new Set(value));\n\t\t}\n\t\treturn snapshot;\n\t}\n\n\tprivate persistScopedSettings(\n\t\tscope: SettingsScope,\n\t\tsnapshotSettings: Settings,\n\t\tmodifiedFields: Set<keyof Settings>,\n\t\tmodifiedNestedFields: Map<keyof Settings, Set<string>>,\n\t): void {\n\t\tthis.storage.withLock(scope, (current) => {\n\t\t\tconst currentFileSettings = current\n\t\t\t\t? SettingsManager.migrateSettings(JSON.parse(current) as Record<string, unknown>)\n\t\t\t\t: {};\n\t\t\tconst mergedSettings: Settings = { ...currentFileSettings };\n\t\t\tfor (const field of modifiedFields) {\n\t\t\t\tconst value = snapshotSettings[field];\n\t\t\t\tif (modifiedNestedFields.has(field) && typeof value === \"object\" && value !== null) {\n\t\t\t\t\tconst nestedModified = modifiedNestedFields.get(field)!;\n\t\t\t\t\tconst baseNested = (currentFileSettings[field] as Record<string, unknown>) ?? {};\n\t\t\t\t\tconst inMemoryNested = value as Record<string, unknown>;\n\t\t\t\t\tconst mergedNested = { ...baseNested };\n\t\t\t\t\tfor (const nestedKey of nestedModified) {\n\t\t\t\t\t\tmergedNested[nestedKey] = inMemoryNested[nestedKey];\n\t\t\t\t\t}\n\t\t\t\t\t(mergedSettings as Record<string, unknown>)[field] = mergedNested;\n\t\t\t\t} else {\n\t\t\t\t\t(mergedSettings as Record<string, unknown>)[field] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn JSON.stringify(mergedSettings, null, 2);\n\t\t});\n\t}\n\n\tprivate save(): void {\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\n\t\tif (this.globalSettingsLoadError) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst snapshotGlobalSettings = structuredClone(this.globalSettings);\n\t\tconst modifiedFields = new Set(this.modifiedFields);\n\t\tconst modifiedNestedFields = this.cloneModifiedNestedFields(this.modifiedNestedFields);\n\n\t\tthis.enqueueWrite(\"global\", () => {\n\t\t\tthis.persistScopedSettings(\"global\", snapshotGlobalSettings, modifiedFields, modifiedNestedFields);\n\t\t});\n\t}\n\n\tprivate saveProjectSettings(settings: Settings): void {\n\t\tthis.projectSettings = structuredClone(settings);\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\n\t\tif (this.projectSettingsLoadError) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst snapshotProjectSettings = structuredClone(this.projectSettings);\n\t\tconst modifiedFields = new Set(this.modifiedProjectFields);\n\t\tconst modifiedNestedFields = this.cloneModifiedNestedFields(this.modifiedProjectNestedFields);\n\t\tthis.enqueueWrite(\"project\", () => {\n\t\t\tthis.persistScopedSettings(\"project\", snapshotProjectSettings, modifiedFields, modifiedNestedFields);\n\t\t});\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tawait this.writeQueue;\n\t}\n\n\tdrainErrors(): SettingsError[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\tgetLastChangelogVersion(): string | undefined {\n\t\treturn this.settings.lastChangelogVersion;\n\t}\n\n\tsetLastChangelogVersion(version: string): void {\n\t\tthis.globalSettings.lastChangelogVersion = version;\n\t\tthis.markModified(\"lastChangelogVersion\");\n\t\tthis.save();\n\t}\n\n\tgetSessionDir(): string | undefined {\n\t\tconst sessionDir = this.settings.sessionDir;\n\t\tif (!sessionDir) {\n\t\t\treturn sessionDir;\n\t\t}\n\t\tif (sessionDir === \"~\") {\n\t\t\treturn homedir();\n\t\t}\n\t\tif (sessionDir.startsWith(\"~/\")) {\n\t\t\treturn join(homedir(), sessionDir.slice(2));\n\t\t}\n\t\treturn sessionDir;\n\t}\n\n\tgetDefaultProvider(): string | undefined {\n\t\treturn this.settings.defaultProvider;\n\t}\n\n\tgetDefaultModel(): string | undefined {\n\t\treturn this.settings.defaultModel;\n\t}\n\n\tsetDefaultProvider(provider: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.markModified(\"defaultProvider\");\n\t\tthis.save();\n\t}\n\n\tsetDefaultModel(modelId: string): void {\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.markModified(\"defaultModel\");\n\t\tthis.save();\n\t}\n\n\tsetDefaultModelAndProvider(provider: string, modelId: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.markModified(\"defaultProvider\");\n\t\tthis.markModified(\"defaultModel\");\n\t\tthis.save();\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.steeringMode || DEFAULT_SETTINGS.steeringMode!;\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.steeringMode = mode;\n\t\tthis.markModified(\"steeringMode\");\n\t\tthis.save();\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.followUpMode || DEFAULT_SETTINGS.followUpMode!;\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.followUpMode = mode;\n\t\tthis.markModified(\"followUpMode\");\n\t\tthis.save();\n\t}\n\n\tgetTheme(): string | undefined {\n\t\treturn this.settings.theme;\n\t}\n\n\tsetTheme(theme: string): void {\n\t\tthis.globalSettings.theme = theme;\n\t\tthis.markModified(\"theme\");\n\t\tthis.save();\n\t}\n\n\tgetDefaultThinkingLevel(): \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | undefined {\n\t\treturn this.settings.defaultThinkingLevel;\n\t}\n\n\tsetDefaultThinkingLevel(level: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"): void {\n\t\tthis.globalSettings.defaultThinkingLevel = level;\n\t\tthis.markModified(\"defaultThinkingLevel\");\n\t\tthis.save();\n\t}\n\n\tgetTransport(): TransportSetting {\n\t\treturn this.settings.transport ?? DEFAULT_SETTINGS.transport!;\n\t}\n\n\tsetTransport(transport: TransportSetting): void {\n\t\tthis.globalSettings.transport = transport;\n\t\tthis.markModified(\"transport\");\n\t\tthis.save();\n\t}\n\n\tgetCompactionEnabled(): boolean {\n\t\treturn this.settings.compaction?.enabled ?? DEFAULT_SETTINGS.compaction!.enabled;\n\t}\n\n\tsetCompactionEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.compaction) {\n\t\t\tthis.globalSettings.compaction = {};\n\t\t}\n\t\tthis.globalSettings.compaction.enabled = enabled;\n\t\tthis.markModified(\"compaction\", \"enabled\");\n\t\tthis.save();\n\t}\n\n\tgetCompactionReserveTokens(): number {\n\t\treturn this.settings.compaction?.reserveTokens ?? DEFAULT_SETTINGS.compaction!.reserveTokens;\n\t}\n\n\tgetCompactionKeepRecentTokens(): number {\n\t\treturn this.settings.compaction?.keepRecentTokens ?? DEFAULT_SETTINGS.compaction!.keepRecentTokens;\n\t}\n\n\tgetCompactionSettings(): Required<CompactionSettings> {\n\t\treturn { ...DEFAULT_SETTINGS.compaction, ...this.settings.compaction };\n\t}\n\n\tgetBranchSummarySettings(): Required<BranchSummarySettings> {\n\t\treturn { ...DEFAULT_SETTINGS.branchSummary, ...this.settings.branchSummary };\n\t}\n\n\tgetBranchSummarySkipPrompt(): boolean {\n\t\treturn this.settings.branchSummary?.skipPrompt ?? DEFAULT_SETTINGS.branchSummary!.skipPrompt;\n\t}\n\n\tgetRetryEnabled(): boolean {\n\t\treturn this.settings.retry?.enabled ?? DEFAULT_SETTINGS.retry!.enabled;\n\t}\n\n\tsetRetryEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.retry) {\n\t\t\tthis.globalSettings.retry = {};\n\t\t}\n\t\tthis.globalSettings.retry.enabled = enabled;\n\t\tthis.markModified(\"retry\", \"enabled\");\n\t\tthis.save();\n\t}\n\n\tgetRetrySettings(): { enabled: boolean; maxRetries: number; baseDelayMs: number } {\n\t\treturn {\n\t\t\tenabled: this.getRetryEnabled(),\n\t\t\tmaxRetries: this.settings.retry?.maxRetries ?? DEFAULT_SETTINGS.retry!.maxRetries,\n\t\t\tbaseDelayMs: this.settings.retry?.baseDelayMs ?? DEFAULT_SETTINGS.retry!.baseDelayMs,\n\t\t};\n\t}\n\n\tgetProviderRetrySettings(): { timeoutMs?: number; maxRetries?: number; maxRetryDelayMs: number } {\n\t\treturn {\n\t\t\t...DEFAULT_SETTINGS.retry!.provider,\n\t\t\t...this.settings.retry?.provider,\n\t\t};\n\t}\n\n\tgetHideThinkingBlock(): boolean {\n\t\treturn this.settings.hideThinkingBlock ?? DEFAULT_SETTINGS.hideThinkingBlock!;\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.globalSettings.hideThinkingBlock = hide;\n\t\tthis.markModified(\"hideThinkingBlock\");\n\t\tthis.save();\n\t}\n\n\tgetShellPath(): string | undefined {\n\t\treturn this.settings.shellPath;\n\t}\n\n\tsetShellPath(path: string | undefined): void {\n\t\tthis.globalSettings.shellPath = path;\n\t\tthis.markModified(\"shellPath\");\n\t\tthis.save();\n\t}\n\n\tgetQuietStartup(): boolean {\n\t\treturn this.settings.quietStartup ?? DEFAULT_SETTINGS.quietStartup!;\n\t}\n\n\tsetQuietStartup(quiet: boolean): void {\n\t\tthis.globalSettings.quietStartup = quiet;\n\t\tthis.markModified(\"quietStartup\");\n\t\tthis.save();\n\t}\n\n\tgetShellCommandPrefix(): string | undefined {\n\t\treturn this.settings.shellCommandPrefix;\n\t}\n\n\tsetShellCommandPrefix(prefix: string | undefined): void {\n\t\tthis.globalSettings.shellCommandPrefix = prefix;\n\t\tthis.markModified(\"shellCommandPrefix\");\n\t\tthis.save();\n\t}\n\n\tgetNpmCommand(): string[] | undefined {\n\t\treturn this.settings.npmCommand ? [...this.settings.npmCommand] : undefined;\n\t}\n\n\tsetNpmCommand(command: string[] | undefined): void {\n\t\tthis.globalSettings.npmCommand = command ? [...command] : undefined;\n\t\tthis.markModified(\"npmCommand\");\n\t\tthis.save();\n\t}\n\n\tgetCollapseChangelog(): boolean {\n\t\treturn this.settings.collapseChangelog ?? DEFAULT_SETTINGS.collapseChangelog!;\n\t}\n\n\tsetCollapseChangelog(collapse: boolean): void {\n\t\tthis.globalSettings.collapseChangelog = collapse;\n\t\tthis.markModified(\"collapseChangelog\");\n\t\tthis.save();\n\t}\n\n\tgetEnableInstallTelemetry(): boolean {\n\t\treturn this.settings.enableInstallTelemetry ?? DEFAULT_SETTINGS.enableInstallTelemetry!;\n\t}\n\n\tsetEnableInstallTelemetry(enabled: boolean): void {\n\t\tthis.globalSettings.enableInstallTelemetry = enabled;\n\t\tthis.markModified(\"enableInstallTelemetry\");\n\t\tthis.save();\n\t}\n\n\tgetPackages(): PackageSource[] {\n\t\treturn [...(this.settings.packages ?? DEFAULT_SETTINGS.packages!)];\n\t}\n\n\tsetPackages(packages: PackageSource[]): void {\n\t\tthis.globalSettings.packages = packages;\n\t\tthis.markModified(\"packages\");\n\t\tthis.save();\n\t}\n\n\tsetProjectPackages(packages: PackageSource[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.packages = packages;\n\t\tthis.markProjectModified(\"packages\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetExtensionPaths(): string[] {\n\t\treturn [...(this.settings.extensions ?? DEFAULT_SETTINGS.extensions!)];\n\t}\n\n\tsetExtensionPaths(paths: string[]): void {\n\t\tthis.globalSettings.extensions = paths;\n\t\tthis.markModified(\"extensions\");\n\t\tthis.save();\n\t}\n\n\tsetProjectExtensionPaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.extensions = paths;\n\t\tthis.markProjectModified(\"extensions\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetSkillPaths(): string[] {\n\t\treturn [...(this.settings.skills ?? DEFAULT_SETTINGS.skills!)];\n\t}\n\n\tsetSkillPaths(paths: string[]): void {\n\t\tthis.globalSettings.skills = paths;\n\t\tthis.markModified(\"skills\");\n\t\tthis.save();\n\t}\n\n\tsetProjectSkillPaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.skills = paths;\n\t\tthis.markProjectModified(\"skills\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetPromptTemplatePaths(): string[] {\n\t\treturn [...(this.settings.prompts ?? DEFAULT_SETTINGS.prompts!)];\n\t}\n\n\tsetPromptTemplatePaths(paths: string[]): void {\n\t\tthis.globalSettings.prompts = paths;\n\t\tthis.markModified(\"prompts\");\n\t\tthis.save();\n\t}\n\n\tsetProjectPromptTemplatePaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.prompts = paths;\n\t\tthis.markProjectModified(\"prompts\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetSlashCommandPaths(): string[] {\n\t\treturn [...(this.settings.slashCommands ?? DEFAULT_SETTINGS.slashCommands!)];\n\t}\n\n\tsetSlashCommandPaths(paths: string[]): void {\n\t\tthis.globalSettings.slashCommands = paths;\n\t\tthis.markModified(\"slashCommands\");\n\t\tthis.save();\n\t}\n\n\tsetProjectSlashCommandPaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.slashCommands = paths;\n\t\tthis.markProjectModified(\"slashCommands\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetThemePaths(): string[] {\n\t\treturn [...(this.settings.themes ?? DEFAULT_SETTINGS.themes!)];\n\t}\n\n\tsetThemePaths(paths: string[]): void {\n\t\tthis.globalSettings.themes = paths;\n\t\tthis.markModified(\"themes\");\n\t\tthis.save();\n\t}\n\n\tsetProjectThemePaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.themes = paths;\n\t\tthis.markProjectModified(\"themes\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetEnableSkillCommands(): boolean {\n\t\treturn this.settings.enableSkillCommands ?? DEFAULT_SETTINGS.enableSkillCommands!;\n\t}\n\n\tsetEnableSkillCommands(enabled: boolean): void {\n\t\tthis.globalSettings.enableSkillCommands = enabled;\n\t\tthis.markModified(\"enableSkillCommands\");\n\t\tthis.save();\n\t}\n\n\tgetEnableSubagent(): boolean {\n\t\treturn this.settings.enableSubagent ?? DEFAULT_SETTINGS.enableSubagent!;\n\t}\n\n\tsetEnableSubagent(enabled: boolean): void {\n\t\tthis.globalSettings.enableSubagent = enabled;\n\t\tthis.markModified(\"enableSubagent\");\n\t\tthis.save();\n\t}\n\n\t/** Whether to dispatch eligible subagents on reused warm workers (experimental). */\n\tgetWarmSubagents(): boolean {\n\t\treturn this.settings.warmSubagents ?? DEFAULT_SETTINGS.warmSubagents!;\n\t}\n\n\tsetWarmSubagents(enabled: boolean): void {\n\t\tthis.globalSettings.warmSubagents = enabled;\n\t\tthis.markModified(\"warmSubagents\");\n\t\tthis.save();\n\t}\n\n\t/** Tree-wide subagent nesting cap. Clamped to >= 1 (delegation can never be disabled here). */\n\tgetMaxSubagentDepth(): number {\n\t\tconst v = this.settings.maxSubagentDepth ?? DEFAULT_SETTINGS.maxSubagentDepth!;\n\t\treturn Number.isFinite(v) && v >= 1 ? Math.floor(v) : 1;\n\t}\n\n\t/** Max concurrent subagents per pool at nesting depth >= 1. Clamped to >= 1. */\n\tgetNestedSubagentConcurrency(): number {\n\t\tconst v = this.settings.nestedSubagentConcurrency ?? DEFAULT_SETTINGS.nestedSubagentConcurrency!;\n\t\treturn Number.isFinite(v) && v >= 1 ? Math.floor(v) : 2;\n\t}\n\n\tgetEnableTodoWrite(): boolean {\n\t\treturn this.settings.enableTodoWrite ?? DEFAULT_SETTINGS.enableTodoWrite!;\n\t}\n\n\tsetEnableTodoWrite(enabled: boolean): void {\n\t\tthis.globalSettings.enableTodoWrite = enabled;\n\t\tthis.markModified(\"enableTodoWrite\");\n\t\tthis.save();\n\t}\n\n\tgetEnableWebTools(): boolean {\n\t\treturn this.settings.enableWebTools ?? DEFAULT_SETTINGS.enableWebTools!;\n\t}\n\n\tsetEnableWebTools(enabled: boolean): void {\n\t\tthis.globalSettings.enableWebTools = enabled;\n\t\tthis.markModified(\"enableWebTools\");\n\t\tthis.save();\n\t}\n\n\tgetEnableBrowserTools(): boolean {\n\t\treturn this.settings.enableBrowserTools ?? DEFAULT_SETTINGS.enableBrowserTools!;\n\t}\n\n\tsetEnableBrowserTools(enabled: boolean): void {\n\t\tthis.globalSettings.enableBrowserTools = enabled;\n\t\tthis.markModified(\"enableBrowserTools\");\n\t\tthis.save();\n\t}\n\n\tgetEnableBrowserLivePreview(): boolean {\n\t\treturn this.settings.enableBrowserLivePreview ?? DEFAULT_SETTINGS.enableBrowserLivePreview!;\n\t}\n\n\tsetEnableBrowserLivePreview(enabled: boolean): void {\n\t\tthis.globalSettings.enableBrowserLivePreview = enabled;\n\t\tthis.markModified(\"enableBrowserLivePreview\");\n\t\tthis.save();\n\t}\n\n\tgetEnableFileTools(): boolean {\n\t\treturn this.settings.enableFileTools ?? DEFAULT_SETTINGS.enableFileTools!;\n\t}\n\n\tsetEnableFileTools(enabled: boolean): void {\n\t\tthis.globalSettings.enableFileTools = enabled;\n\t\tthis.markModified(\"enableFileTools\");\n\t\tthis.save();\n\t}\n\n\tgetThinkingBudgets(): ThinkingBudgetsSettings | undefined {\n\t\treturn this.settings.thinkingBudgets;\n\t}\n\n\tgetThinkingDisplay(): \"summarized\" | \"omitted\" | undefined {\n\t\treturn this.settings.thinkingDisplay;\n\t}\n\n\tgetShowImages(): boolean {\n\t\treturn this.settings.terminal?.showImages ?? DEFAULT_SETTINGS.terminal!.showImages;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.showImages = show;\n\t\tthis.markModified(\"terminal\", \"showImages\");\n\t\tthis.save();\n\t}\n\n\tgetImageWidthCells(): number {\n\t\tconst width = this.settings.terminal?.imageWidthCells ?? DEFAULT_SETTINGS.terminal!.imageWidthCells;\n\t\tif (typeof width !== \"number\" || !Number.isFinite(width)) {\n\t\t\treturn DEFAULT_SETTINGS.terminal!.imageWidthCells;\n\t\t}\n\t\treturn Math.max(1, Math.floor(width));\n\t}\n\n\tsetImageWidthCells(width: number): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.imageWidthCells = Math.max(1, Math.floor(width));\n\t\tthis.markModified(\"terminal\", \"imageWidthCells\");\n\t\tthis.save();\n\t}\n\n\tgetClearOnShrink(): boolean {\n\t\t// Settings takes precedence, then env var, then default false\n\t\tif (this.settings.terminal?.clearOnShrink !== undefined) {\n\t\t\treturn this.settings.terminal.clearOnShrink;\n\t\t}\n\t\treturn process.env.HOOCODE_CLEAR_ON_SHRINK === \"1\";\n\t}\n\n\tsetClearOnShrink(enabled: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.clearOnShrink = enabled;\n\t\tthis.markModified(\"terminal\", \"clearOnShrink\");\n\t\tthis.save();\n\t}\n\n\tgetShowTerminalProgress(): boolean {\n\t\treturn this.settings.terminal?.showTerminalProgress ?? DEFAULT_SETTINGS.terminal!.showTerminalProgress;\n\t}\n\n\tsetShowTerminalProgress(enabled: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.showTerminalProgress = enabled;\n\t\tthis.markModified(\"terminal\", \"showTerminalProgress\");\n\t\tthis.save();\n\t}\n\n\tgetImageAutoResize(): boolean {\n\t\treturn this.settings.images?.autoResize ?? DEFAULT_SETTINGS.images!.autoResize;\n\t}\n\n\tsetImageAutoResize(enabled: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.autoResize = enabled;\n\t\tthis.markModified(\"images\", \"autoResize\");\n\t\tthis.save();\n\t}\n\n\tgetBlockImages(): boolean {\n\t\treturn this.settings.images?.blockImages ?? DEFAULT_SETTINGS.images!.blockImages;\n\t}\n\n\tsetBlockImages(blocked: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.blockImages = blocked;\n\t\tthis.markModified(\"images\", \"blockImages\");\n\t\tthis.save();\n\t}\n\n\tgetEnabledModels(): string[] | undefined {\n\t\treturn this.settings.enabledModels;\n\t}\n\n\tsetEnabledModels(patterns: string[] | undefined): void {\n\t\tthis.globalSettings.enabledModels = patterns;\n\t\tthis.markModified(\"enabledModels\");\n\t\tthis.save();\n\t}\n\n\tgetDoubleEscapeAction(): \"fork\" | \"tree\" | \"none\" {\n\t\treturn this.settings.doubleEscapeAction ?? DEFAULT_SETTINGS.doubleEscapeAction!;\n\t}\n\n\tsetDoubleEscapeAction(action: \"fork\" | \"tree\" | \"none\"): void {\n\t\tthis.globalSettings.doubleEscapeAction = action;\n\t\tthis.markModified(\"doubleEscapeAction\");\n\t\tthis.save();\n\t}\n\n\tgetTreeFilterMode(): \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\" {\n\t\tconst mode = this.settings.treeFilterMode;\n\t\tconst valid = [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"];\n\t\treturn mode && valid.includes(mode) ? mode : DEFAULT_SETTINGS.treeFilterMode!;\n\t}\n\n\tsetTreeFilterMode(mode: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\"): void {\n\t\tthis.globalSettings.treeFilterMode = mode;\n\t\tthis.markModified(\"treeFilterMode\");\n\t\tthis.save();\n\t}\n\n\tgetShowHardwareCursor(): boolean {\n\t\treturn this.settings.showHardwareCursor ?? process.env.HOOCODE_HARDWARE_CURSOR === \"1\";\n\t}\n\n\tsetShowHardwareCursor(enabled: boolean): void {\n\t\tthis.globalSettings.showHardwareCursor = enabled;\n\t\tthis.markModified(\"showHardwareCursor\");\n\t\tthis.save();\n\t}\n\n\tgetEditorPaddingX(): number {\n\t\treturn this.settings.editorPaddingX ?? DEFAULT_SETTINGS.editorPaddingX!;\n\t}\n\n\tsetEditorPaddingX(padding: number): void {\n\t\tthis.globalSettings.editorPaddingX = Math.max(0, Math.min(3, Math.floor(padding)));\n\t\tthis.markModified(\"editorPaddingX\");\n\t\tthis.save();\n\t}\n\n\tgetAutocompleteMaxVisible(): number {\n\t\treturn this.settings.autocompleteMaxVisible ?? DEFAULT_SETTINGS.autocompleteMaxVisible!;\n\t}\n\n\tsetAutocompleteMaxVisible(maxVisible: number): void {\n\t\tthis.globalSettings.autocompleteMaxVisible = Math.max(3, Math.min(20, Math.floor(maxVisible)));\n\t\tthis.markModified(\"autocompleteMaxVisible\");\n\t\tthis.save();\n\t}\n\n\tgetCodeBlockIndent(): string {\n\t\treturn this.settings.markdown?.codeBlockIndent ?? DEFAULT_SETTINGS.markdown!.codeBlockIndent;\n\t}\n\n\tgetWarnings(): WarningSettings {\n\t\treturn { ...DEFAULT_SETTINGS.warnings, ...this.settings.warnings };\n\t}\n\n\tsetWarnings(warnings: WarningSettings): void {\n\t\tthis.globalSettings.warnings = { ...warnings };\n\t\tthis.markModified(\"warnings\");\n\t\tthis.save();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"settings-manager.d.ts","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { homedir } from \"os\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.js\";\nimport { DEFAULT_SETTINGS } from \"./settings-defaults.js\";\nimport {\n\tFileSettingsStorage,\n\tInMemorySettingsStorage,\n\ttype SettingsError,\n\ttype SettingsScope,\n\ttype SettingsStorage,\n} from \"./settings-storage.js\";\nimport type {\n\tBranchSummarySettings,\n\tCompactionSettings,\n\tPackageSource,\n\tSettings,\n\tThinkingBudgetsSettings,\n\tTransportSetting,\n\tWarningSettings,\n} from \"./settings-types.js\";\n\n/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */\nfunction deepMergeSettings(base: Settings, overrides: Settings): Settings {\n\tconst result: Settings = { ...base };\n\n\tfor (const key of Object.keys(overrides) as (keyof Settings)[]) {\n\t\tconst overrideValue = overrides[key];\n\t\tconst baseValue = base[key];\n\n\t\tif (overrideValue === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// For nested objects, merge recursively\n\t\tif (\n\t\t\ttypeof overrideValue === \"object\" &&\n\t\t\toverrideValue !== null &&\n\t\t\t!Array.isArray(overrideValue) &&\n\t\t\ttypeof baseValue === \"object\" &&\n\t\t\tbaseValue !== null &&\n\t\t\t!Array.isArray(baseValue)\n\t\t) {\n\t\t\t(result as Record<string, unknown>)[key] = { ...baseValue, ...overrideValue };\n\t\t} else {\n\t\t\t// For primitives and arrays, override value wins\n\t\t\t(result as Record<string, unknown>)[key] = overrideValue;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport { FileSettingsStorage, InMemorySettingsStorage };\nexport type { SettingsError, SettingsScope, SettingsStorage };\nexport type {\n\tBranchSummarySettings,\n\tCompactionSettings,\n\tImageSettings,\n\tMarkdownSettings,\n\tModelCategories,\n\tPackageSource,\n\tProviderRetrySettings,\n\tRetrySettings,\n\tSettings,\n\tTerminalSettings,\n\tThinkingBudgetsSettings,\n\tTransportSetting,\n\tWarningSettings,\n} from \"./settings-types.js\";\n\nexport class SettingsManager {\n\tprivate storage: SettingsStorage;\n\tprivate globalSettings: Settings;\n\tprivate projectSettings: Settings;\n\tprivate settings: Settings;\n\tprivate modifiedFields = new Set<keyof Settings>(); // Track global fields modified during session\n\tprivate modifiedNestedFields = new Map<keyof Settings, Set<string>>(); // Track global nested field modifications\n\tprivate modifiedProjectFields = new Set<keyof Settings>(); // Track project fields modified during session\n\tprivate modifiedProjectNestedFields = new Map<keyof Settings, Set<string>>(); // Track project nested field modifications\n\tprivate globalSettingsLoadError: Error | null = null; // Track if global settings file had parse errors\n\tprivate projectSettingsLoadError: Error | null = null; // Track if project settings file had parse errors\n\tprivate writeQueue: Promise<void> = Promise.resolve();\n\tprivate errors: SettingsError[];\n\n\tprivate constructor(\n\t\tstorage: SettingsStorage,\n\t\tinitialGlobal: Settings,\n\t\tinitialProject: Settings,\n\t\tglobalLoadError: Error | null = null,\n\t\tprojectLoadError: Error | null = null,\n\t\tinitialErrors: SettingsError[] = [],\n\t) {\n\t\tthis.storage = storage;\n\t\tthis.globalSettings = initialGlobal;\n\t\tthis.projectSettings = initialProject;\n\t\tthis.globalSettingsLoadError = globalLoadError;\n\t\tthis.projectSettingsLoadError = projectLoadError;\n\t\tthis.errors = [...initialErrors];\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\t}\n\n\t/** Create a SettingsManager that loads from files */\n\tstatic create(cwd: string, agentDir: string = getAgentDir()): SettingsManager {\n\t\tconst storage = new FileSettingsStorage(cwd, agentDir);\n\t\treturn SettingsManager.fromStorage(storage);\n\t}\n\n\t/** Create a SettingsManager from an arbitrary storage backend */\n\tstatic fromStorage(storage: SettingsStorage): SettingsManager {\n\t\tconst globalLoad = SettingsManager.tryLoadFromStorage(storage, \"global\");\n\t\tconst projectLoad = SettingsManager.tryLoadFromStorage(storage, \"project\");\n\t\tconst initialErrors: SettingsError[] = [];\n\t\tif (globalLoad.error) {\n\t\t\tinitialErrors.push({ scope: \"global\", error: globalLoad.error });\n\t\t}\n\t\tif (projectLoad.error) {\n\t\t\tinitialErrors.push({ scope: \"project\", error: projectLoad.error });\n\t\t}\n\n\t\treturn new SettingsManager(\n\t\t\tstorage,\n\t\t\tglobalLoad.settings,\n\t\t\tprojectLoad.settings,\n\t\t\tglobalLoad.error,\n\t\t\tprojectLoad.error,\n\t\t\tinitialErrors,\n\t\t);\n\t}\n\n\t/** Create an in-memory SettingsManager (no file I/O) */\n\tstatic inMemory(settings: Partial<Settings> = {}): SettingsManager {\n\t\tconst storage = new InMemorySettingsStorage();\n\t\tconst initialSettings = SettingsManager.migrateSettings(structuredClone(settings) as Record<string, unknown>);\n\t\tstorage.withLock(\"global\", () => JSON.stringify(initialSettings, null, 2));\n\t\treturn SettingsManager.fromStorage(storage);\n\t}\n\n\tprivate static loadFromStorage(storage: SettingsStorage, scope: SettingsScope): Settings {\n\t\tlet content: string | undefined;\n\t\tstorage.withLock(scope, (current) => {\n\t\t\tcontent = current;\n\t\t\treturn undefined;\n\t\t});\n\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\tconst settings = JSON.parse(content);\n\t\treturn SettingsManager.migrateSettings(settings);\n\t}\n\n\tprivate static tryLoadFromStorage(\n\t\tstorage: SettingsStorage,\n\t\tscope: SettingsScope,\n\t): { settings: Settings; error: Error | null } {\n\t\ttry {\n\t\t\treturn { settings: SettingsManager.loadFromStorage(storage, scope), error: null };\n\t\t} catch (error) {\n\t\t\treturn { settings: {}, error: error as Error };\n\t\t}\n\t}\n\n\t/** Migrate old settings format to new format */\n\tprivate static migrateSettings(settings: Record<string, unknown>): Settings {\n\t\t// Migrate queueMode -> steeringMode\n\t\tif (\"queueMode\" in settings && !(\"steeringMode\" in settings)) {\n\t\t\tsettings.steeringMode = settings.queueMode;\n\t\t\tdelete settings.queueMode;\n\t\t}\n\n\t\t// Migrate legacy websockets boolean -> transport enum\n\t\tif (!(\"transport\" in settings) && typeof settings.websockets === \"boolean\") {\n\t\t\tsettings.transport = settings.websockets ? \"websocket\" : \"sse\";\n\t\t\tdelete settings.websockets;\n\t\t}\n\n\t\t// Migrate old skills object format to new array format\n\t\tif (\n\t\t\t\"skills\" in settings &&\n\t\t\ttypeof settings.skills === \"object\" &&\n\t\t\tsettings.skills !== null &&\n\t\t\t!Array.isArray(settings.skills)\n\t\t) {\n\t\t\tconst skillsSettings = settings.skills as {\n\t\t\t\tenableSkillCommands?: boolean;\n\t\t\t\tcustomDirectories?: unknown;\n\t\t\t};\n\t\t\tif (skillsSettings.enableSkillCommands !== undefined && settings.enableSkillCommands === undefined) {\n\t\t\t\tsettings.enableSkillCommands = skillsSettings.enableSkillCommands;\n\t\t\t}\n\t\t\tif (Array.isArray(skillsSettings.customDirectories) && skillsSettings.customDirectories.length > 0) {\n\t\t\t\tsettings.skills = skillsSettings.customDirectories;\n\t\t\t} else {\n\t\t\t\tdelete settings.skills;\n\t\t\t}\n\t\t}\n\n\t\t// Migrate retry.maxDelayMs -> retry.provider.maxRetryDelayMs\n\t\tif (\n\t\t\t\"retry\" in settings &&\n\t\t\ttypeof settings.retry === \"object\" &&\n\t\t\tsettings.retry !== null &&\n\t\t\t!Array.isArray(settings.retry)\n\t\t) {\n\t\t\tconst retrySettings = settings.retry as Record<string, unknown>;\n\t\t\tconst providerSettings =\n\t\t\t\ttypeof retrySettings.provider === \"object\" && retrySettings.provider !== null\n\t\t\t\t\t? (retrySettings.provider as Record<string, unknown>)\n\t\t\t\t\t: undefined;\n\t\t\tif (\n\t\t\t\ttypeof retrySettings.maxDelayMs === \"number\" &&\n\t\t\t\t(providerSettings?.maxRetryDelayMs === undefined || providerSettings?.maxRetryDelayMs === null)\n\t\t\t) {\n\t\t\t\tretrySettings.provider = {\n\t\t\t\t\t...(providerSettings ?? {}),\n\t\t\t\t\tmaxRetryDelayMs: retrySettings.maxDelayMs,\n\t\t\t\t};\n\t\t\t}\n\t\t\tdelete retrySettings.maxDelayMs;\n\t\t}\n\n\t\treturn settings as Settings;\n\t}\n\n\tgetGlobalSettings(): Settings {\n\t\treturn structuredClone(this.globalSettings);\n\t}\n\n\tgetProjectSettings(): Settings {\n\t\treturn structuredClone(this.projectSettings);\n\t}\n\n\tgetDefaultSettings(): Settings {\n\t\treturn structuredClone(DEFAULT_SETTINGS);\n\t}\n\n\tasync reload(): Promise<void> {\n\t\tawait this.writeQueue;\n\t\tconst globalLoad = SettingsManager.tryLoadFromStorage(this.storage, \"global\");\n\t\tif (!globalLoad.error) {\n\t\t\tthis.globalSettings = globalLoad.settings;\n\t\t\tthis.globalSettingsLoadError = null;\n\t\t} else {\n\t\t\tthis.globalSettingsLoadError = globalLoad.error;\n\t\t\tthis.recordError(\"global\", globalLoad.error);\n\t\t}\n\n\t\tthis.modifiedFields.clear();\n\t\tthis.modifiedNestedFields.clear();\n\t\tthis.modifiedProjectFields.clear();\n\t\tthis.modifiedProjectNestedFields.clear();\n\n\t\tconst projectLoad = SettingsManager.tryLoadFromStorage(this.storage, \"project\");\n\t\tif (!projectLoad.error) {\n\t\t\tthis.projectSettings = projectLoad.settings;\n\t\t\tthis.projectSettingsLoadError = null;\n\t\t} else {\n\t\t\tthis.projectSettingsLoadError = projectLoad.error;\n\t\t\tthis.recordError(\"project\", projectLoad.error);\n\t\t}\n\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\t}\n\n\t/** Apply additional overrides on top of current settings */\n\tapplyOverrides(overrides: Partial<Settings>): void {\n\t\tthis.settings = deepMergeSettings(this.settings, overrides);\n\t}\n\n\t/** Mark a global field as modified during this session */\n\tprivate markModified(field: keyof Settings, nestedKey?: string): void {\n\t\tthis.modifiedFields.add(field);\n\t\tif (nestedKey) {\n\t\t\tif (!this.modifiedNestedFields.has(field)) {\n\t\t\t\tthis.modifiedNestedFields.set(field, new Set());\n\t\t\t}\n\t\t\tthis.modifiedNestedFields.get(field)!.add(nestedKey);\n\t\t}\n\t}\n\n\t/** Mark a project field as modified during this session */\n\tprivate markProjectModified(field: keyof Settings, nestedKey?: string): void {\n\t\tthis.modifiedProjectFields.add(field);\n\t\tif (nestedKey) {\n\t\t\tif (!this.modifiedProjectNestedFields.has(field)) {\n\t\t\t\tthis.modifiedProjectNestedFields.set(field, new Set());\n\t\t\t}\n\t\t\tthis.modifiedProjectNestedFields.get(field)!.add(nestedKey);\n\t\t}\n\t}\n\n\tprivate recordError(scope: SettingsScope, error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push({ scope, error: normalizedError });\n\t}\n\n\tprivate clearModifiedScope(scope: SettingsScope): void {\n\t\tif (scope === \"global\") {\n\t\t\tthis.modifiedFields.clear();\n\t\t\tthis.modifiedNestedFields.clear();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.modifiedProjectFields.clear();\n\t\tthis.modifiedProjectNestedFields.clear();\n\t}\n\n\tprivate enqueueWrite(scope: SettingsScope, task: () => void): void {\n\t\tthis.writeQueue = this.writeQueue\n\t\t\t.then(() => {\n\t\t\t\ttask();\n\t\t\t\tthis.clearModifiedScope(scope);\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthis.recordError(scope, error);\n\t\t\t});\n\t}\n\n\tprivate cloneModifiedNestedFields(source: Map<keyof Settings, Set<string>>): Map<keyof Settings, Set<string>> {\n\t\tconst snapshot = new Map<keyof Settings, Set<string>>();\n\t\tfor (const [key, value] of source.entries()) {\n\t\t\tsnapshot.set(key, new Set(value));\n\t\t}\n\t\treturn snapshot;\n\t}\n\n\tprivate persistScopedSettings(\n\t\tscope: SettingsScope,\n\t\tsnapshotSettings: Settings,\n\t\tmodifiedFields: Set<keyof Settings>,\n\t\tmodifiedNestedFields: Map<keyof Settings, Set<string>>,\n\t): void {\n\t\tthis.storage.withLock(scope, (current) => {\n\t\t\tconst currentFileSettings = current\n\t\t\t\t? SettingsManager.migrateSettings(JSON.parse(current) as Record<string, unknown>)\n\t\t\t\t: {};\n\t\t\tconst mergedSettings: Settings = { ...currentFileSettings };\n\t\t\tfor (const field of modifiedFields) {\n\t\t\t\tconst value = snapshotSettings[field];\n\t\t\t\tif (modifiedNestedFields.has(field) && typeof value === \"object\" && value !== null) {\n\t\t\t\t\tconst nestedModified = modifiedNestedFields.get(field)!;\n\t\t\t\t\tconst baseNested = (currentFileSettings[field] as Record<string, unknown>) ?? {};\n\t\t\t\t\tconst inMemoryNested = value as Record<string, unknown>;\n\t\t\t\t\tconst mergedNested = { ...baseNested };\n\t\t\t\t\tfor (const nestedKey of nestedModified) {\n\t\t\t\t\t\tmergedNested[nestedKey] = inMemoryNested[nestedKey];\n\t\t\t\t\t}\n\t\t\t\t\t(mergedSettings as Record<string, unknown>)[field] = mergedNested;\n\t\t\t\t} else {\n\t\t\t\t\t(mergedSettings as Record<string, unknown>)[field] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn JSON.stringify(mergedSettings, null, 2);\n\t\t});\n\t}\n\n\tprivate save(): void {\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\n\t\tif (this.globalSettingsLoadError) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst snapshotGlobalSettings = structuredClone(this.globalSettings);\n\t\tconst modifiedFields = new Set(this.modifiedFields);\n\t\tconst modifiedNestedFields = this.cloneModifiedNestedFields(this.modifiedNestedFields);\n\n\t\tthis.enqueueWrite(\"global\", () => {\n\t\t\tthis.persistScopedSettings(\"global\", snapshotGlobalSettings, modifiedFields, modifiedNestedFields);\n\t\t});\n\t}\n\n\tprivate saveProjectSettings(settings: Settings): void {\n\t\tthis.projectSettings = structuredClone(settings);\n\t\tthis.settings = deepMergeSettings(this.globalSettings, this.projectSettings);\n\n\t\tif (this.projectSettingsLoadError) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst snapshotProjectSettings = structuredClone(this.projectSettings);\n\t\tconst modifiedFields = new Set(this.modifiedProjectFields);\n\t\tconst modifiedNestedFields = this.cloneModifiedNestedFields(this.modifiedProjectNestedFields);\n\t\tthis.enqueueWrite(\"project\", () => {\n\t\t\tthis.persistScopedSettings(\"project\", snapshotProjectSettings, modifiedFields, modifiedNestedFields);\n\t\t});\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tawait this.writeQueue;\n\t}\n\n\tdrainErrors(): SettingsError[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\tgetLastChangelogVersion(): string | undefined {\n\t\treturn this.settings.lastChangelogVersion;\n\t}\n\n\tsetLastChangelogVersion(version: string): void {\n\t\tthis.globalSettings.lastChangelogVersion = version;\n\t\tthis.markModified(\"lastChangelogVersion\");\n\t\tthis.save();\n\t}\n\n\tgetSessionDir(): string | undefined {\n\t\tconst sessionDir = this.settings.sessionDir;\n\t\tif (!sessionDir) {\n\t\t\treturn sessionDir;\n\t\t}\n\t\tif (sessionDir === \"~\") {\n\t\t\treturn homedir();\n\t\t}\n\t\tif (sessionDir.startsWith(\"~/\")) {\n\t\t\treturn join(homedir(), sessionDir.slice(2));\n\t\t}\n\t\treturn sessionDir;\n\t}\n\n\tgetDefaultProvider(): string | undefined {\n\t\treturn this.settings.defaultProvider;\n\t}\n\n\tgetDefaultModel(): string | undefined {\n\t\treturn this.settings.defaultModel;\n\t}\n\n\tsetDefaultProvider(provider: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.markModified(\"defaultProvider\");\n\t\tthis.save();\n\t}\n\n\tsetDefaultModel(modelId: string): void {\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.markModified(\"defaultModel\");\n\t\tthis.save();\n\t}\n\n\tsetDefaultModelAndProvider(provider: string, modelId: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.markModified(\"defaultProvider\");\n\t\tthis.markModified(\"defaultModel\");\n\t\tthis.save();\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.steeringMode || DEFAULT_SETTINGS.steeringMode!;\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.steeringMode = mode;\n\t\tthis.markModified(\"steeringMode\");\n\t\tthis.save();\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.followUpMode || DEFAULT_SETTINGS.followUpMode!;\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.followUpMode = mode;\n\t\tthis.markModified(\"followUpMode\");\n\t\tthis.save();\n\t}\n\n\tgetTheme(): string | undefined {\n\t\treturn this.settings.theme;\n\t}\n\n\tsetTheme(theme: string): void {\n\t\tthis.globalSettings.theme = theme;\n\t\tthis.markModified(\"theme\");\n\t\tthis.save();\n\t}\n\n\tgetDefaultThinkingLevel(): \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | undefined {\n\t\treturn this.settings.defaultThinkingLevel;\n\t}\n\n\tsetDefaultThinkingLevel(level: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"): void {\n\t\tthis.globalSettings.defaultThinkingLevel = level;\n\t\tthis.markModified(\"defaultThinkingLevel\");\n\t\tthis.save();\n\t}\n\n\tgetTransport(): TransportSetting {\n\t\treturn this.settings.transport ?? DEFAULT_SETTINGS.transport!;\n\t}\n\n\tsetTransport(transport: TransportSetting): void {\n\t\tthis.globalSettings.transport = transport;\n\t\tthis.markModified(\"transport\");\n\t\tthis.save();\n\t}\n\n\tgetCompactionEnabled(): boolean {\n\t\treturn this.settings.compaction?.enabled ?? DEFAULT_SETTINGS.compaction!.enabled;\n\t}\n\n\tsetCompactionEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.compaction) {\n\t\t\tthis.globalSettings.compaction = {};\n\t\t}\n\t\tthis.globalSettings.compaction.enabled = enabled;\n\t\tthis.markModified(\"compaction\", \"enabled\");\n\t\tthis.save();\n\t}\n\n\tgetCompactionReserveTokens(): number {\n\t\treturn this.settings.compaction?.reserveTokens ?? DEFAULT_SETTINGS.compaction!.reserveTokens;\n\t}\n\n\tgetCompactionKeepRecentTokens(): number {\n\t\treturn this.settings.compaction?.keepRecentTokens ?? DEFAULT_SETTINGS.compaction!.keepRecentTokens;\n\t}\n\n\tgetCompactionSettings(): Required<CompactionSettings> {\n\t\treturn { ...DEFAULT_SETTINGS.compaction, ...this.settings.compaction };\n\t}\n\n\tgetBranchSummarySettings(): Required<BranchSummarySettings> {\n\t\treturn { ...DEFAULT_SETTINGS.branchSummary, ...this.settings.branchSummary };\n\t}\n\n\tgetBranchSummarySkipPrompt(): boolean {\n\t\treturn this.settings.branchSummary?.skipPrompt ?? DEFAULT_SETTINGS.branchSummary!.skipPrompt;\n\t}\n\n\tgetRetryEnabled(): boolean {\n\t\treturn this.settings.retry?.enabled ?? DEFAULT_SETTINGS.retry!.enabled;\n\t}\n\n\tsetRetryEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.retry) {\n\t\t\tthis.globalSettings.retry = {};\n\t\t}\n\t\tthis.globalSettings.retry.enabled = enabled;\n\t\tthis.markModified(\"retry\", \"enabled\");\n\t\tthis.save();\n\t}\n\n\tgetRetrySettings(): { enabled: boolean; maxRetries: number; baseDelayMs: number } {\n\t\treturn {\n\t\t\tenabled: this.getRetryEnabled(),\n\t\t\tmaxRetries: this.settings.retry?.maxRetries ?? DEFAULT_SETTINGS.retry!.maxRetries,\n\t\t\tbaseDelayMs: this.settings.retry?.baseDelayMs ?? DEFAULT_SETTINGS.retry!.baseDelayMs,\n\t\t};\n\t}\n\n\tgetProviderRetrySettings(): { timeoutMs?: number; maxRetries?: number; maxRetryDelayMs: number } {\n\t\treturn {\n\t\t\t...DEFAULT_SETTINGS.retry!.provider,\n\t\t\t...this.settings.retry?.provider,\n\t\t};\n\t}\n\n\tgetHideThinkingBlock(): boolean {\n\t\treturn this.settings.hideThinkingBlock ?? DEFAULT_SETTINGS.hideThinkingBlock!;\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.globalSettings.hideThinkingBlock = hide;\n\t\tthis.markModified(\"hideThinkingBlock\");\n\t\tthis.save();\n\t}\n\n\tgetShellPath(): string | undefined {\n\t\treturn this.settings.shellPath;\n\t}\n\n\tsetShellPath(path: string | undefined): void {\n\t\tthis.globalSettings.shellPath = path;\n\t\tthis.markModified(\"shellPath\");\n\t\tthis.save();\n\t}\n\n\tgetQuietStartup(): boolean {\n\t\treturn this.settings.quietStartup ?? DEFAULT_SETTINGS.quietStartup!;\n\t}\n\n\tsetQuietStartup(quiet: boolean): void {\n\t\tthis.globalSettings.quietStartup = quiet;\n\t\tthis.markModified(\"quietStartup\");\n\t\tthis.save();\n\t}\n\n\tgetShellCommandPrefix(): string | undefined {\n\t\treturn this.settings.shellCommandPrefix;\n\t}\n\n\tsetShellCommandPrefix(prefix: string | undefined): void {\n\t\tthis.globalSettings.shellCommandPrefix = prefix;\n\t\tthis.markModified(\"shellCommandPrefix\");\n\t\tthis.save();\n\t}\n\n\tgetNpmCommand(): string[] | undefined {\n\t\treturn this.settings.npmCommand ? [...this.settings.npmCommand] : undefined;\n\t}\n\n\tsetNpmCommand(command: string[] | undefined): void {\n\t\tthis.globalSettings.npmCommand = command ? [...command] : undefined;\n\t\tthis.markModified(\"npmCommand\");\n\t\tthis.save();\n\t}\n\n\tgetCollapseChangelog(): boolean {\n\t\treturn this.settings.collapseChangelog ?? DEFAULT_SETTINGS.collapseChangelog!;\n\t}\n\n\tsetCollapseChangelog(collapse: boolean): void {\n\t\tthis.globalSettings.collapseChangelog = collapse;\n\t\tthis.markModified(\"collapseChangelog\");\n\t\tthis.save();\n\t}\n\n\tgetEnableInstallTelemetry(): boolean {\n\t\treturn this.settings.enableInstallTelemetry ?? DEFAULT_SETTINGS.enableInstallTelemetry!;\n\t}\n\n\tsetEnableInstallTelemetry(enabled: boolean): void {\n\t\tthis.globalSettings.enableInstallTelemetry = enabled;\n\t\tthis.markModified(\"enableInstallTelemetry\");\n\t\tthis.save();\n\t}\n\n\tgetPackages(): PackageSource[] {\n\t\treturn [...(this.settings.packages ?? DEFAULT_SETTINGS.packages!)];\n\t}\n\n\tsetPackages(packages: PackageSource[]): void {\n\t\tthis.globalSettings.packages = packages;\n\t\tthis.markModified(\"packages\");\n\t\tthis.save();\n\t}\n\n\tsetProjectPackages(packages: PackageSource[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.packages = packages;\n\t\tthis.markProjectModified(\"packages\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetExtensionPaths(): string[] {\n\t\treturn [...(this.settings.extensions ?? DEFAULT_SETTINGS.extensions!)];\n\t}\n\n\tsetExtensionPaths(paths: string[]): void {\n\t\tthis.globalSettings.extensions = paths;\n\t\tthis.markModified(\"extensions\");\n\t\tthis.save();\n\t}\n\n\tsetProjectExtensionPaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.extensions = paths;\n\t\tthis.markProjectModified(\"extensions\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetSkillPaths(): string[] {\n\t\treturn [...(this.settings.skills ?? DEFAULT_SETTINGS.skills!)];\n\t}\n\n\tsetSkillPaths(paths: string[]): void {\n\t\tthis.globalSettings.skills = paths;\n\t\tthis.markModified(\"skills\");\n\t\tthis.save();\n\t}\n\n\tsetProjectSkillPaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.skills = paths;\n\t\tthis.markProjectModified(\"skills\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetPromptTemplatePaths(): string[] {\n\t\treturn [...(this.settings.prompts ?? DEFAULT_SETTINGS.prompts!)];\n\t}\n\n\tsetPromptTemplatePaths(paths: string[]): void {\n\t\tthis.globalSettings.prompts = paths;\n\t\tthis.markModified(\"prompts\");\n\t\tthis.save();\n\t}\n\n\tsetProjectPromptTemplatePaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.prompts = paths;\n\t\tthis.markProjectModified(\"prompts\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetSlashCommandPaths(): string[] {\n\t\treturn [...(this.settings.slashCommands ?? DEFAULT_SETTINGS.slashCommands!)];\n\t}\n\n\tsetSlashCommandPaths(paths: string[]): void {\n\t\tthis.globalSettings.slashCommands = paths;\n\t\tthis.markModified(\"slashCommands\");\n\t\tthis.save();\n\t}\n\n\tsetProjectSlashCommandPaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.slashCommands = paths;\n\t\tthis.markProjectModified(\"slashCommands\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetThemePaths(): string[] {\n\t\treturn [...(this.settings.themes ?? DEFAULT_SETTINGS.themes!)];\n\t}\n\n\tsetThemePaths(paths: string[]): void {\n\t\tthis.globalSettings.themes = paths;\n\t\tthis.markModified(\"themes\");\n\t\tthis.save();\n\t}\n\n\tsetProjectThemePaths(paths: string[]): void {\n\t\tconst projectSettings = structuredClone(this.projectSettings);\n\t\tprojectSettings.themes = paths;\n\t\tthis.markProjectModified(\"themes\");\n\t\tthis.saveProjectSettings(projectSettings);\n\t}\n\n\tgetEnableSkillCommands(): boolean {\n\t\treturn this.settings.enableSkillCommands ?? DEFAULT_SETTINGS.enableSkillCommands!;\n\t}\n\n\tsetEnableSkillCommands(enabled: boolean): void {\n\t\tthis.globalSettings.enableSkillCommands = enabled;\n\t\tthis.markModified(\"enableSkillCommands\");\n\t\tthis.save();\n\t}\n\n\tgetEnableSubagent(): boolean {\n\t\treturn this.settings.enableSubagent ?? DEFAULT_SETTINGS.enableSubagent!;\n\t}\n\n\tsetEnableSubagent(enabled: boolean): void {\n\t\tthis.globalSettings.enableSubagent = enabled;\n\t\tthis.markModified(\"enableSubagent\");\n\t\tthis.save();\n\t}\n\n\t/** Whether to dispatch eligible subagents on reused warm workers (experimental). */\n\tgetWarmSubagents(): boolean {\n\t\treturn this.settings.warmSubagents ?? DEFAULT_SETTINGS.warmSubagents!;\n\t}\n\n\tsetWarmSubagents(enabled: boolean): void {\n\t\tthis.globalSettings.warmSubagents = enabled;\n\t\tthis.markModified(\"warmSubagents\");\n\t\tthis.save();\n\t}\n\n\t/** Tree-wide subagent nesting cap. Clamped to >= 1 (delegation can never be disabled here). */\n\tgetMaxSubagentDepth(): number {\n\t\tconst v = this.settings.maxSubagentDepth ?? DEFAULT_SETTINGS.maxSubagentDepth!;\n\t\treturn Number.isFinite(v) && v >= 1 ? Math.floor(v) : 1;\n\t}\n\n\t/** Max concurrent subagents per pool at nesting depth >= 1. Clamped to >= 1. */\n\tgetNestedSubagentConcurrency(): number {\n\t\tconst v = this.settings.nestedSubagentConcurrency ?? DEFAULT_SETTINGS.nestedSubagentConcurrency!;\n\t\treturn Number.isFinite(v) && v >= 1 ? Math.floor(v) : 2;\n\t}\n\n\tgetEnableTodoWrite(): boolean {\n\t\treturn this.settings.enableTodoWrite ?? DEFAULT_SETTINGS.enableTodoWrite!;\n\t}\n\n\tsetEnableTodoWrite(enabled: boolean): void {\n\t\tthis.globalSettings.enableTodoWrite = enabled;\n\t\tthis.markModified(\"enableTodoWrite\");\n\t\tthis.save();\n\t}\n\n\tgetEnablePluginTools(): boolean {\n\t\treturn this.settings.enablePluginTools ?? DEFAULT_SETTINGS.enablePluginTools!;\n\t}\n\n\tsetEnablePluginTools(enabled: boolean): void {\n\t\tthis.globalSettings.enablePluginTools = enabled;\n\t\tthis.markModified(\"enablePluginTools\");\n\t\tthis.save();\n\t}\n\n\tgetDeferMcpSchemas(): boolean {\n\t\treturn this.settings.deferMcpSchemas ?? DEFAULT_SETTINGS.deferMcpSchemas!;\n\t}\n\n\tsetDeferMcpSchemas(enabled: boolean): void {\n\t\tthis.globalSettings.deferMcpSchemas = enabled;\n\t\tthis.markModified(\"deferMcpSchemas\");\n\t\tthis.save();\n\t}\n\n\tgetEnableWebTools(): boolean {\n\t\treturn this.settings.enableWebTools ?? DEFAULT_SETTINGS.enableWebTools!;\n\t}\n\n\tsetEnableWebTools(enabled: boolean): void {\n\t\tthis.globalSettings.enableWebTools = enabled;\n\t\tthis.markModified(\"enableWebTools\");\n\t\tthis.save();\n\t}\n\n\tgetEnableBrowserTools(): boolean {\n\t\treturn this.settings.enableBrowserTools ?? DEFAULT_SETTINGS.enableBrowserTools!;\n\t}\n\n\tsetEnableBrowserTools(enabled: boolean): void {\n\t\tthis.globalSettings.enableBrowserTools = enabled;\n\t\tthis.markModified(\"enableBrowserTools\");\n\t\tthis.save();\n\t}\n\n\tgetEnableBrowserLivePreview(): boolean {\n\t\treturn this.settings.enableBrowserLivePreview ?? DEFAULT_SETTINGS.enableBrowserLivePreview!;\n\t}\n\n\tsetEnableBrowserLivePreview(enabled: boolean): void {\n\t\tthis.globalSettings.enableBrowserLivePreview = enabled;\n\t\tthis.markModified(\"enableBrowserLivePreview\");\n\t\tthis.save();\n\t}\n\n\tgetEnableFileTools(): boolean {\n\t\treturn this.settings.enableFileTools ?? DEFAULT_SETTINGS.enableFileTools!;\n\t}\n\n\tsetEnableFileTools(enabled: boolean): void {\n\t\tthis.globalSettings.enableFileTools = enabled;\n\t\tthis.markModified(\"enableFileTools\");\n\t\tthis.save();\n\t}\n\n\tgetThinkingBudgets(): ThinkingBudgetsSettings | undefined {\n\t\treturn this.settings.thinkingBudgets;\n\t}\n\n\tgetThinkingDisplay(): \"summarized\" | \"omitted\" | undefined {\n\t\treturn this.settings.thinkingDisplay;\n\t}\n\n\tgetShowImages(): boolean {\n\t\treturn this.settings.terminal?.showImages ?? DEFAULT_SETTINGS.terminal!.showImages;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.showImages = show;\n\t\tthis.markModified(\"terminal\", \"showImages\");\n\t\tthis.save();\n\t}\n\n\tgetImageWidthCells(): number {\n\t\tconst width = this.settings.terminal?.imageWidthCells ?? DEFAULT_SETTINGS.terminal!.imageWidthCells;\n\t\tif (typeof width !== \"number\" || !Number.isFinite(width)) {\n\t\t\treturn DEFAULT_SETTINGS.terminal!.imageWidthCells;\n\t\t}\n\t\treturn Math.max(1, Math.floor(width));\n\t}\n\n\tsetImageWidthCells(width: number): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.imageWidthCells = Math.max(1, Math.floor(width));\n\t\tthis.markModified(\"terminal\", \"imageWidthCells\");\n\t\tthis.save();\n\t}\n\n\tgetClearOnShrink(): boolean {\n\t\t// Settings takes precedence, then env var, then default false\n\t\tif (this.settings.terminal?.clearOnShrink !== undefined) {\n\t\t\treturn this.settings.terminal.clearOnShrink;\n\t\t}\n\t\treturn process.env.HOOCODE_CLEAR_ON_SHRINK === \"1\";\n\t}\n\n\tsetClearOnShrink(enabled: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.clearOnShrink = enabled;\n\t\tthis.markModified(\"terminal\", \"clearOnShrink\");\n\t\tthis.save();\n\t}\n\n\tgetShowTerminalProgress(): boolean {\n\t\treturn this.settings.terminal?.showTerminalProgress ?? DEFAULT_SETTINGS.terminal!.showTerminalProgress;\n\t}\n\n\tsetShowTerminalProgress(enabled: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.showTerminalProgress = enabled;\n\t\tthis.markModified(\"terminal\", \"showTerminalProgress\");\n\t\tthis.save();\n\t}\n\n\tgetImageAutoResize(): boolean {\n\t\treturn this.settings.images?.autoResize ?? DEFAULT_SETTINGS.images!.autoResize;\n\t}\n\n\tsetImageAutoResize(enabled: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.autoResize = enabled;\n\t\tthis.markModified(\"images\", \"autoResize\");\n\t\tthis.save();\n\t}\n\n\tgetBlockImages(): boolean {\n\t\treturn this.settings.images?.blockImages ?? DEFAULT_SETTINGS.images!.blockImages;\n\t}\n\n\tsetBlockImages(blocked: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.blockImages = blocked;\n\t\tthis.markModified(\"images\", \"blockImages\");\n\t\tthis.save();\n\t}\n\n\tgetEnabledModels(): string[] | undefined {\n\t\treturn this.settings.enabledModels;\n\t}\n\n\tsetEnabledModels(patterns: string[] | undefined): void {\n\t\tthis.globalSettings.enabledModels = patterns;\n\t\tthis.markModified(\"enabledModels\");\n\t\tthis.save();\n\t}\n\n\tgetDoubleEscapeAction(): \"fork\" | \"tree\" | \"none\" {\n\t\treturn this.settings.doubleEscapeAction ?? DEFAULT_SETTINGS.doubleEscapeAction!;\n\t}\n\n\tsetDoubleEscapeAction(action: \"fork\" | \"tree\" | \"none\"): void {\n\t\tthis.globalSettings.doubleEscapeAction = action;\n\t\tthis.markModified(\"doubleEscapeAction\");\n\t\tthis.save();\n\t}\n\n\tgetTreeFilterMode(): \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\" {\n\t\tconst mode = this.settings.treeFilterMode;\n\t\tconst valid = [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"];\n\t\treturn mode && valid.includes(mode) ? mode : DEFAULT_SETTINGS.treeFilterMode!;\n\t}\n\n\tsetTreeFilterMode(mode: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\"): void {\n\t\tthis.globalSettings.treeFilterMode = mode;\n\t\tthis.markModified(\"treeFilterMode\");\n\t\tthis.save();\n\t}\n\n\tgetShowHardwareCursor(): boolean {\n\t\treturn this.settings.showHardwareCursor ?? process.env.HOOCODE_HARDWARE_CURSOR === \"1\";\n\t}\n\n\tsetShowHardwareCursor(enabled: boolean): void {\n\t\tthis.globalSettings.showHardwareCursor = enabled;\n\t\tthis.markModified(\"showHardwareCursor\");\n\t\tthis.save();\n\t}\n\n\tgetEditorPaddingX(): number {\n\t\treturn this.settings.editorPaddingX ?? DEFAULT_SETTINGS.editorPaddingX!;\n\t}\n\n\tsetEditorPaddingX(padding: number): void {\n\t\tthis.globalSettings.editorPaddingX = Math.max(0, Math.min(3, Math.floor(padding)));\n\t\tthis.markModified(\"editorPaddingX\");\n\t\tthis.save();\n\t}\n\n\tgetAutocompleteMaxVisible(): number {\n\t\treturn this.settings.autocompleteMaxVisible ?? DEFAULT_SETTINGS.autocompleteMaxVisible!;\n\t}\n\n\tsetAutocompleteMaxVisible(maxVisible: number): void {\n\t\tthis.globalSettings.autocompleteMaxVisible = Math.max(3, Math.min(20, Math.floor(maxVisible)));\n\t\tthis.markModified(\"autocompleteMaxVisible\");\n\t\tthis.save();\n\t}\n\n\tgetCodeBlockIndent(): string {\n\t\treturn this.settings.markdown?.codeBlockIndent ?? DEFAULT_SETTINGS.markdown!.codeBlockIndent;\n\t}\n\n\tgetWarnings(): WarningSettings {\n\t\treturn { ...DEFAULT_SETTINGS.warnings, ...this.settings.warnings };\n\t}\n\n\tsetWarnings(warnings: WarningSettings): void {\n\t\tthis.globalSettings.warnings = { ...warnings };\n\t\tthis.markModified(\"warnings\");\n\t\tthis.save();\n\t}\n}\n"]}
|
|
@@ -608,6 +608,22 @@ export class SettingsManager {
|
|
|
608
608
|
this.markModified("enableTodoWrite");
|
|
609
609
|
this.save();
|
|
610
610
|
}
|
|
611
|
+
getEnablePluginTools() {
|
|
612
|
+
return this.settings.enablePluginTools ?? DEFAULT_SETTINGS.enablePluginTools;
|
|
613
|
+
}
|
|
614
|
+
setEnablePluginTools(enabled) {
|
|
615
|
+
this.globalSettings.enablePluginTools = enabled;
|
|
616
|
+
this.markModified("enablePluginTools");
|
|
617
|
+
this.save();
|
|
618
|
+
}
|
|
619
|
+
getDeferMcpSchemas() {
|
|
620
|
+
return this.settings.deferMcpSchemas ?? DEFAULT_SETTINGS.deferMcpSchemas;
|
|
621
|
+
}
|
|
622
|
+
setDeferMcpSchemas(enabled) {
|
|
623
|
+
this.globalSettings.deferMcpSchemas = enabled;
|
|
624
|
+
this.markModified("deferMcpSchemas");
|
|
625
|
+
this.save();
|
|
626
|
+
}
|
|
611
627
|
getEnableWebTools() {
|
|
612
628
|
return this.settings.enableWebTools ?? DEFAULT_SETTINGS.enableWebTools;
|
|
613
629
|
}
|