agentinit 1.23.0 → 1.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +24 -2
  3. package/dist/agents/CodexCliAgent.js +1 -1
  4. package/dist/agents/CodexCliAgent.js.map +1 -1
  5. package/dist/cli.js +932 -45
  6. package/dist/commands/agent.d.ts.map +1 -1
  7. package/dist/commands/agent.js +113 -8
  8. package/dist/commands/agent.js.map +1 -1
  9. package/dist/core/agentSettings/adapters/claude.d.ts.map +1 -1
  10. package/dist/core/agentSettings/adapters/claude.js +140 -2
  11. package/dist/core/agentSettings/adapters/claude.js.map +1 -1
  12. package/dist/core/agentSettings/adapters/codex.d.ts +3 -0
  13. package/dist/core/agentSettings/adapters/codex.d.ts.map +1 -0
  14. package/dist/core/agentSettings/adapters/codex.js +121 -0
  15. package/dist/core/agentSettings/adapters/codex.js.map +1 -0
  16. package/dist/core/agentSettings/adapters/opencode.d.ts +3 -0
  17. package/dist/core/agentSettings/adapters/opencode.d.ts.map +1 -0
  18. package/dist/core/agentSettings/adapters/opencode.js +134 -0
  19. package/dist/core/agentSettings/adapters/opencode.js.map +1 -0
  20. package/dist/core/agentSettings/registry.d.ts.map +1 -1
  21. package/dist/core/agentSettings/registry.js +4 -0
  22. package/dist/core/agentSettings/registry.js.map +1 -1
  23. package/dist/core/agentSettings/settingsManager.d.ts +4 -1
  24. package/dist/core/agentSettings/settingsManager.d.ts.map +1 -1
  25. package/dist/core/agentSettings/settingsManager.js +277 -27
  26. package/dist/core/agentSettings/settingsManager.js.map +1 -1
  27. package/dist/core/agentSettings/types.d.ts +34 -3
  28. package/dist/core/agentSettings/types.d.ts.map +1 -1
  29. package/dist/core/agentSettings/valueParser.d.ts.map +1 -1
  30. package/dist/core/agentSettings/valueParser.js +20 -0
  31. package/dist/core/agentSettings/valueParser.js.map +1 -1
  32. package/package.json +2 -1
@@ -0,0 +1,134 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { expandTilde } from '../../../utils/paths.js';
4
+ const SUPPORTED_SCOPES = ['global', 'project'];
5
+ const PERMISSION_ACTIONS = ['allow', 'ask', 'deny'];
6
+ function firstExistingPath(paths, fallback) {
7
+ return paths.find(candidate => existsSync(candidate)) ?? fallback;
8
+ }
9
+ function setting(key, valueType, title, description, options = {}) {
10
+ const definition = {
11
+ agent: 'opencode',
12
+ key,
13
+ nativePath: options.nativePath ?? key.split('.'),
14
+ title,
15
+ description,
16
+ valueType,
17
+ scopes: options.scopes ?? SUPPORTED_SCOPES,
18
+ defaultScope: options.defaultScope ?? 'global',
19
+ category: options.category ?? 'settings',
20
+ risk: options.risk ?? 'safe',
21
+ };
22
+ if (options.allowedValues) {
23
+ definition.allowedValues = options.allowedValues;
24
+ }
25
+ if (options.deprecated !== undefined) {
26
+ definition.deprecated = options.deprecated;
27
+ }
28
+ if (options.replacement) {
29
+ definition.replacement = options.replacement;
30
+ }
31
+ return definition;
32
+ }
33
+ export const opencodeSettingsAdapter = {
34
+ agent: 'opencode',
35
+ displayName: 'OpenCode',
36
+ format: 'jsonc',
37
+ definitions: [
38
+ setting('model', 'string', 'Model', 'Default model identifier in provider/model format (e.g. anthropic/claude-sonnet-4-5).', {
39
+ category: 'model',
40
+ }),
41
+ setting('small_model', 'string', 'Small model', 'Small model used for tasks such as title generation, in provider/model format.', {
42
+ category: 'model',
43
+ }),
44
+ setting('provider', 'object', 'Providers', 'Custom OpenCode provider configurations and model overrides.', {
45
+ category: 'provider',
46
+ risk: 'security-sensitive',
47
+ }),
48
+ setting('default_agent', 'string', 'Default agent', 'Default primary agent to use when none is specified.', {
49
+ category: 'agent',
50
+ }),
51
+ setting('autoupdate', 'booleanOrEnum', 'Auto update', 'Control automatic updates: true, false, or notify.', {
52
+ allowedValues: ['notify'],
53
+ scopes: ['global'],
54
+ defaultScope: 'global',
55
+ category: 'runtime',
56
+ }),
57
+ setting('shell', 'string', 'Shell', 'Default shell to use for terminal and bash tool execution.', {
58
+ category: 'runtime',
59
+ }),
60
+ setting('share', 'enum', 'Share', 'Control sharing behavior: manual, auto, or disabled.', {
61
+ allowedValues: ['manual', 'auto', 'disabled'],
62
+ category: 'sharing',
63
+ }),
64
+ setting('username', 'string', 'Username', 'Custom username displayed in conversations instead of system username.', {
65
+ category: 'ui',
66
+ }),
67
+ setting('logLevel', 'enum', 'Log level', 'Log verbosity level.', {
68
+ allowedValues: ['DEBUG', 'INFO', 'WARN', 'ERROR'],
69
+ category: 'runtime',
70
+ }),
71
+ setting('snapshot', 'boolean', 'Snapshot tracking', 'Enable or disable filesystem snapshot tracking for undo/redo.', {
72
+ category: 'runtime',
73
+ }),
74
+ setting('permission.*', 'enum', 'Default permission', 'Fallback permission rule for all tools: allow, ask, or deny.', {
75
+ nativePath: ['permission', '*'],
76
+ category: 'permissions',
77
+ risk: 'security-sensitive',
78
+ allowedValues: PERMISSION_ACTIONS,
79
+ }),
80
+ setting('permission.bash', 'enum', 'Bash permission', 'Permission rule for shell command execution.', {
81
+ category: 'permissions',
82
+ risk: 'security-sensitive',
83
+ allowedValues: PERMISSION_ACTIONS,
84
+ }),
85
+ setting('permission.read', 'enum', 'Read permission', 'Permission rule for file reads.', {
86
+ category: 'permissions',
87
+ allowedValues: PERMISSION_ACTIONS,
88
+ }),
89
+ setting('permission.edit', 'enum', 'Edit permission', 'Permission rule for editing and writing files.', {
90
+ category: 'permissions',
91
+ risk: 'risky',
92
+ allowedValues: PERMISSION_ACTIONS,
93
+ }),
94
+ setting('permission.webfetch', 'enum', 'Web fetch permission', 'Permission rule for fetching external URLs.', {
95
+ category: 'permissions',
96
+ allowedValues: PERMISSION_ACTIONS,
97
+ }),
98
+ setting('permission.task', 'enum', 'Task permission', 'Permission rule for spawning subagent tasks.', {
99
+ category: 'permissions',
100
+ allowedValues: PERMISSION_ACTIONS,
101
+ }),
102
+ setting('permission.websearch', 'enum', 'Web search permission', 'Permission rule for web search operations.', {
103
+ category: 'permissions',
104
+ allowedValues: PERMISSION_ACTIONS,
105
+ }),
106
+ setting('compaction.auto', 'boolean', 'Auto compaction', 'Enable automatic context compaction when context is full.', {
107
+ category: 'compaction',
108
+ }),
109
+ setting('tool_output.max_lines', 'positiveInteger', 'Tool output max lines', 'Maximum lines of tool output before truncation.', {
110
+ category: 'output',
111
+ }),
112
+ setting('tool_output.max_bytes', 'positiveInteger', 'Tool output max bytes', 'Maximum bytes of tool output before truncation.', {
113
+ category: 'output',
114
+ }),
115
+ ],
116
+ getSettingsPath(scope, projectPath) {
117
+ switch (scope) {
118
+ case 'global':
119
+ return firstExistingPath([
120
+ expandTilde('~/.config/opencode/opencode.jsonc'),
121
+ expandTilde('~/.config/opencode/opencode.json'),
122
+ expandTilde('~/.config/opencode/config.json'),
123
+ ], expandTilde('~/.config/opencode/opencode.json'));
124
+ case 'project':
125
+ return firstExistingPath([
126
+ join(projectPath, '.opencode', 'opencode.jsonc'),
127
+ join(projectPath, '.opencode', 'opencode.json'),
128
+ ], join(projectPath, '.opencode', 'opencode.json'));
129
+ case 'local':
130
+ throw new Error('OpenCode settings do not support local scope. Use --global or --project.');
131
+ }
132
+ },
133
+ };
134
+ //# sourceMappingURL=opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../../src/core/agentSettings/adapters/opencode.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/agentSettings/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAMxG,wBAAgB,wBAAwB,IAAI,oBAAoB,EAAE,CAEjE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAEvF;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAExG;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,sBAAsB,GAAG,uBAAuB,CAKzF"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/agentSettings/registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAQxG,wBAAgB,wBAAwB,IAAI,oBAAoB,EAAE,CAEjE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAEvF;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAExG;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,sBAAsB,GAAG,uBAAuB,CAKzF"}
@@ -1,6 +1,10 @@
1
1
  import { claudeSettingsAdapter } from './adapters/claude.js';
2
+ import { codexSettingsAdapter } from './adapters/codex.js';
3
+ import { opencodeSettingsAdapter } from './adapters/opencode.js';
2
4
  const ADAPTERS = [
3
5
  claudeSettingsAdapter,
6
+ codexSettingsAdapter,
7
+ opencodeSettingsAdapter,
4
8
  ];
5
9
  export function getAgentSettingsAdapters() {
6
10
  return [...ADAPTERS];
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/agentSettings/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAG7D,MAAM,QAAQ,GAA2B;IACvC,qBAAqB;CACtB,CAAC;AAEF,MAAM,UAAU,wBAAwB;IACtC,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAa;IACnD,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAa,EAAE,GAAW;IAClE,OAAO,uBAAuB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAkC;IAC9D,OAAO;QACL,GAAG,UAAU;QACb,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;KAC5C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/agentSettings/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,MAAM,QAAQ,GAA2B;IACvC,qBAAqB;IACrB,oBAAoB;IACpB,uBAAuB;CACxB,CAAC;AAEF,MAAM,UAAU,wBAAwB;IACtC,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAa;IACnD,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAa,EAAE,GAAW;IAClE,OAAO,uBAAuB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAkC;IAC9D,OAAO;QACL,GAAG,UAAU;QACb,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;KAC5C,CAAC;AACJ,CAAC"}
@@ -1,12 +1,15 @@
1
- import type { AgentHookAddOptions, AgentHookMatcher, AgentHookRemoveOptions, AgentHookWriteResult, AgentSettingReadOptions, AgentSettingsSchema, AgentSettingSetOptions, AgentSettingsWriteResult } from './types.js';
1
+ import type { AgentApiKeyAction, AgentApiKeyOptions, AgentApiKeyResult, AgentHookAddOptions, AgentHookMatcher, AgentHookRemoveOptions, AgentHookWriteResult, AgentSettingReadOptions, AgentSettingsList, AgentSettingsSchema, AgentSettingSetOptions, AgentSettingsWriteResult } from './types.js';
2
2
  export declare class AgentSettingsManager {
3
3
  getSupportedAgents(): string[];
4
4
  getSchema(agent: string): AgentSettingsSchema;
5
+ listSettings(agent: string, options?: AgentSettingReadOptions): Promise<AgentSettingsList>;
5
6
  get(agent: string, key?: string, options?: AgentSettingReadOptions): Promise<unknown>;
6
7
  set(agent: string, key: string, rawValue: string, options?: AgentSettingSetOptions): Promise<AgentSettingsWriteResult>;
7
8
  unset(agent: string, key: string, options?: AgentSettingSetOptions): Promise<AgentSettingsWriteResult>;
8
9
  listHooks(agent: string, event?: string, options?: AgentSettingReadOptions): Promise<Record<string, AgentHookMatcher[]> | AgentHookMatcher[]>;
9
10
  addHook(agent: string, event: string, command: string, options?: AgentHookAddOptions): Promise<AgentHookWriteResult>;
10
11
  removeHook(agent: string, event: string, commandOrName: string, options?: AgentHookRemoveOptions): Promise<AgentHookWriteResult>;
12
+ getApiKeyStatus(agent: string, apiKey: string, options?: AgentApiKeyOptions): Promise<AgentApiKeyResult>;
13
+ updateApiKeyTrust(agent: string, action: AgentApiKeyAction, apiKey: string, options?: AgentApiKeyOptions): Promise<AgentApiKeyResult>;
11
14
  }
12
15
  //# sourceMappingURL=settingsManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"settingsManager.d.ts","sourceRoot":"","sources":["../../../src/core/agentSettings/settingsManager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,mBAAmB,EAInB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EAEnB,sBAAsB,EACtB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAoOpB,qBAAa,oBAAoB;IAC/B,kBAAkB,IAAI,MAAM,EAAE;IAI9B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB;IAcvC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,OAAO,CAAC;IAuBzF,GAAG,CACP,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,wBAAwB,CAAC;IAkC9B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAgC1G,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC;IAgCjJ,OAAO,CACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,oBAAoB,CAAC;IA2C1B,UAAU,CACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,oBAAoB,CAAC;CAiDjC"}
1
+ {"version":3,"file":"settingsManager.d.ts","sourceRoot":"","sources":["../../../src/core/agentSettings/settingsManager.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EAEjB,mBAAmB,EAInB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EAEpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EAEnB,sBAAsB,EACtB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AA8WpB,qBAAa,oBAAoB;IAC/B,kBAAkB,IAAI,MAAM,EAAE;IAI9B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB;IAcvC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkD9F,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,OAAO,CAAC;IA4CzF,GAAG,CACP,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,wBAAwB,CAAC;IAkC9B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAgC1G,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC;IAoCjJ,OAAO,CACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,oBAAoB,CAAC;IA4C1B,UAAU,CACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,oBAAoB,CAAC;IAmD1B,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAuB5G,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA4ChJ"}
@@ -1,3 +1,5 @@
1
+ import * as TOML from '@iarna/toml';
2
+ import { parse as parseJsonc } from 'jsonc-parser';
1
3
  import { readFileIfExists, writeFile } from '../../utils/fs.js';
2
4
  import { getEffectiveAgentSettingsDefaultScopeSync } from '../userConfig.js';
3
5
  import { parseAgentSettingValue } from './valueParser.js';
@@ -13,8 +15,22 @@ const HOOK_EVENT_ALIASES = {
13
15
  stop: 'Stop',
14
16
  'session-start': 'SessionStart',
15
17
  'session-end': 'SessionEnd',
18
+ 'user-prompt-submit': 'UserPromptSubmit',
16
19
  };
17
20
  const HOOK_EVENTS = new Set(Object.values(HOOK_EVENT_ALIASES));
21
+ const CLAUDE_API_KEY_RESPONSES_DEFINITION = {
22
+ agent: 'claude',
23
+ key: 'customApiKeyResponses',
24
+ nativePath: ['customApiKeyResponses'],
25
+ title: 'Custom API key responses',
26
+ description: 'Remembered custom API key trust responses.',
27
+ valueType: 'object',
28
+ scopes: ['global'],
29
+ defaultScope: 'global',
30
+ category: 'auth',
31
+ risk: 'security-sensitive',
32
+ store: 'globalConfig',
33
+ };
18
34
  function assertObject(value, path) {
19
35
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
20
36
  throw new Error(`${path} contains JSON that is not an object.`);
@@ -144,6 +160,50 @@ function buildHookCommand(command, name) {
144
160
  ...(name ? { name } : {}),
145
161
  };
146
162
  }
163
+ function normalizeApiKeyForConfig(apiKey) {
164
+ const trimmed = apiKey.trim();
165
+ if (!trimmed) {
166
+ throw new Error('API key cannot be empty.');
167
+ }
168
+ return trimmed.slice(-20);
169
+ }
170
+ function getApiKeyResponses(config) {
171
+ const current = config.customApiKeyResponses;
172
+ if (current !== undefined && (!current || typeof current !== 'object' || Array.isArray(current))) {
173
+ throw new Error('Existing customApiKeyResponses value is not an object.');
174
+ }
175
+ const responses = (current ?? {});
176
+ const approved = responses.approved ?? [];
177
+ const rejected = responses.rejected ?? [];
178
+ if (!Array.isArray(approved) || !approved.every(value => typeof value === 'string')) {
179
+ throw new Error('Existing customApiKeyResponses.approved value must be an array of strings.');
180
+ }
181
+ if (!Array.isArray(rejected) || !rejected.every(value => typeof value === 'string')) {
182
+ throw new Error('Existing customApiKeyResponses.rejected value must be an array of strings.');
183
+ }
184
+ return {
185
+ approved,
186
+ rejected,
187
+ };
188
+ }
189
+ function getApiKeyStatusFromResponses(responses, fingerprint) {
190
+ if (responses.approved.includes(fingerprint)) {
191
+ return 'approved';
192
+ }
193
+ if (responses.rejected.includes(fingerprint)) {
194
+ return 'rejected';
195
+ }
196
+ return 'unknown';
197
+ }
198
+ function setApiKeyResponses(config, approved, rejected) {
199
+ config.customApiKeyResponses = {
200
+ ...(config.customApiKeyResponses && typeof config.customApiKeyResponses === 'object' && !Array.isArray(config.customApiKeyResponses)
201
+ ? config.customApiKeyResponses
202
+ : {}),
203
+ approved,
204
+ rejected,
205
+ };
206
+ }
147
207
  function deleteNestedValue(config, path) {
148
208
  const parents = [];
149
209
  let current = config;
@@ -178,27 +238,91 @@ function resolveProjectPath(projectPath) {
178
238
  return projectPath ?? process.cwd();
179
239
  }
180
240
  function resolveScope(definition, scope) {
181
- const resolvedScope = scope ?? getEffectiveAgentSettingsDefaultScopeSync();
241
+ const defaultScope = getEffectiveAgentSettingsDefaultScopeSync();
242
+ const resolvedScope = scope ?? (definition.store === 'globalConfig' && !definition.scopes.includes(defaultScope) ? definition.defaultScope : defaultScope);
182
243
  if (!definition.scopes.includes(resolvedScope)) {
183
244
  throw new Error(`"${definition.key}" does not support ${resolvedScope} scope. Supported scopes: ${definition.scopes.join(', ')}.`);
184
245
  }
185
246
  return resolvedScope;
186
247
  }
187
- async function readJsonObject(path) {
248
+ function getSupportedSettingScopes(adapter) {
249
+ return [...new Set(adapter.definitions.flatMap(definition => definition.scopes))];
250
+ }
251
+ function resolveFullReadScope(adapter, scope) {
252
+ const resolvedScope = scope ?? getEffectiveAgentSettingsDefaultScopeSync();
253
+ const supportedScopes = getSupportedSettingScopes(adapter);
254
+ if (!supportedScopes.includes(resolvedScope)) {
255
+ throw new Error(`Agent ${adapter.agent} settings do not support ${resolvedScope} scope. Supported scopes: ${supportedScopes.join(', ')}.`);
256
+ }
257
+ return resolvedScope;
258
+ }
259
+ async function readConfigObject(adapter, path) {
188
260
  const content = await readFileIfExists(path);
189
261
  if (!content) {
190
262
  return {};
191
263
  }
264
+ if (adapter.format === 'toml') {
265
+ try {
266
+ return assertObject(TOML.parse(content), path);
267
+ }
268
+ catch (error) {
269
+ if (error instanceof SyntaxError || error instanceof Error) {
270
+ throw new Error(`${path} contains invalid TOML.`);
271
+ }
272
+ throw error;
273
+ }
274
+ }
192
275
  try {
193
- return assertObject(JSON.parse(content), path);
276
+ const errors = [];
277
+ const value = adapter.format === 'jsonc'
278
+ ? parseJsonc(content, errors, { allowTrailingComma: true })
279
+ : JSON.parse(content);
280
+ if (errors.length > 0) {
281
+ throw new SyntaxError('Invalid JSONC');
282
+ }
283
+ return assertObject(value, path);
194
284
  }
195
285
  catch (error) {
196
286
  if (error instanceof SyntaxError) {
197
- throw new Error(`${path} contains invalid JSON.`);
287
+ throw new Error(`${path} contains invalid ${adapter.format === 'jsonc' ? 'JSONC' : 'JSON'}.`);
198
288
  }
199
289
  throw error;
200
290
  }
201
291
  }
292
+ function stringifyConfigObject(adapter, config) {
293
+ if (adapter.format === 'toml') {
294
+ return TOML.stringify(config);
295
+ }
296
+ return `${JSON.stringify(config, null, 2)}\n`;
297
+ }
298
+ function summarizeSettingValue(value) {
299
+ if (value === undefined) {
300
+ return 'not set';
301
+ }
302
+ if (Array.isArray(value)) {
303
+ return `set (array, ${value.length} item${value.length === 1 ? '' : 's'})`;
304
+ }
305
+ if (value && typeof value === 'object') {
306
+ return 'set (object)';
307
+ }
308
+ return JSON.stringify(value);
309
+ }
310
+ async function writeConfigObject(adapter, path, config) {
311
+ await writeFile(path, stringifyConfigObject(adapter, config));
312
+ }
313
+ function assertHookEventSupported(adapter, event) {
314
+ if (adapter.hookEvents && !adapter.hookEvents.includes(event)) {
315
+ throw new Error(`Agent ${adapter.agent} does not support ${event} hooks. Supported: ${adapter.hookEvents.join(', ')}.`);
316
+ }
317
+ }
318
+ function resolveHookScope(adapter, scope) {
319
+ const resolvedScope = scope ?? getEffectiveAgentSettingsDefaultScopeSync();
320
+ const supportedScopes = adapter.hookScopes ?? ['global', 'project', 'local'];
321
+ if (!supportedScopes.includes(resolvedScope)) {
322
+ throw new Error(`Agent ${adapter.agent} hooks do not support ${resolvedScope} scope. Supported scopes: ${supportedScopes.join(', ')}.`);
323
+ }
324
+ return resolvedScope;
325
+ }
202
326
  export class AgentSettingsManager {
203
327
  getSupportedAgents() {
204
328
  return getAgentSettingsAdapters().map(adapter => adapter.agent);
@@ -215,23 +339,84 @@ export class AgentSettingsManager {
215
339
  settings: adapter.definitions.map(toSchemaEntry),
216
340
  };
217
341
  }
342
+ async listSettings(agent, options = {}) {
343
+ const adapter = getAgentSettingsAdapter(agent);
344
+ if (!adapter) {
345
+ throw new Error(`Unsupported agent settings adapter: ${agent}. Supported: ${this.getSupportedAgents().join(', ')}`);
346
+ }
347
+ const scope = resolveFullReadScope(adapter, options.scope);
348
+ const projectPath = resolveProjectPath(options.projectPath);
349
+ const configByPath = new Map();
350
+ const readConfigForDefinition = async (definition) => {
351
+ const path = adapter.getSettingsPath(scope, projectPath, definition);
352
+ const cached = configByPath.get(path);
353
+ if (cached) {
354
+ return cached;
355
+ }
356
+ const config = await readConfigObject(adapter, path);
357
+ configByPath.set(path, config);
358
+ return config;
359
+ };
360
+ const settings = [];
361
+ for (const definition of adapter.definitions) {
362
+ const schemaEntry = toSchemaEntry(definition);
363
+ if (!definition.scopes.includes(scope)) {
364
+ settings.push({
365
+ ...schemaEntry,
366
+ currentStatus: 'not-applicable',
367
+ currentSummary: 'n/a',
368
+ });
369
+ continue;
370
+ }
371
+ const config = await readConfigForDefinition(definition);
372
+ const value = getNestedValue(config, definition.nativePath);
373
+ settings.push({
374
+ ...schemaEntry,
375
+ currentStatus: value === undefined ? 'not-set' : 'set',
376
+ currentSummary: summarizeSettingValue(value),
377
+ });
378
+ }
379
+ return {
380
+ agent: adapter.agent,
381
+ displayName: adapter.displayName,
382
+ scope,
383
+ settings,
384
+ };
385
+ }
218
386
  async get(agent, key, options = {}) {
219
387
  const adapter = getAgentSettingsAdapter(agent);
220
388
  if (!adapter) {
221
389
  throw new Error(`Unsupported agent settings adapter: ${agent}. Supported: ${this.getSupportedAgents().join(', ')}`);
222
390
  }
223
391
  if (!key) {
224
- const scope = options.scope ?? getEffectiveAgentSettingsDefaultScopeSync();
392
+ const scope = resolveFullReadScope(adapter, options.scope);
225
393
  const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath));
226
- return await readJsonObject(path);
394
+ const config = await readConfigObject(adapter, path);
395
+ if (scope !== 'global') {
396
+ return config;
397
+ }
398
+ const globalConfigDefinitions = adapter.definitions.filter(definition => definition.store === 'globalConfig' && definition.scopes.includes('global'));
399
+ if (globalConfigDefinitions.length === 0) {
400
+ return config;
401
+ }
402
+ const globalConfigPath = adapter.getSettingsPath('global', resolveProjectPath(options.projectPath), globalConfigDefinitions[0]);
403
+ const globalConfig = await readConfigObject(adapter, globalConfigPath);
404
+ const result = { ...config };
405
+ for (const definition of globalConfigDefinitions) {
406
+ const value = getNestedValue(globalConfig, definition.nativePath);
407
+ if (value !== undefined) {
408
+ setNestedValue(result, definition.nativePath, value);
409
+ }
410
+ }
411
+ return result;
227
412
  }
228
413
  const definition = getAgentSettingDefinition(agent, key);
229
414
  if (!definition) {
230
415
  throw new Error(`Unknown ${agent} setting: ${key}.`);
231
416
  }
232
417
  const scope = resolveScope(definition, options.scope);
233
- const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath));
234
- const config = await readJsonObject(path);
418
+ const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath), definition);
419
+ const config = await readConfigObject(adapter, path);
235
420
  return getNestedValue(config, definition.nativePath);
236
421
  }
237
422
  async set(agent, key, rawValue, options = {}) {
@@ -244,13 +429,13 @@ export class AgentSettingsManager {
244
429
  throw new Error(`Unknown ${agent} setting: ${key}.`);
245
430
  }
246
431
  const scope = resolveScope(definition, options.scope);
247
- const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath));
248
- const config = await readJsonObject(path);
432
+ const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath), definition);
433
+ const config = await readConfigObject(adapter, path);
249
434
  const previousValue = getNestedValue(config, definition.nativePath);
250
435
  const value = parseAgentSettingValue(definition, rawValue, options.parseJson);
251
436
  setNestedValue(config, definition.nativePath, value);
252
437
  if (!options.dryRun) {
253
- await writeFile(path, `${JSON.stringify(config, null, 2)}\n`);
438
+ await writeConfigObject(adapter, path, config);
254
439
  }
255
440
  return {
256
441
  agent,
@@ -272,12 +457,12 @@ export class AgentSettingsManager {
272
457
  throw new Error(`Unknown ${agent} setting: ${key}.`);
273
458
  }
274
459
  const scope = resolveScope(definition, options.scope);
275
- const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath));
276
- const config = await readJsonObject(path);
460
+ const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath), definition);
461
+ const config = await readConfigObject(adapter, path);
277
462
  const previousValue = getNestedValue(config, definition.nativePath);
278
463
  deleteNestedValue(config, definition.nativePath);
279
464
  if (!options.dryRun) {
280
- await writeFile(path, `${JSON.stringify(config, null, 2)}\n`);
465
+ await writeConfigObject(adapter, path, config);
281
466
  }
282
467
  return {
283
468
  agent,
@@ -293,14 +478,18 @@ export class AgentSettingsManager {
293
478
  if (!adapter) {
294
479
  throw new Error(`Unsupported agent settings adapter: ${agent}. Supported: ${this.getSupportedAgents().join(', ')}`);
295
480
  }
296
- if (agent !== 'claude') {
481
+ if (!adapter.hookEvents) {
297
482
  throw new Error(`Agent ${agent} does not support hook management.`);
298
483
  }
299
- const scope = options.scope ?? getEffectiveAgentSettingsDefaultScopeSync();
484
+ const hookEvent = event ? normalizeHookEvent(event) : undefined;
485
+ if (hookEvent) {
486
+ assertHookEventSupported(adapter, hookEvent);
487
+ }
488
+ const scope = resolveHookScope(adapter, options.scope);
300
489
  const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath));
301
- const config = await readJsonObject(path);
302
- if (event) {
303
- return getHookMatchers(config, normalizeHookEvent(event));
490
+ const config = await readConfigObject(adapter, path);
491
+ if (hookEvent) {
492
+ return getHookMatchers(config, hookEvent);
304
493
  }
305
494
  const hooks = getNestedValue(config, ['hooks']);
306
495
  if (hooks === undefined) {
@@ -320,13 +509,14 @@ export class AgentSettingsManager {
320
509
  if (!adapter) {
321
510
  throw new Error(`Unsupported agent settings adapter: ${agent}. Supported: ${this.getSupportedAgents().join(', ')}`);
322
511
  }
323
- if (agent !== 'claude') {
512
+ if (!adapter.hookEvents) {
324
513
  throw new Error(`Agent ${agent} does not support hook management.`);
325
514
  }
326
515
  const hookEvent = normalizeHookEvent(event);
327
- const scope = options.scope ?? getEffectiveAgentSettingsDefaultScopeSync();
516
+ assertHookEventSupported(adapter, hookEvent);
517
+ const scope = resolveHookScope(adapter, options.scope);
328
518
  const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath));
329
- const config = await readJsonObject(path);
519
+ const config = await readConfigObject(adapter, path);
330
520
  const hook = buildHookCommand(command, options.name);
331
521
  const matchers = getHookMatchers(config, hookEvent);
332
522
  const matcher = options.matcher ?? '*';
@@ -342,7 +532,7 @@ export class AgentSettingsManager {
342
532
  }
343
533
  setHookMatchers(config, hookEvent, matchers);
344
534
  if (!options.dryRun) {
345
- await writeFile(path, `${JSON.stringify(config, null, 2)}\n`);
535
+ await writeConfigObject(adapter, path, config);
346
536
  }
347
537
  return {
348
538
  agent,
@@ -358,13 +548,14 @@ export class AgentSettingsManager {
358
548
  if (!adapter) {
359
549
  throw new Error(`Unsupported agent settings adapter: ${agent}. Supported: ${this.getSupportedAgents().join(', ')}`);
360
550
  }
361
- if (agent !== 'claude') {
551
+ if (!adapter.hookEvents) {
362
552
  throw new Error(`Agent ${agent} does not support hook management.`);
363
553
  }
364
554
  const hookEvent = normalizeHookEvent(event);
365
- const scope = options.scope ?? getEffectiveAgentSettingsDefaultScopeSync();
555
+ assertHookEventSupported(adapter, hookEvent);
556
+ const scope = resolveHookScope(adapter, options.scope);
366
557
  const path = adapter.getSettingsPath(scope, resolveProjectPath(options.projectPath));
367
- const config = await readJsonObject(path);
558
+ const config = await readConfigObject(adapter, path);
368
559
  const matchers = getHookMatchers(config, hookEvent);
369
560
  let removed = 0;
370
561
  const nextMatchers = matchers
@@ -384,7 +575,7 @@ export class AgentSettingsManager {
384
575
  .filter(entry => entry.hooks.length > 0);
385
576
  setHookMatchers(config, hookEvent, nextMatchers);
386
577
  if (!options.dryRun) {
387
- await writeFile(path, `${JSON.stringify(config, null, 2)}\n`);
578
+ await writeConfigObject(adapter, path, config);
388
579
  }
389
580
  return {
390
581
  agent,
@@ -395,5 +586,64 @@ export class AgentSettingsManager {
395
586
  dryRun: Boolean(options.dryRun),
396
587
  };
397
588
  }
589
+ async getApiKeyStatus(agent, apiKey, options = {}) {
590
+ const adapter = getAgentSettingsAdapter(agent);
591
+ if (!adapter) {
592
+ throw new Error(`Unsupported agent settings adapter: ${agent}. Supported: ${this.getSupportedAgents().join(', ')}`);
593
+ }
594
+ if (agent !== 'claude') {
595
+ throw new Error(`Agent ${agent} does not support API key trust management.`);
596
+ }
597
+ const path = adapter.getSettingsPath('global', resolveProjectPath(options.projectPath), CLAUDE_API_KEY_RESPONSES_DEFINITION);
598
+ const config = await readConfigObject(adapter, path);
599
+ const fingerprint = normalizeApiKeyForConfig(apiKey);
600
+ const responses = getApiKeyResponses(config);
601
+ return {
602
+ agent,
603
+ path,
604
+ fingerprint,
605
+ status: getApiKeyStatusFromResponses(responses, fingerprint),
606
+ dryRun: false,
607
+ };
608
+ }
609
+ async updateApiKeyTrust(agent, action, apiKey, options = {}) {
610
+ const adapter = getAgentSettingsAdapter(agent);
611
+ if (!adapter) {
612
+ throw new Error(`Unsupported agent settings adapter: ${agent}. Supported: ${this.getSupportedAgents().join(', ')}`);
613
+ }
614
+ if (agent !== 'claude') {
615
+ throw new Error(`Agent ${agent} does not support API key trust management.`);
616
+ }
617
+ const path = adapter.getSettingsPath('global', resolveProjectPath(options.projectPath), CLAUDE_API_KEY_RESPONSES_DEFINITION);
618
+ const config = await readConfigObject(adapter, path);
619
+ const fingerprint = normalizeApiKeyForConfig(apiKey);
620
+ const responses = getApiKeyResponses(config);
621
+ const previousStatus = getApiKeyStatusFromResponses(responses, fingerprint);
622
+ let approved = responses.approved.filter(value => value !== fingerprint);
623
+ let rejected = responses.rejected.filter(value => value !== fingerprint);
624
+ if (action === 'approve') {
625
+ approved = [...approved, fingerprint];
626
+ }
627
+ else if (action === 'reject') {
628
+ rejected = [...rejected, fingerprint];
629
+ }
630
+ const status = action === 'approve'
631
+ ? 'approved'
632
+ : action === 'reject'
633
+ ? 'rejected'
634
+ : 'unknown';
635
+ setApiKeyResponses(config, approved, rejected);
636
+ if (!options.dryRun) {
637
+ await writeConfigObject(adapter, path, config);
638
+ }
639
+ return {
640
+ agent,
641
+ path,
642
+ fingerprint,
643
+ status,
644
+ previousStatus,
645
+ dryRun: Boolean(options.dryRun),
646
+ };
647
+ }
398
648
  }
399
649
  //# sourceMappingURL=settingsManager.js.map