zoe-agent 0.3.1
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 +154 -0
- package/LICENSE +96 -0
- package/README.md +568 -0
- package/dist/adapters/cli/agent.d.ts +59 -0
- package/dist/adapters/cli/agent.js +232 -0
- package/dist/adapters/cli/bootstrap.d.ts +25 -0
- package/dist/adapters/cli/bootstrap.js +204 -0
- package/dist/adapters/cli/commands/build-registry.d.ts +14 -0
- package/dist/adapters/cli/commands/build-registry.js +88 -0
- package/dist/adapters/cli/commands/clear.d.ts +7 -0
- package/dist/adapters/cli/commands/clear.js +10 -0
- package/dist/adapters/cli/commands/compact.d.ts +13 -0
- package/dist/adapters/cli/commands/compact.js +96 -0
- package/dist/adapters/cli/commands/exit.d.ts +7 -0
- package/dist/adapters/cli/commands/exit.js +9 -0
- package/dist/adapters/cli/commands/gateway.d.ts +7 -0
- package/dist/adapters/cli/commands/gateway.js +152 -0
- package/dist/adapters/cli/commands/help.d.ts +9 -0
- package/dist/adapters/cli/commands/help.js +12 -0
- package/dist/adapters/cli/commands/models.d.ts +10 -0
- package/dist/adapters/cli/commands/models.js +32 -0
- package/dist/adapters/cli/commands/registry.d.ts +70 -0
- package/dist/adapters/cli/commands/registry.js +111 -0
- package/dist/adapters/cli/commands/settings-utils.d.ts +38 -0
- package/dist/adapters/cli/commands/settings-utils.js +182 -0
- package/dist/adapters/cli/commands/settings.d.ts +9 -0
- package/dist/adapters/cli/commands/settings.js +395 -0
- package/dist/adapters/cli/commands/skills.d.ts +7 -0
- package/dist/adapters/cli/commands/skills.js +21 -0
- package/dist/adapters/cli/config-loader.d.ts +27 -0
- package/dist/adapters/cli/config-loader.js +48 -0
- package/dist/adapters/cli/docker-utils.d.ts +37 -0
- package/dist/adapters/cli/docker-utils.js +90 -0
- package/dist/adapters/cli/index.d.ts +2 -0
- package/dist/adapters/cli/index.js +88 -0
- package/dist/adapters/cli/repl.d.ts +22 -0
- package/dist/adapters/cli/repl.js +256 -0
- package/dist/adapters/cli/setup.d.ts +19 -0
- package/dist/adapters/cli/setup.js +613 -0
- package/dist/adapters/cli/system-prompts.d.ts +56 -0
- package/dist/adapters/cli/system-prompts.js +131 -0
- package/dist/adapters/cli/tui/app.d.ts +58 -0
- package/dist/adapters/cli/tui/app.js +314 -0
- package/dist/adapters/cli/tui/components/assistant-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/assistant-message.js +9 -0
- package/dist/adapters/cli/tui/components/autocomplete.d.ts +19 -0
- package/dist/adapters/cli/tui/components/autocomplete.js +75 -0
- package/dist/adapters/cli/tui/components/command-palette.d.ts +15 -0
- package/dist/adapters/cli/tui/components/command-palette.js +50 -0
- package/dist/adapters/cli/tui/components/diff-viewer.d.ts +5 -0
- package/dist/adapters/cli/tui/components/diff-viewer.js +109 -0
- package/dist/adapters/cli/tui/components/error-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/error-message.js +8 -0
- package/dist/adapters/cli/tui/components/footer.d.ts +20 -0
- package/dist/adapters/cli/tui/components/footer.js +19 -0
- package/dist/adapters/cli/tui/components/goal-status.d.ts +12 -0
- package/dist/adapters/cli/tui/components/goal-status.js +22 -0
- package/dist/adapters/cli/tui/components/info-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/info-message.js +8 -0
- package/dist/adapters/cli/tui/components/logo-banner.d.ts +7 -0
- package/dist/adapters/cli/tui/components/logo-banner.js +33 -0
- package/dist/adapters/cli/tui/components/markdown.d.ts +9 -0
- package/dist/adapters/cli/tui/components/markdown.js +92 -0
- package/dist/adapters/cli/tui/components/message-area.d.ts +19 -0
- package/dist/adapters/cli/tui/components/message-area.js +55 -0
- package/dist/adapters/cli/tui/components/permission-prompt.d.ts +13 -0
- package/dist/adapters/cli/tui/components/permission-prompt.js +32 -0
- package/dist/adapters/cli/tui/components/prompt-area.d.ts +22 -0
- package/dist/adapters/cli/tui/components/prompt-area.js +68 -0
- package/dist/adapters/cli/tui/components/text-input.d.ts +27 -0
- package/dist/adapters/cli/tui/components/text-input.js +142 -0
- package/dist/adapters/cli/tui/components/tool-call-block.d.ts +11 -0
- package/dist/adapters/cli/tui/components/tool-call-block.js +68 -0
- package/dist/adapters/cli/tui/components/user-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/user-message.js +8 -0
- package/dist/adapters/cli/tui/diff/file-write-meta.d.ts +11 -0
- package/dist/adapters/cli/tui/diff/file-write-meta.js +11 -0
- package/dist/adapters/cli/tui/diff/line-diff.d.ts +17 -0
- package/dist/adapters/cli/tui/diff/line-diff.js +44 -0
- package/dist/adapters/cli/tui/feed-serializer.d.ts +29 -0
- package/dist/adapters/cli/tui/feed-serializer.js +70 -0
- package/dist/adapters/cli/tui/file-index.d.ts +8 -0
- package/dist/adapters/cli/tui/file-index.js +41 -0
- package/dist/adapters/cli/tui/hooks/use-agent.d.ts +54 -0
- package/dist/adapters/cli/tui/hooks/use-agent.js +177 -0
- package/dist/adapters/cli/tui/hooks/use-feed.d.ts +16 -0
- package/dist/adapters/cli/tui/hooks/use-feed.js +25 -0
- package/dist/adapters/cli/tui/hooks/use-file-watcher.d.ts +10 -0
- package/dist/adapters/cli/tui/hooks/use-file-watcher.js +43 -0
- package/dist/adapters/cli/tui/hooks/use-keybindings.d.ts +16 -0
- package/dist/adapters/cli/tui/hooks/use-keybindings.js +25 -0
- package/dist/adapters/cli/tui/hooks/use-theme.d.ts +8 -0
- package/dist/adapters/cli/tui/hooks/use-theme.js +12 -0
- package/dist/adapters/cli/tui/index.d.ts +19 -0
- package/dist/adapters/cli/tui/index.js +206 -0
- package/dist/adapters/cli/tui/ink-reset.d.ts +29 -0
- package/dist/adapters/cli/tui/ink-reset.js +57 -0
- package/dist/adapters/cli/tui/layout.d.ts +15 -0
- package/dist/adapters/cli/tui/layout.js +15 -0
- package/dist/adapters/cli/tui/logo/gradient.d.ts +11 -0
- package/dist/adapters/cli/tui/logo/gradient.js +31 -0
- package/dist/adapters/cli/tui/overlays/help-dialog.d.ts +4 -0
- package/dist/adapters/cli/tui/overlays/help-dialog.js +26 -0
- package/dist/adapters/cli/tui/overlays/model-selector.d.ts +14 -0
- package/dist/adapters/cli/tui/overlays/model-selector.js +43 -0
- package/dist/adapters/cli/tui/overlays/session-selector.d.ts +35 -0
- package/dist/adapters/cli/tui/overlays/session-selector.js +162 -0
- package/dist/adapters/cli/tui/overlays/settings-overlay.d.ts +24 -0
- package/dist/adapters/cli/tui/overlays/settings-overlay.js +126 -0
- package/dist/adapters/cli/tui/session-export.d.ts +21 -0
- package/dist/adapters/cli/tui/session-export.js +63 -0
- package/dist/adapters/cli/tui/theme.d.ts +23 -0
- package/dist/adapters/cli/tui/theme.js +22 -0
- package/dist/adapters/cli/tui/types.d.ts +52 -0
- package/dist/adapters/cli/tui/types.js +12 -0
- package/dist/adapters/sdk/agent.d.ts +20 -0
- package/dist/adapters/sdk/agent.js +356 -0
- package/dist/adapters/sdk/http.d.ts +43 -0
- package/dist/adapters/sdk/http.js +61 -0
- package/dist/adapters/sdk/index.d.ts +58 -0
- package/dist/adapters/sdk/index.js +209 -0
- package/dist/adapters/sdk/settings.d.ts +18 -0
- package/dist/adapters/sdk/settings.js +57 -0
- package/dist/adapters/sdk/tools.d.ts +7 -0
- package/dist/adapters/sdk/tools.js +13 -0
- package/dist/adapters/server/auth.d.ts +53 -0
- package/dist/adapters/server/auth.js +168 -0
- package/dist/adapters/server/index.d.ts +40 -0
- package/dist/adapters/server/index.js +255 -0
- package/dist/adapters/server/rest-gateway.d.ts +13 -0
- package/dist/adapters/server/rest-gateway.js +218 -0
- package/dist/adapters/server/rest.d.ts +37 -0
- package/dist/adapters/server/rest.js +341 -0
- package/dist/adapters/server/server-core.d.ts +55 -0
- package/dist/adapters/server/server-core.js +121 -0
- package/dist/adapters/server/session-store.d.ts +81 -0
- package/dist/adapters/server/session-store.js +272 -0
- package/dist/adapters/server/settings-handlers.d.ts +24 -0
- package/dist/adapters/server/settings-handlers.js +360 -0
- package/dist/adapters/server/standalone.d.ts +19 -0
- package/dist/adapters/server/standalone.js +113 -0
- package/dist/adapters/server/websocket.d.ts +26 -0
- package/dist/adapters/server/websocket.js +68 -0
- package/dist/adapters/server/ws-handlers.d.ts +32 -0
- package/dist/adapters/server/ws-handlers.js +523 -0
- package/dist/adapters/server/ws-types.d.ts +304 -0
- package/dist/adapters/server/ws-types.js +7 -0
- package/dist/core/agent-loop.d.ts +68 -0
- package/dist/core/agent-loop.js +423 -0
- package/dist/core/config.d.ts +115 -0
- package/dist/core/config.js +189 -0
- package/dist/core/errors.d.ts +58 -0
- package/dist/core/errors.js +88 -0
- package/dist/core/hooks.d.ts +35 -0
- package/dist/core/hooks.js +49 -0
- package/dist/core/index.d.ts +23 -0
- package/dist/core/index.js +29 -0
- package/dist/core/message-convert.d.ts +41 -0
- package/dist/core/message-convert.js +94 -0
- package/dist/core/middleware/auth.d.ts +24 -0
- package/dist/core/middleware/auth.js +28 -0
- package/dist/core/middleware/logging.d.ts +23 -0
- package/dist/core/middleware/logging.js +28 -0
- package/dist/core/middleware/rate-limit.d.ts +27 -0
- package/dist/core/middleware/rate-limit.js +38 -0
- package/dist/core/middleware/semantic-tools.d.ts +10 -0
- package/dist/core/middleware/semantic-tools.js +43 -0
- package/dist/core/middleware.d.ts +48 -0
- package/dist/core/middleware.js +38 -0
- package/dist/core/permission.d.ts +25 -0
- package/dist/core/permission.js +50 -0
- package/dist/core/provider-config.d.ts +129 -0
- package/dist/core/provider-config.js +273 -0
- package/dist/core/provider-env.d.ts +39 -0
- package/dist/core/provider-env.js +142 -0
- package/dist/core/provider-resolver.d.ts +12 -0
- package/dist/core/provider-resolver.js +12 -0
- package/dist/core/session-store.d.ts +75 -0
- package/dist/core/session-store.js +245 -0
- package/dist/core/settings-manager.d.ts +57 -0
- package/dist/core/settings-manager.js +359 -0
- package/dist/core/settings-schema.d.ts +38 -0
- package/dist/core/settings-schema.js +171 -0
- package/dist/core/skill-catalog.d.ts +6 -0
- package/dist/core/skill-catalog.js +17 -0
- package/dist/core/skill-invoker.d.ts +127 -0
- package/dist/core/skill-invoker.js +182 -0
- package/dist/core/stream-accumulator.d.ts +21 -0
- package/dist/core/stream-accumulator.js +51 -0
- package/dist/core/stream-manager.d.ts +58 -0
- package/dist/core/stream-manager.js +212 -0
- package/dist/core/tool-executor.d.ts +84 -0
- package/dist/core/tool-executor.js +256 -0
- package/dist/core/types.d.ts +259 -0
- package/dist/core/types.js +11 -0
- package/dist/gateway/gateway.d.ts +52 -0
- package/dist/gateway/gateway.js +537 -0
- package/dist/gateway/index.d.ts +21 -0
- package/dist/gateway/index.js +31 -0
- package/dist/gateway/openapi-importer.d.ts +15 -0
- package/dist/gateway/openapi-importer.js +66 -0
- package/dist/gateway/semantic-scorer.d.ts +7 -0
- package/dist/gateway/semantic-scorer.js +24 -0
- package/dist/gateway/settings-adapter.d.ts +49 -0
- package/dist/gateway/settings-adapter.js +137 -0
- package/dist/gateway/tool-factory.d.ts +9 -0
- package/dist/gateway/tool-factory.js +414 -0
- package/dist/gateway/types.d.ts +68 -0
- package/dist/gateway/types.js +7 -0
- package/dist/models-catalog.js +46 -0
- package/dist/providers/anthropic.d.ts +22 -0
- package/dist/providers/anthropic.js +148 -0
- package/dist/providers/factory.d.ts +10 -0
- package/dist/providers/factory.js +25 -0
- package/dist/providers/openai.d.ts +15 -0
- package/dist/providers/openai.js +71 -0
- package/dist/providers/types.d.ts +48 -0
- package/dist/providers/types.js +1 -0
- package/dist/skills/args.d.ts +37 -0
- package/dist/skills/args.js +99 -0
- package/dist/skills/index.d.ts +11 -0
- package/dist/skills/index.js +23 -0
- package/dist/skills/loader.d.ts +3 -0
- package/dist/skills/loader.js +59 -0
- package/dist/skills/parser.d.ts +7 -0
- package/dist/skills/parser.js +152 -0
- package/dist/skills/registry.d.ts +13 -0
- package/dist/skills/registry.js +74 -0
- package/dist/skills/resolver.d.ts +19 -0
- package/dist/skills/resolver.js +116 -0
- package/dist/skills/types.d.ts +74 -0
- package/dist/skills/types.js +50 -0
- package/dist/tools/browser.d.ts +2 -0
- package/dist/tools/browser.js +68 -0
- package/dist/tools/core.d.ts +20 -0
- package/dist/tools/core.js +244 -0
- package/dist/tools/email.d.ts +2 -0
- package/dist/tools/email.js +61 -0
- package/dist/tools/image.d.ts +2 -0
- package/dist/tools/image.js +257 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +88 -0
- package/dist/tools/interface.d.ts +22 -0
- package/dist/tools/interface.js +1 -0
- package/dist/tools/notify.d.ts +2 -0
- package/dist/tools/notify.js +100 -0
- package/dist/tools/prompt-optimizer.d.ts +2 -0
- package/dist/tools/prompt-optimizer.js +65 -0
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.js +184 -0
- package/dist/tools/search.d.ts +2 -0
- package/dist/tools/search.js +78 -0
- package/dist/tools/todos.d.ts +10 -0
- package/dist/tools/todos.js +50 -0
- package/package.json +119 -0
- package/skills/docker-ops/SKILL.md +329 -0
- package/skills/k8s-deploy/SKILL.md +397 -0
- package/skills/log-analyzer/SKILL.md +331 -0
- package/skills/speckit-analyze/SKILL.md +260 -0
- package/skills/speckit-checklist/SKILL.md +374 -0
- package/skills/speckit-clarify/SKILL.md +286 -0
- package/skills/speckit-constitution/SKILL.md +157 -0
- package/skills/speckit-implement/SKILL.md +224 -0
- package/skills/speckit-plan/SKILL.md +171 -0
- package/skills/speckit-specify/SKILL.md +346 -0
- package/skills/speckit-tasks/SKILL.md +215 -0
- package/skills/speckit-taskstoissues/SKILL.md +107 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe CLI — Settings Display Utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure formatting functions for settings display.
|
|
5
|
+
* Render functions produce bordered ASCII boxes for the wizard TUI.
|
|
6
|
+
*/
|
|
7
|
+
import { ENV_VAR_MAP, SETTINGS_MAP, SETTINGS_SCHEMA, } from '../../../core/settings-schema.js';
|
|
8
|
+
// ── Value formatting ──────────────────────────────────────────────────────
|
|
9
|
+
export function formatSettingValue(value, secret) {
|
|
10
|
+
if (value === undefined || value === null)
|
|
11
|
+
return '(not set)';
|
|
12
|
+
if (secret)
|
|
13
|
+
return maskValue(String(value));
|
|
14
|
+
if (typeof value === 'boolean')
|
|
15
|
+
return value ? 'true' : 'false';
|
|
16
|
+
return String(value);
|
|
17
|
+
}
|
|
18
|
+
export function maskValue(value) {
|
|
19
|
+
if (!value || value.length < 8)
|
|
20
|
+
return '******';
|
|
21
|
+
return `${value.slice(0, 3)}...${value.slice(-4)}`;
|
|
22
|
+
}
|
|
23
|
+
// ── Origin resolution ─────────────────────────────────────────────────────
|
|
24
|
+
export function getOriginLabel(dotKey, projectConfig, globalConfig) {
|
|
25
|
+
const envVar = ENV_VAR_MAP.get(dotKey);
|
|
26
|
+
if (envVar && process.env[envVar])
|
|
27
|
+
return `env: ${envVar}`;
|
|
28
|
+
const entry = SETTINGS_MAP.get(dotKey);
|
|
29
|
+
if (!entry)
|
|
30
|
+
return 'default';
|
|
31
|
+
if (projectConfig && hasPath(projectConfig, entry.configPath)) {
|
|
32
|
+
return 'project config';
|
|
33
|
+
}
|
|
34
|
+
if (globalConfig && hasPath(globalConfig, entry.configPath)) {
|
|
35
|
+
return 'global config';
|
|
36
|
+
}
|
|
37
|
+
return 'default';
|
|
38
|
+
}
|
|
39
|
+
export function formatSettingTable(settings) {
|
|
40
|
+
if (settings.length === 0)
|
|
41
|
+
return ' No settings found.';
|
|
42
|
+
const col1 = 24;
|
|
43
|
+
const col2 = 20;
|
|
44
|
+
const lines = [];
|
|
45
|
+
lines.push(` ${padTo('Setting', col1)} ${padTo('Value', col2)} Origin`);
|
|
46
|
+
lines.push(` ${'─'.repeat(col1)} ${'─'.repeat(col2)} ${'─'.repeat(20)}`);
|
|
47
|
+
for (const s of settings) {
|
|
48
|
+
const originSuffix = s.restartRequired ? ' [restart]' : '';
|
|
49
|
+
lines.push(` ${padTo(s.dotKey, col1)} ${padTo(s.value, col2)} ${s.origin}${originSuffix}`);
|
|
50
|
+
}
|
|
51
|
+
return lines.join('\n');
|
|
52
|
+
}
|
|
53
|
+
// ── Wizard renderers ──────────────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Render the bordered header box for Level 1 (category menu).
|
|
56
|
+
* Shows current provider, model, permissions, and auto-confirm.
|
|
57
|
+
*/
|
|
58
|
+
export function renderWizardHeader(manager) {
|
|
59
|
+
const width = Math.max(40, Math.min(50, (process.stdout.columns ?? 80) - 2));
|
|
60
|
+
const halfWidth = Math.floor((width - 4) / 2);
|
|
61
|
+
const provider = manager.get('provider').value ?? 'not configured';
|
|
62
|
+
const permLevel = manager.get('agent.permissionLevel').value ?? 'moderate';
|
|
63
|
+
const autoConfirm = manager.get('agent.autoConfirm').value ?? false;
|
|
64
|
+
// Resolve current model from active provider
|
|
65
|
+
let model = 'not configured';
|
|
66
|
+
const providerModelMap = {
|
|
67
|
+
'openai': 'providers.openai.model',
|
|
68
|
+
'anthropic': 'providers.anthropic.model',
|
|
69
|
+
'glm': 'providers.glm.model',
|
|
70
|
+
'openai-compatible': 'providers.openai-compat.model',
|
|
71
|
+
};
|
|
72
|
+
const modelKey = providerModelMap[String(provider)];
|
|
73
|
+
if (modelKey) {
|
|
74
|
+
const modelResult = manager.get(modelKey);
|
|
75
|
+
if (modelResult.value != null)
|
|
76
|
+
model = String(modelResult.value);
|
|
77
|
+
}
|
|
78
|
+
const left1 = `Provider: ${provider}`;
|
|
79
|
+
const right1 = `Model: ${model}`;
|
|
80
|
+
const left2 = `Permissions: ${permLevel}`;
|
|
81
|
+
const right2 = `Auto-confirm: ${autoConfirm ? 'on' : 'off'}`;
|
|
82
|
+
const lines = [];
|
|
83
|
+
lines.push(`┌${'─'.repeat(width - 2)}┐`);
|
|
84
|
+
lines.push(`│${padTo(' Current Settings', width - 2)}│`);
|
|
85
|
+
lines.push(`│${' '.repeat(width - 2)}│`);
|
|
86
|
+
lines.push(`│ ${padTo(left1, halfWidth)} ${padTo(right1, halfWidth)}│`);
|
|
87
|
+
lines.push(`│ ${padTo(left2, halfWidth)} ${padTo(right2, halfWidth)}│`);
|
|
88
|
+
lines.push(`│${' '.repeat(width - 2)}│`);
|
|
89
|
+
lines.push(`│${padTo(' ? What would you like to change?', width - 2)}│`);
|
|
90
|
+
lines.push(`└${'─'.repeat(width - 2)}┘`);
|
|
91
|
+
return lines.join('\n');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Render the aligned settings list for Level 2 (category drilldown).
|
|
95
|
+
* 3-column layout: label, value, origin.
|
|
96
|
+
*/
|
|
97
|
+
export function renderSettingsList(keys, manager) {
|
|
98
|
+
if (keys.length === 0)
|
|
99
|
+
return ' (none configured)';
|
|
100
|
+
const col1 = 28;
|
|
101
|
+
const col2 = 18;
|
|
102
|
+
const lines = [];
|
|
103
|
+
for (const key of keys) {
|
|
104
|
+
const entry = SETTINGS_MAP.get(key);
|
|
105
|
+
const schema = SETTINGS_SCHEMA.get(key);
|
|
106
|
+
const result = manager.get(key);
|
|
107
|
+
const label = entry?.label ?? key;
|
|
108
|
+
const value = formatSettingValue(result.value, result.masked);
|
|
109
|
+
const origin = result.origin;
|
|
110
|
+
const restartTag = schema?.restartRequired ? ' [restart]' : '';
|
|
111
|
+
lines.push(` ${padTo(label, col1)} ${padTo(value, col2)} ${chalk_dim(origin)}${restartTag}`);
|
|
112
|
+
}
|
|
113
|
+
return lines.join('\n');
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Render the bordered mini-form for Level 3 (setting edit).
|
|
117
|
+
* Shows setting name, current value, source, and type.
|
|
118
|
+
*/
|
|
119
|
+
export function renderSettingForm(dotKey, manager) {
|
|
120
|
+
const width = Math.max(36, Math.min(42, (process.stdout.columns ?? 80) - 2));
|
|
121
|
+
const entry = SETTINGS_MAP.get(dotKey);
|
|
122
|
+
const schema = SETTINGS_SCHEMA.get(dotKey);
|
|
123
|
+
const result = manager.get(dotKey);
|
|
124
|
+
const label = entry?.label ?? dotKey;
|
|
125
|
+
const current = formatSettingValue(result.value, result.masked);
|
|
126
|
+
const origin = result.origin;
|
|
127
|
+
const type = schema?.type ?? 'string';
|
|
128
|
+
const restartTag = schema?.restartRequired ? ' [restart required]' : '';
|
|
129
|
+
const lines = [];
|
|
130
|
+
lines.push(`┌${'─'.repeat(width - 2)}┐`);
|
|
131
|
+
lines.push(`│${padTo(` ${label}${restartTag}`, width - 2)}│`);
|
|
132
|
+
lines.push(`│${' '.repeat(width - 2)}│`);
|
|
133
|
+
lines.push(`│${padTo(` Current: ${current}`, width - 2)}│`);
|
|
134
|
+
lines.push(`│${padTo(` Source: ${origin}`, width - 2)}│`);
|
|
135
|
+
lines.push(`│${padTo(` Type: ${type}`, width - 2)}│`);
|
|
136
|
+
lines.push(`└${'─'.repeat(width - 2)}┘`);
|
|
137
|
+
return lines.join('\n');
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Render the category status label for the Level 1 menu.
|
|
141
|
+
*/
|
|
142
|
+
export function renderCategoryStatus(categoryKey, manager) {
|
|
143
|
+
const keys = [];
|
|
144
|
+
for (const entry of SETTINGS_MAP.values()) {
|
|
145
|
+
if (entry.category === categoryKey)
|
|
146
|
+
keys.push(entry.dotKey);
|
|
147
|
+
}
|
|
148
|
+
if (keys.length === 0)
|
|
149
|
+
return '(reserved)';
|
|
150
|
+
const configured = keys.filter(k => {
|
|
151
|
+
const v = manager.get(k).value;
|
|
152
|
+
return v !== undefined && v !== null && v !== '';
|
|
153
|
+
}).length;
|
|
154
|
+
if (categoryKey === 'providers') {
|
|
155
|
+
return `[${configured}/${keys.length}]`;
|
|
156
|
+
}
|
|
157
|
+
if (configured === 0)
|
|
158
|
+
return '[not set]';
|
|
159
|
+
if (configured === keys.length)
|
|
160
|
+
return '[configured]';
|
|
161
|
+
return '[partially configured]';
|
|
162
|
+
}
|
|
163
|
+
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
164
|
+
function padTo(str, len) {
|
|
165
|
+
if (str.length >= len)
|
|
166
|
+
return str.slice(0, len - 1) + ' ';
|
|
167
|
+
return str + ' '.repeat(len - str.length);
|
|
168
|
+
}
|
|
169
|
+
function hasPath(obj, pathParts) {
|
|
170
|
+
let current = obj;
|
|
171
|
+
for (const part of pathParts) {
|
|
172
|
+
if (current == null || typeof current !== 'object')
|
|
173
|
+
return false;
|
|
174
|
+
if (!(part in current))
|
|
175
|
+
return false;
|
|
176
|
+
current = current[part];
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
function chalk_dim(str) {
|
|
181
|
+
return `\x1b[2m${str}\x1b[0m`;
|
|
182
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe CLI — /settings Command Handler
|
|
3
|
+
*
|
|
4
|
+
* Read subcommands (list/get/reset/export/help) return their output for the
|
|
5
|
+
* adapter to render. The interactive wizard and `set` use inquirer (own stdin)
|
|
6
|
+
* and self-print — the TUI defers those. Subcommand router accepts direct ops.
|
|
7
|
+
*/
|
|
8
|
+
import type { CommandHandler } from './registry.js';
|
|
9
|
+
export declare function settingsHandler(): CommandHandler;
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe CLI — /settings Command Handler
|
|
3
|
+
*
|
|
4
|
+
* Read subcommands (list/get/reset/export/help) return their output for the
|
|
5
|
+
* adapter to render. The interactive wizard and `set` use inquirer (own stdin)
|
|
6
|
+
* and self-print — the TUI defers those. Subcommand router accepts direct ops.
|
|
7
|
+
*/
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import inquirer from 'inquirer';
|
|
10
|
+
import { SettingsManager } from '../../../core/settings-manager.js';
|
|
11
|
+
import { SettingsError } from '../../../core/settings-manager.js';
|
|
12
|
+
import { SETTINGS_MAP, SETTINGS_SCHEMA, SETTINGS_CATEGORIES, isSecretField, isRestartRequired, getSettingsByCategory, } from '../../../core/settings-schema.js';
|
|
13
|
+
import { formatSettingValue, formatSettingTable, renderWizardHeader, renderSettingsList, renderSettingForm, renderCategoryStatus, } from './settings-utils.js';
|
|
14
|
+
import { loadMergedConfig, loadJsonConfig, getConfigPaths, applyEnvOverrides } from '../config-loader.js';
|
|
15
|
+
import { isNonInteractive } from '../docker-utils.js';
|
|
16
|
+
function parseSubcommand(args) {
|
|
17
|
+
const parts = args.trim().split(/\s+/);
|
|
18
|
+
const first = parts[0]?.toLowerCase();
|
|
19
|
+
const subcommands = ['list', 'get', 'set', 'reset', 'export', 'help'];
|
|
20
|
+
if (!first)
|
|
21
|
+
return { sub: null, rest: '' };
|
|
22
|
+
if (subcommands.includes(first)) {
|
|
23
|
+
return { sub: first, rest: parts.slice(1).join(' ') };
|
|
24
|
+
}
|
|
25
|
+
return { sub: null, rest: args };
|
|
26
|
+
}
|
|
27
|
+
// ── Manager factory ───────────────────────────────────────────────────────
|
|
28
|
+
function createManager() {
|
|
29
|
+
const config = applyEnvOverrides(loadMergedConfig());
|
|
30
|
+
const paths = getConfigPaths();
|
|
31
|
+
const projectConfig = loadJsonConfig(paths.local);
|
|
32
|
+
const globalConfig = loadJsonConfig(paths.global);
|
|
33
|
+
return new SettingsManager({
|
|
34
|
+
config,
|
|
35
|
+
projectConfigPath: paths.local,
|
|
36
|
+
globalConfigPath: paths.global,
|
|
37
|
+
projectConfig: projectConfig,
|
|
38
|
+
globalConfig: globalConfig,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// ── Main handler ──────────────────────────────────────────────────────────
|
|
42
|
+
export function settingsHandler() {
|
|
43
|
+
return async (ctx) => {
|
|
44
|
+
const { sub, rest } = parseSubcommand(ctx.args);
|
|
45
|
+
const manager = createManager();
|
|
46
|
+
if (sub === 'list' || (sub === null && isNonInteractive())) {
|
|
47
|
+
return { output: handleList(manager) };
|
|
48
|
+
}
|
|
49
|
+
if (sub === 'get')
|
|
50
|
+
return { output: handleGet(manager, rest) };
|
|
51
|
+
if (sub === 'set') {
|
|
52
|
+
await handleSet(manager, rest);
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
if (sub === 'reset')
|
|
56
|
+
return { output: await handleReset(manager, rest) };
|
|
57
|
+
if (sub === 'export')
|
|
58
|
+
return { output: handleExport(manager) };
|
|
59
|
+
if (sub === 'help')
|
|
60
|
+
return { output: handleHelp() };
|
|
61
|
+
// sub === null && interactive → wizard (owns stdin)
|
|
62
|
+
await handleWizard(manager);
|
|
63
|
+
return {};
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// ── Level 1: Category Menu (interactive — self-prints) ────────────────────
|
|
67
|
+
async function handleWizard(manager) {
|
|
68
|
+
while (true) {
|
|
69
|
+
console.log(renderWizardHeader(manager));
|
|
70
|
+
const choices = SETTINGS_CATEGORIES.map(c => {
|
|
71
|
+
const status = renderCategoryStatus(c.key, manager);
|
|
72
|
+
const keys = getSettingsByCategory(c.key);
|
|
73
|
+
const suffix = keys.length > 0 ? `(${keys.length})` : '';
|
|
74
|
+
return { name: `${c.label} ${status} ${suffix}`, value: c.key };
|
|
75
|
+
});
|
|
76
|
+
let selected;
|
|
77
|
+
try {
|
|
78
|
+
const result = await inquirer.prompt([{
|
|
79
|
+
type: 'list',
|
|
80
|
+
name: 'selected',
|
|
81
|
+
message: 'Select a category:',
|
|
82
|
+
choices: [
|
|
83
|
+
...choices,
|
|
84
|
+
new inquirer.Separator(),
|
|
85
|
+
{ name: 'View full config (JSON)', value: '__export' },
|
|
86
|
+
{ name: 'Reset to defaults', value: '__reset' },
|
|
87
|
+
{ name: 'Done', value: '__done' },
|
|
88
|
+
],
|
|
89
|
+
}]);
|
|
90
|
+
selected = result.selected;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return; // Ctrl+C
|
|
94
|
+
}
|
|
95
|
+
if (selected === '__done')
|
|
96
|
+
return;
|
|
97
|
+
if (selected === '__export') {
|
|
98
|
+
console.log(handleExport(manager));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (selected === '__reset') {
|
|
102
|
+
try {
|
|
103
|
+
const { confirm } = await inquirer.prompt([{
|
|
104
|
+
type: 'confirm',
|
|
105
|
+
name: 'confirm',
|
|
106
|
+
message: 'Reset all settings to defaults?',
|
|
107
|
+
default: false,
|
|
108
|
+
}]);
|
|
109
|
+
if (confirm) {
|
|
110
|
+
await manager.resetAll();
|
|
111
|
+
console.log(chalk.green('All settings reset to defaults.'));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return; // Ctrl+C
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
await handleCategoryDrilldown(manager, selected);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ── Level 2: Settings List (interactive — self-prints) ────────────────────
|
|
123
|
+
async function handleCategoryDrilldown(manager, category) {
|
|
124
|
+
const catEntry = SETTINGS_CATEGORIES.find(c => c.key === category);
|
|
125
|
+
if (!catEntry)
|
|
126
|
+
return;
|
|
127
|
+
const keys = getSettingsByCategory(catEntry.key);
|
|
128
|
+
const categoryInfo = catEntry;
|
|
129
|
+
while (true) {
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(chalk.bold(` ${categoryInfo.label}`));
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log(renderSettingsList(keys, manager));
|
|
134
|
+
console.log('');
|
|
135
|
+
const settingChoices = keys.map(k => {
|
|
136
|
+
const entry = SETTINGS_MAP.get(k);
|
|
137
|
+
const result = manager.get(k);
|
|
138
|
+
const label = entry?.label ?? k;
|
|
139
|
+
const value = formatSettingValue(result.value, result.masked);
|
|
140
|
+
return { name: `${label}: ${value}`, value: k };
|
|
141
|
+
});
|
|
142
|
+
let selected;
|
|
143
|
+
try {
|
|
144
|
+
const result = await inquirer.prompt([{
|
|
145
|
+
type: 'list',
|
|
146
|
+
name: 'selected',
|
|
147
|
+
message: 'Select a setting to edit:',
|
|
148
|
+
choices: [
|
|
149
|
+
...settingChoices,
|
|
150
|
+
new inquirer.Separator(),
|
|
151
|
+
{ name: '← Back to categories', value: '__back' },
|
|
152
|
+
],
|
|
153
|
+
}]);
|
|
154
|
+
selected = result.selected;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return; // Ctrl+C
|
|
158
|
+
}
|
|
159
|
+
if (selected === '__back')
|
|
160
|
+
return;
|
|
161
|
+
await handleSettingEdit(manager, selected);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ── Level 3: Bordered Mini-Form (interactive — self-prints) ───────────────
|
|
165
|
+
async function handleSettingEdit(manager, dotKey) {
|
|
166
|
+
const entry = SETTINGS_MAP.get(dotKey);
|
|
167
|
+
const schema = SETTINGS_SCHEMA.get(dotKey);
|
|
168
|
+
const label = entry?.label ?? dotKey;
|
|
169
|
+
while (true) {
|
|
170
|
+
console.log('');
|
|
171
|
+
console.log(renderSettingForm(dotKey, manager));
|
|
172
|
+
console.log('');
|
|
173
|
+
// Warn if env var overrides this setting
|
|
174
|
+
const envVar = schema?.envVar;
|
|
175
|
+
if (envVar && process.env[envVar]) {
|
|
176
|
+
console.log(chalk.yellow(` ⚠ This setting is overridden by env var ${envVar}`));
|
|
177
|
+
console.log(chalk.yellow(` Your edit will only take effect when ${envVar} is unset.`));
|
|
178
|
+
console.log('');
|
|
179
|
+
}
|
|
180
|
+
// Determine prompt type from schema
|
|
181
|
+
let promptType;
|
|
182
|
+
let promptChoices;
|
|
183
|
+
let validate;
|
|
184
|
+
if (schema?.type === 'boolean') {
|
|
185
|
+
promptType = 'confirm';
|
|
186
|
+
}
|
|
187
|
+
else if (schema?.type === 'enum' && schema.enumValues) {
|
|
188
|
+
promptType = 'list';
|
|
189
|
+
promptChoices = schema.enumValues.map(v => ({ name: v, value: v }));
|
|
190
|
+
}
|
|
191
|
+
else if (schema?.secret) {
|
|
192
|
+
promptType = 'password';
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
promptType = 'input';
|
|
196
|
+
// M4: Validate number fields
|
|
197
|
+
if (schema?.type === 'number') {
|
|
198
|
+
validate = (input) => {
|
|
199
|
+
const num = Number(input);
|
|
200
|
+
if (isNaN(num))
|
|
201
|
+
return 'Please enter a valid number';
|
|
202
|
+
if (schema.min !== undefined && num < schema.min)
|
|
203
|
+
return `Minimum value is ${schema.min}`;
|
|
204
|
+
if (schema.max !== undefined && num > schema.max)
|
|
205
|
+
return `Maximum value is ${schema.max}`;
|
|
206
|
+
return true;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const current = manager.get(dotKey);
|
|
211
|
+
const promptConfig = {
|
|
212
|
+
type: promptType,
|
|
213
|
+
name: 'newValue',
|
|
214
|
+
message: `New value for ${label}:`,
|
|
215
|
+
};
|
|
216
|
+
if (promptType === 'password')
|
|
217
|
+
promptConfig.mask = '*';
|
|
218
|
+
if (promptType === 'input' && current.value != null && !schema?.secret) {
|
|
219
|
+
promptConfig.default = String(current.value);
|
|
220
|
+
}
|
|
221
|
+
if (validate)
|
|
222
|
+
promptConfig.validate = validate;
|
|
223
|
+
if (promptType === 'confirm' && current.value != null) {
|
|
224
|
+
promptConfig.default = Boolean(current.value);
|
|
225
|
+
}
|
|
226
|
+
if (promptType === 'list' && promptChoices) {
|
|
227
|
+
promptConfig.choices = promptChoices;
|
|
228
|
+
if (current.value != null)
|
|
229
|
+
promptConfig.default = String(current.value);
|
|
230
|
+
}
|
|
231
|
+
let newValue;
|
|
232
|
+
try {
|
|
233
|
+
const result = await inquirer.prompt([promptConfig]);
|
|
234
|
+
newValue = result.newValue;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return; // Ctrl+C
|
|
238
|
+
}
|
|
239
|
+
// Boolean confirm — value is already boolean, set directly
|
|
240
|
+
if (promptType === 'confirm') {
|
|
241
|
+
try {
|
|
242
|
+
await manager.set(dotKey, String(newValue));
|
|
243
|
+
const updated = manager.get(dotKey);
|
|
244
|
+
console.log(chalk.green(` Updated ${dotKey} = ${formatSettingValue(updated.value, updated.masked)}`));
|
|
245
|
+
}
|
|
246
|
+
catch (e) {
|
|
247
|
+
console.log(chalk.red(` Error: ${e.message}`));
|
|
248
|
+
}
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// For other types, empty means cancel
|
|
252
|
+
if (!newValue && newValue !== 0) {
|
|
253
|
+
console.log(chalk.dim(' Cancelled.'));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
await manager.set(dotKey, String(newValue));
|
|
258
|
+
const updated = manager.get(dotKey);
|
|
259
|
+
console.log(chalk.green(` Updated ${dotKey} = ${formatSettingValue(updated.value, updated.masked)}`));
|
|
260
|
+
if (isRestartRequired(dotKey)) {
|
|
261
|
+
console.log(chalk.yellow(' Restart the REPL for this change to take full effect.'));
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
catch (e) {
|
|
266
|
+
console.log(chalk.red(` Error: ${e.message}`));
|
|
267
|
+
console.log(chalk.dim(' Retrying...'));
|
|
268
|
+
// Loop to retry
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// ── Read subcommands (return output for the adapter to render) ────────────
|
|
273
|
+
function handleList(manager) {
|
|
274
|
+
const settings = manager.list();
|
|
275
|
+
const rows = settings.map(s => ({
|
|
276
|
+
dotKey: s.dotKey,
|
|
277
|
+
value: formatSettingValue(s.value, s.masked),
|
|
278
|
+
origin: s.origin,
|
|
279
|
+
category: s.category,
|
|
280
|
+
restartRequired: s.restartRequired,
|
|
281
|
+
}));
|
|
282
|
+
return formatSettingTable(rows);
|
|
283
|
+
}
|
|
284
|
+
function handleGet(manager, args) {
|
|
285
|
+
const dotKey = args.trim();
|
|
286
|
+
if (!dotKey) {
|
|
287
|
+
return chalk.yellow('Usage: /settings get <dot.key>');
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
const result = manager.get(dotKey);
|
|
291
|
+
const schema = SETTINGS_SCHEMA.get(dotKey);
|
|
292
|
+
const lines = [`${chalk.cyan(dotKey)} = ${formatSettingValue(result.value, result.masked)}`];
|
|
293
|
+
if (schema?.default !== undefined && result.value !== schema.default) {
|
|
294
|
+
lines.push(chalk.dim(` Default: ${schema.default}`));
|
|
295
|
+
}
|
|
296
|
+
lines.push(chalk.dim(` Source: ${result.origin}`));
|
|
297
|
+
if (isRestartRequired(dotKey)) {
|
|
298
|
+
lines.push(chalk.yellow(' [restart required]'));
|
|
299
|
+
}
|
|
300
|
+
return lines.join('\n');
|
|
301
|
+
}
|
|
302
|
+
catch (e) {
|
|
303
|
+
return chalk.red(e.message);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function handleSet(manager, args) {
|
|
307
|
+
const parts = args.trim().split(/\s+/);
|
|
308
|
+
const dotKey = parts[0];
|
|
309
|
+
let value = parts.slice(1).join(' ');
|
|
310
|
+
if (!dotKey) {
|
|
311
|
+
console.log(chalk.yellow('Usage: /settings set <dot.key> <value>'));
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// Secret field with no value — prompt
|
|
315
|
+
if (!value && isSecretField(dotKey)) {
|
|
316
|
+
const answers = await inquirer.prompt([{
|
|
317
|
+
type: 'password',
|
|
318
|
+
name: 'secretValue',
|
|
319
|
+
message: `Enter new value for ${dotKey}:`,
|
|
320
|
+
mask: '*',
|
|
321
|
+
}]);
|
|
322
|
+
value = answers.secretValue;
|
|
323
|
+
if (!value)
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
else if (!value || value === '-') {
|
|
327
|
+
const answers = await inquirer.prompt([{
|
|
328
|
+
type: 'password',
|
|
329
|
+
name: 'secretValue',
|
|
330
|
+
message: `Enter new value for ${dotKey}:`,
|
|
331
|
+
mask: '*',
|
|
332
|
+
}]);
|
|
333
|
+
value = answers.secretValue;
|
|
334
|
+
if (!value)
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
await manager.set(dotKey, value);
|
|
339
|
+
const result = manager.get(dotKey);
|
|
340
|
+
console.log(chalk.green(`Updated ${dotKey} = ${formatSettingValue(result.value, result.masked)}`));
|
|
341
|
+
if (isRestartRequired(dotKey)) {
|
|
342
|
+
console.log(chalk.yellow('Restart the REPL for this change to take full effect.'));
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
console.log(chalk.dim('Change takes effect immediately.'));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
if (e instanceof SettingsError) {
|
|
350
|
+
console.log(chalk.red(`Error: ${e.message}`));
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
console.log(chalk.red(`Error: ${e.message}`));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async function handleReset(manager, args) {
|
|
358
|
+
const dotKey = args.trim();
|
|
359
|
+
if (!dotKey) {
|
|
360
|
+
return chalk.yellow('Usage: /settings reset <dot.key>');
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
await manager.reset(dotKey);
|
|
364
|
+
return chalk.green(`Reset ${dotKey} to default.`);
|
|
365
|
+
}
|
|
366
|
+
catch (e) {
|
|
367
|
+
return chalk.red(`Error: ${e.message}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function handleExport(manager) {
|
|
371
|
+
const settings = manager.list();
|
|
372
|
+
const obj = {};
|
|
373
|
+
for (const s of settings) {
|
|
374
|
+
obj[s.dotKey] = formatSettingValue(s.value, s.masked);
|
|
375
|
+
}
|
|
376
|
+
return JSON.stringify(obj, null, 2);
|
|
377
|
+
}
|
|
378
|
+
function handleHelp() {
|
|
379
|
+
const lines = [
|
|
380
|
+
`${chalk.bold.cyan('/settings')} — View and edit configuration`,
|
|
381
|
+
'',
|
|
382
|
+
'Usage:',
|
|
383
|
+
' /settings Interactive settings wizard',
|
|
384
|
+
' /settings list [category] List settings in a category',
|
|
385
|
+
' /settings get <dot.key> Show current value + origin',
|
|
386
|
+
' /settings set <dot.key> <value> Set a value',
|
|
387
|
+
' /settings reset <dot.key> Remove a value (revert to default)',
|
|
388
|
+
' /settings export Print full merged config as JSON',
|
|
389
|
+
' /settings help Show this help',
|
|
390
|
+
'',
|
|
391
|
+
chalk.dim('Aliases: /config, /setting'),
|
|
392
|
+
chalk.dim('Setup wizard: /setup'),
|
|
393
|
+
];
|
|
394
|
+
return lines.join('\n');
|
|
395
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe CLI — /skills Command Handler
|
|
3
|
+
*
|
|
4
|
+
* Lists loaded skills with descriptions.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
export const skillsHandler = async (ctx) => {
|
|
8
|
+
const { agent } = ctx;
|
|
9
|
+
const registry = agent.getSkillRegistry();
|
|
10
|
+
if (!registry || registry.getAll().length === 0) {
|
|
11
|
+
return {
|
|
12
|
+
output: `${chalk.yellow('No skills loaded.')}\n${chalk.dim('Add skills to .zoe/skills/ or set ZOE_SKILLS_PATH env var.')}`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const lines = [chalk.bold.cyan('Loaded Skills:')];
|
|
16
|
+
for (const s of registry.getAll()) {
|
|
17
|
+
lines.push(`${chalk.green(` ${s.name}`)}${chalk.dim(` — ${s.description.split('\n')[0]}`)}`);
|
|
18
|
+
}
|
|
19
|
+
lines.push(chalk.dim('\nUse /<skill-name> <query> to invoke a skill directly.'));
|
|
20
|
+
return { output: lines.join('\n') };
|
|
21
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe CLI — Config Loader
|
|
3
|
+
*
|
|
4
|
+
* Re-exports core config utilities and adds CLI-specific chalk output.
|
|
5
|
+
*/
|
|
6
|
+
import type { AppConfig } from '../../core/config.js';
|
|
7
|
+
import { applyEnvOverrides, getConfigPath, getConfigDir, getConfigPaths, migrateLegacyFormat, resolveActiveProviderType, maskSecret } from '../../core/config.js';
|
|
8
|
+
export { AppConfig, applyEnvOverrides, getConfigPath, getConfigDir, getConfigPaths, migrateLegacyFormat, resolveActiveProviderType, maskSecret, };
|
|
9
|
+
/**
|
|
10
|
+
* Load and parse a JSON config file, returning {} on failure.
|
|
11
|
+
* Logs parse warnings to console with chalk.
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadJsonConfig(filePath: string): AppConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Load global and local configs and merge them.
|
|
16
|
+
* Priority: local > global.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadMergedConfig(): AppConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Save config to disk. If a local config exists, saves there; otherwise global.
|
|
21
|
+
*/
|
|
22
|
+
export declare function saveConfig(config: AppConfig): void;
|
|
23
|
+
/**
|
|
24
|
+
* Save config to a specific path.
|
|
25
|
+
* Logs errors to console with chalk.
|
|
26
|
+
*/
|
|
27
|
+
export declare function writeConfigToPath(config: AppConfig, targetFile: string): void;
|