@compilr-dev/cli 0.5.15 → 0.5.17

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/dist/agent.d.ts CHANGED
@@ -194,6 +194,10 @@ export declare function getBuiltinGuardrailInfo(): Array<{
194
194
  action: 'warn' | 'confirm' | 'block';
195
195
  category: string;
196
196
  }>;
197
+ /**
198
+ * Derive profile group IDs from a tool filter (array of tool names).
199
+ * A group is included if any of its tools are in the filter.
200
+ */
197
201
  /**
198
202
  * Creates an Agent instance configured with all tools.
199
203
  */
package/dist/agent.js CHANGED
@@ -5,11 +5,12 @@
5
5
  * ClaudeProvider, OllamaProvider, OpenAIProvider, or GeminiProvider
6
6
  * and available tools.
7
7
  */
8
- import { Agent, ContextManager, DEFAULT_CONTEXT_CONFIG, createTaskTool, createSuggestTool, defaultAgentTypes, TOOL_SETS, BUILTIN_GUARDRAILS, createProviderFromType, CapabilityManager, CAPABILITY_PACKS, GIT_SAFETY_MODULE, PLATFORM_TOOL_HINTS_MODULE, FACTORY_TOOL_HINTS_MODULE, TOOL_USAGE_META_MODULE, createLoadCapabilityTool, generateCapabilityCatalog, } from '@compilr-dev/sdk';
8
+ import { log } from './foundation/logger.js';
9
+ import { Agent, ContextManager, DEFAULT_CONTEXT_CONFIG, createTaskTool, createSuggestTool, defaultAgentTypes, TOOL_SETS, BUILTIN_GUARDRAILS, createProviderFromType, CapabilityManager, CapabilityContext, CAPABILITY_PACKS, GIT_SAFETY_MODULE, PLATFORM_TOOL_HINTS_MODULE, FACTORY_TOOL_HINTS_MODULE, TOOL_USAGE_META_MODULE, createLoadCapabilityTool, createCapabilityHook, resolveProfileGroups, resolveUpfrontGroups, } from '@compilr-dev/sdk';
9
10
  import { isAutoCompactEnabled, isDelegationEnabled, getSetting } from './settings/index.js';
10
11
  import { getApiKey } from './utils/credentials.js';
11
12
  import { createToolRegistry, createMinimalToolRegistry, getDirectTools, getMetaTools, initializeMetaTools, getToolIndexForSystemPrompt, getFilteredToolIndexForSystemPrompt, getToolStats, setMetaToolFilter, createToolFallback, getRegisteredMetaTools, } from './tools.js';
12
- import { TOOL_GROUPS } from '@compilr-dev/sdk';
13
+ // TOOL_GROUPS no longer needed — profile resolution moved to SDK
13
14
  import { setCapabilityManager } from './multi-agent/capability-loader.js';
14
15
  import { getAgentRegistry } from './agents/registry.js';
15
16
  import { SystemPromptBuilder } from './system-prompt/index.js';
@@ -135,144 +136,9 @@ function createProvider(options) {
135
136
  * Derive profile group IDs from a tool filter (array of tool names).
136
137
  * A group is included if any of its tools are in the filter.
137
138
  */
138
- function getProfileGroupsFromToolFilter(toolFilter) {
139
- const toolSet = new Set(toolFilter);
140
- const groups = [];
141
- for (const [groupId, group] of Object.entries(TOOL_GROUPS)) {
142
- if (group.tools.some((t) => toolSet.has(t))) {
143
- groups.push(groupId);
144
- }
145
- }
146
- return groups;
147
- }
148
- /**
149
- * Scan recent messages for "Tool not found: X" errors and auto-load
150
- * the corresponding capability packs (max 2 per turn, loadable only).
151
- * Also detects forbidden tool access for handoff suggestions.
152
- */
153
- function autoDetectCapabilities(manager, messages) {
154
- const MAX_AUTO_LOADS = 2;
155
- const result = { loaded: [], forbidden: [] };
156
- const toolNotFoundPattern = /Tool not found: (\w+)/;
157
- // Find the most recent user message (contains the last turn's tool results)
158
- for (let i = messages.length - 1; i >= 0; i--) {
159
- const msg = messages[i];
160
- if (msg.role !== 'user' || typeof msg.content === 'string')
161
- continue;
162
- // Scan this message's tool_result blocks for "Tool not found" errors
163
- for (const block of msg.content) {
164
- if (block.type !== 'tool_result' || !block.isError || !block.content)
165
- continue;
166
- const match = toolNotFoundPattern.exec(block.content);
167
- if (!match)
168
- continue;
169
- const toolName = match[1];
170
- const packId = manager.findPackForTool(toolName);
171
- if (!packId || manager.isLoaded(packId))
172
- continue;
173
- const tier = manager.getTier(packId);
174
- if (tier === 'loadable' && result.loaded.length < MAX_AUTO_LOADS) {
175
- manager.load(packId, 'auto-detect');
176
- result.loaded.push(packId);
177
- }
178
- else if (tier === 'forbidden') {
179
- result.forbidden.push({
180
- toolName,
181
- packId,
182
- suggestedAgent: manager.getSuggestedAgent(packId),
183
- });
184
- }
185
- }
186
- // Only scan the most recent user message
187
- break;
188
- }
189
- return result;
190
- }
191
- function createCapabilityHook(manager, basePrompt, orphanTools) {
192
- return (ctx) => {
193
- // Auto-detect capability needs from tool failures (safety net)
194
- let forbiddenHints = [];
195
- if (manager.hasLoadablePacks() || ctx.messages.length > 1) {
196
- const detected = autoDetectCapabilities(manager, ctx.messages);
197
- // Generate handoff suggestions for forbidden tool access
198
- if (detected.forbidden.length > 0) {
199
- forbiddenHints = detected.forbidden.map((f) => {
200
- const agentHint = f.suggestedAgent
201
- ? ` Consider using handoff("${f.suggestedAgent}", "Need ${f.packId} access for ${f.toolName}").`
202
- : '';
203
- return `[System] Tool "${f.toolName}" requires ${f.packId} capability which is outside your tool profile.${agentHint}`;
204
- });
205
- }
206
- }
207
- const activeToolNames = manager.getActiveToolNames();
208
- const activeModuleIds = manager.getActivePromptModuleIds();
209
- // Include orphan tools (not in any capability pack) — always visible
210
- const allActiveTools = [...activeToolNames, ...orphanTools];
211
- // Update meta-tool filter for this turn
212
- setMetaToolFilter(allActiveTools);
213
- // Build dynamic system prompt sections
214
- const dynamicSections = [];
215
- // Meta-tools protocol (how to use get_tool_info/use_tool)
216
- dynamicSections.push(TOOL_USAGE_META_MODULE.content);
217
- // Conditional modules based on loaded capability packs
218
- if (activeModuleIds.has('git-safety')) {
219
- dynamicSections.push(GIT_SAFETY_MODULE.content);
220
- }
221
- if (activeModuleIds.has('platform-tool-hints')) {
222
- dynamicSections.push(PLATFORM_TOOL_HINTS_MODULE.content);
223
- }
224
- if (activeModuleIds.has('factory-tool-hints')) {
225
- dynamicSections.push(FACTORY_TOOL_HINTS_MODULE.content);
226
- }
227
- // Prompt snippets from loaded packs
228
- const snippets = manager.getActivePromptSnippets();
229
- if (snippets.length > 0) {
230
- dynamicSections.push('## Active Tool Capabilities\n' + snippets.join('\n'));
231
- }
232
- // Filtered tool index (only loaded tools)
233
- const filteredIndex = getFilteredToolIndexForSystemPrompt(allActiveTools);
234
- dynamicSections.push(filteredIndex);
235
- // Capability catalog (if loadable packs remain)
236
- if (manager.hasLoadablePacks()) {
237
- const catalog = manager.getCatalog();
238
- const catalogSection = generateCapabilityCatalog(catalog, manager.getLoadedPackIds());
239
- if (catalogSection) {
240
- dynamicSections.push(catalogSection);
241
- }
242
- }
243
- // Preserve anchors from the current system prompt.
244
- // The agents library injects anchors into systemContent BEFORE this hook runs.
245
- // We must extract and re-append them, since we rebuild the prompt from basePrompt.
246
- let anchorsBlock = '';
247
- const anchorMarker = '## Active Anchors (Critical Information)';
248
- const anchorIdx = ctx.systemPrompt.indexOf(anchorMarker);
249
- if (anchorIdx > 0) {
250
- // Find the preceding "---\n\n" separator
251
- const separatorBefore = ctx.systemPrompt.lastIndexOf('---', anchorIdx);
252
- const startIdx = separatorBefore > 0 ? separatorBefore : anchorIdx;
253
- // Find the end: either the next "---" after the anchor block, or end of string
254
- const afterAnchor = ctx.systemPrompt.indexOf('\n\n---\n\n', anchorIdx);
255
- if (afterAnchor > 0) {
256
- // Anchors are followed by more content (e.g., role ending) — extract just the anchor block
257
- anchorsBlock = '\n\n' + ctx.systemPrompt.substring(startIdx, afterAnchor + 7); // include trailing "---\n\n"
258
- }
259
- else {
260
- // Anchors are at the end of the prompt
261
- anchorsBlock = '\n\n' + ctx.systemPrompt.substring(startIdx);
262
- }
263
- }
264
- const hookResult = {
265
- systemPrompt: basePrompt + '\n\n' + dynamicSections.join('\n\n') + anchorsBlock,
266
- };
267
- // Inject forbidden boundary messages (handoff suggestions)
268
- if (forbiddenHints.length > 0) {
269
- const hintContent = forbiddenHints.join('\n');
270
- const hintMessage = { role: 'user', content: hintContent };
271
- hookResult.messages = [...ctx.messages, hintMessage];
272
- }
273
- return hookResult;
274
- };
275
- }
139
+ // getProfileGroupsFromToolFilter → replaced by resolveProfileGroups from SDK
140
+ // autoDetectCapabilities replaced by autoDetectCapabilities from SDK
141
+ // createCapabilityHook replaced by createCapabilityHook + CapabilityContext from SDK
276
142
  /**
277
143
  * Creates an Agent instance configured with all tools.
278
144
  */
@@ -300,10 +166,10 @@ export function createAgent(options = {}) {
300
166
  const onEvent = options.verbose
301
167
  ? (event) => {
302
168
  if (event.type === 'tool_start') {
303
- console.log(`\n[Tool: ${event.name}] Starting...`);
169
+ log.debug({ component: 'agent', tool: event.name }, 'Tool starting');
304
170
  }
305
171
  else if (event.type === 'tool_end') {
306
- console.log(`[Tool: ${event.name}] Done`);
172
+ log.debug({ component: 'agent', tool: event.name }, 'Tool done');
307
173
  }
308
174
  }
309
175
  : undefined;
@@ -322,17 +188,14 @@ export function createAgent(options = {}) {
322
188
  // tools and prompt modules are active per-turn.
323
189
  // ==========================================================================
324
190
  let capabilityManager;
191
+ let capabilityContext;
325
192
  let orphanToolNames = [];
326
193
  if (useMetaTools) {
327
- // Determine profile groups from toolFilter
194
+ // Use SDK's profile resolver instead of local implementation
328
195
  const profileGroups = options.toolFilter
329
- ? getProfileGroupsFromToolFilter(options.toolFilter)
196
+ ? resolveProfileGroups(options.toolFilter)
330
197
  : Object.keys(CAPABILITY_PACKS);
331
- // Upfront groups: all direct-tier groups that are in the profile
332
- const upfrontGroupIds = Object.entries(TOOL_GROUPS)
333
- .filter(([, group]) => group.tier === 'direct')
334
- .map(([id]) => id)
335
- .filter((id) => profileGroups.includes(id));
198
+ const upfrontGroupIds = resolveUpfrontGroups(profileGroups);
336
199
  capabilityManager = new CapabilityManager({
337
200
  profileGroups,
338
201
  packs: CAPABILITY_PACKS,
@@ -341,7 +204,6 @@ export function createAgent(options = {}) {
341
204
  // Expose the capability manager for slash command handlers (Phase 3)
342
205
  setCapabilityManager(capabilityManager);
343
206
  // Compute orphan tools (meta-registry tools not in any capability pack)
344
- // These are always visible regardless of which packs are loaded.
345
207
  const packedTools = new Set();
346
208
  for (const pack of Object.values(CAPABILITY_PACKS)) {
347
209
  for (const tool of pack.tools) {
@@ -352,8 +214,13 @@ export function createAgent(options = {}) {
352
214
  orphanToolNames = registered
353
215
  .map((t) => t.definition.name)
354
216
  .filter((name) => !packedTools.has(name));
355
- // Set initial meta-tool filter
356
- setMetaToolFilter([...capabilityManager.getActiveToolNames(), ...orphanToolNames]);
217
+ // Create capability context (SDK) for filter sync + auto-load callback
218
+ capabilityContext = new CapabilityContext({
219
+ manager: capabilityManager,
220
+ orphanTools: orphanToolNames,
221
+ onFilterUpdate: (allowed) => { setMetaToolFilter(allowed); },
222
+ });
223
+ capabilityContext.syncFilter();
357
224
  // Log stats (unless quiet mode)
358
225
  if (!options.quiet) {
359
226
  const stats = getToolStats();
@@ -506,9 +373,17 @@ ${options.systemPromptAddition}
506
373
  messages.splice(1, 0, { role: 'user', content: hint });
507
374
  return { messages };
508
375
  },
509
- // Capability-aware system prompt assembly (with auto-detection safety net)
510
- ...(capabilityManager && baseSystemPrompt
511
- ? [createCapabilityHook(capabilityManager, baseSystemPrompt, orphanToolNames)]
376
+ // Capability-aware system prompt assembly (SDK hook with auto-detection)
377
+ ...(capabilityContext && baseSystemPrompt
378
+ ? [createCapabilityHook(capabilityContext, baseSystemPrompt, {
379
+ staticSections: [TOOL_USAGE_META_MODULE.content],
380
+ conditionalModules: [
381
+ { content: GIT_SAFETY_MODULE.content, whenModuleActive: ['git-safety'] },
382
+ { content: PLATFORM_TOOL_HINTS_MODULE.content, whenModuleActive: ['platform-tool-hints'] },
383
+ { content: FACTORY_TOOL_HINTS_MODULE.content, whenModuleActive: ['factory-tool-hints'] },
384
+ ],
385
+ getToolIndex: (allowedNames) => getFilteredToolIndexForSystemPrompt(allowedNames),
386
+ })]
512
387
  : []),
513
388
  ],
514
389
  beforeTool: [createFileLockCheckHook(getActiveProject)],
@@ -601,7 +476,7 @@ ${options.systemPromptAddition}
601
476
  // Now uses toolUseId for direct correlation (no more FIFO matching!)
602
477
  onSpawn: (agentType, description, toolUseId) => {
603
478
  if (options.verbose) {
604
- console.log(`\n[Sub-agent] Spawning ${agentType}: ${description} (${toolUseId ?? 'no-id'})`);
479
+ log.debug({ component: 'agent', agentType, toolUseId }, 'Spawning sub-agent: %s', description);
605
480
  }
606
481
  // Immediately notify with toolUseId - no more queuing/matching!
607
482
  if (toolUseId) {
@@ -613,7 +488,7 @@ ${options.systemPromptAddition}
613
488
  },
614
489
  onComplete: (agentType, result, toolUseId) => {
615
490
  if (options.verbose) {
616
- console.log(`[Sub-agent] ${agentType} completed (${String(result.iterations)} iterations)`);
491
+ log.debug({ component: 'agent', agentType, iterations: result.iterations }, 'Sub-agent completed');
617
492
  }
618
493
  // Direct notification with toolUseId - no more searching!
619
494
  if (toolUseId && options.onSubagentEnd) {
@@ -4,6 +4,7 @@
4
4
  * Handles loading, saving, and managing agent definitions.
5
5
  * Loads from both project (.compilr-dev/agents/) and user (~/.compilr-dev/agents/) directories.
6
6
  */
7
+ import { log } from '../foundation/logger.js';
7
8
  import * as fs from 'fs';
8
9
  import * as path from 'path';
9
10
  import { BUILTIN_AGENTS, PROJECT_AGENTS_DIR, USER_AGENTS_DIR, isValidAgentName, isBuiltinAgentName, } from './types.js';
@@ -61,16 +62,16 @@ function loadAgentsFromDir(dir, location) {
61
62
  const content = fs.readFileSync(filePath, 'utf-8');
62
63
  const parsed = parseAgentFile(content);
63
64
  if (!parsed) {
64
- console.warn(`Invalid agent file (no frontmatter): ${filePath}`);
65
+ log.warn({ component: 'agents', filePath }, 'Invalid agent file (no frontmatter)');
65
66
  continue;
66
67
  }
67
68
  const { frontmatter, content: systemPrompt } = parsed;
68
69
  if (!frontmatter.name || !frontmatter.description) {
69
- console.warn(`Invalid agent file (missing fields): ${filePath}`);
70
+ log.warn({ component: 'agents', filePath }, 'Invalid agent file (missing name or description)');
70
71
  continue;
71
72
  }
72
73
  if (!isValidAgentName(frontmatter.name)) {
73
- console.warn(`Invalid agent name: ${frontmatter.name} in ${filePath}`);
74
+ log.warn({ component: 'agents', name: frontmatter.name, filePath }, 'Invalid agent name');
74
75
  continue;
75
76
  }
76
77
  const model = (['sonnet', 'opus', 'haiku', 'inherit'].includes(frontmatter.model || '')
@@ -86,7 +87,7 @@ function loadAgentsFromDir(dir, location) {
86
87
  });
87
88
  }
88
89
  catch (error) {
89
- console.warn(`Failed to load agent file: ${filePath}`, error);
90
+ log.warn({ component: 'agents', filePath, err: error }, 'Failed to load agent file');
90
91
  }
91
92
  }
92
93
  return agents;
@@ -10,6 +10,7 @@
10
10
  * // Show login prompt
11
11
  * }
12
12
  */
13
+ import { log } from '../foundation/logger.js';
13
14
  import { v4 as uuidv4 } from 'uuid';
14
15
  import * as os from 'os';
15
16
  import { loadAuthData, saveAuthData, clearAuthData, updateSession, hasAuthData, checkAuthFilePermissions, } from './storage.js';
@@ -314,9 +315,8 @@ class AuthManager {
314
315
  }
315
316
  const result = await apiRefreshToken(this.authData.session.refreshToken);
316
317
  if (!result.success || !result.access_token || !result.refresh_token) {
317
- // Debug: log refresh failure details
318
- console.error('[auth] Token refresh failed:', result.error ?? 'missing token fields');
319
- console.error('[auth] API_BASE_URL:', process.env.COMPILR_API_URL ?? '(not set, using compilr.dev)');
318
+ log.error({ component: 'auth', error: result.error ?? 'missing token fields' }, 'Token refresh failed');
319
+ log.error({ component: 'auth', apiBaseUrl: process.env.COMPILR_API_URL ?? '(not set, using compilr.dev)' }, 'API base URL at time of failure');
320
320
  return false;
321
321
  }
322
322
  // Calculate expiry from expires_in (seconds), default to 1 hour
@@ -4,6 +4,7 @@
4
4
  * Handles loading, saving, and managing custom command definitions.
5
5
  * Loads from both project (.compilr-dev/commands/) and user (~/.compilr-dev/commands/) directories.
6
6
  */
7
+ import { log } from '../foundation/logger.js';
7
8
  import * as fs from 'fs';
8
9
  import * as path from 'path';
9
10
  import { PROJECT_COMMANDS_DIR, USER_COMMANDS_DIR, isValidCommandName, } from './types.js';
@@ -88,11 +89,11 @@ function loadCommandsFromDir(dir, location) {
88
89
  // Name from filename (without .md extension)
89
90
  const name = path.basename(file, '.md');
90
91
  if (!isValidCommandName(name)) {
91
- console.warn(`Invalid command name: ${name} in ${filePath}`);
92
+ log.warn({ component: 'commands', name, filePath }, 'Invalid command name');
92
93
  continue;
93
94
  }
94
95
  if (!parsed.content) {
95
- console.warn(`Empty command file: ${filePath}`);
96
+ log.warn({ component: 'commands', filePath }, 'Empty command file');
96
97
  continue;
97
98
  }
98
99
  commands.push({
@@ -104,7 +105,7 @@ function loadCommandsFromDir(dir, location) {
104
105
  });
105
106
  }
106
107
  catch (error) {
107
- console.warn(`Failed to load command file: ${filePath}`, error);
108
+ log.warn({ component: 'commands', filePath, err: error }, 'Failed to load command file');
108
109
  }
109
110
  }
110
111
  return commands;
Binary file
@@ -0,0 +1,9 @@
1
+ /**
2
+ * CLI logger instance.
3
+ *
4
+ * Diagnostic logging only — NOT for user-facing terminal output.
5
+ * User-facing output uses TerminalUI/TerminalRenderer.
6
+ *
7
+ * Logs are written to ~/.compilr-dev/logs/cli.log (rotated, 10MB x 5).
8
+ */
9
+ export declare const log: import("@compilr-dev/logger").Logger;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * CLI logger instance.
3
+ *
4
+ * Diagnostic logging only — NOT for user-facing terminal output.
5
+ * User-facing output uses TerminalUI/TerminalRenderer.
6
+ *
7
+ * Logs are written to ~/.compilr-dev/logs/cli.log (rotated, 10MB x 5).
8
+ */
9
+ import { join } from 'path';
10
+ import { homedir } from 'os';
11
+ import { createLogger } from '@compilr-dev/logger';
12
+ const logDir = join(homedir(), '.compilr-dev', 'logs');
13
+ export const log = createLogger({
14
+ package: 'cli',
15
+ filePath: join(logDir, 'cli.log'),
16
+ });
package/dist/repl-v2.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * Or imported and used with a real agent from index.ts
11
11
  */
12
12
  import { appendFileSync } from 'fs';
13
+ import { log } from './foundation/logger.js';
13
14
  import { generateEditDiff, generateWriteDiff } from './ui/diff.js';
14
15
  import { getPendingRequestsManager, setActiveSharedContext } from './multi-agent/index.js';
15
16
  import { parseInputForMentions, hasReferences, buildMessageWithContext, ContextResolver, buildContextMap, } from './multi-agent/index.js';
@@ -153,11 +154,11 @@ ${JSON.stringify(state.messages, null, 2)}
153
154
 
154
155
  `;
155
156
  appendFileSync(DEBUG_LOG_FILE, logEntry);
156
- console.log(`[DEBUG] Request #${String(debugRequestCount)} logged to ${DEBUG_LOG_FILE}`);
157
- console.log(`[DEBUG] Estimated: sys:${String(systemPromptTokens)}tok + msg:${String(messagesTokens)}tok + tools:${String(totalToolTokens)}tok = ${String(totalEstimate)}tok`);
157
+ log.debug({ component: 'repl', requestNum: debugRequestCount, file: DEBUG_LOG_FILE }, 'Debug request logged');
158
+ log.debug({ component: 'repl', systemPromptTokens, messagesTokens, totalToolTokens, totalEstimate }, 'Token estimate');
158
159
  }
159
160
  catch (error) {
160
- console.error('[DEBUG] Failed to log agent request:', error);
161
+ log.error({ component: 'repl', err: error }, 'Failed to log agent request');
161
162
  }
162
163
  }
163
164
  // =============================================================================
@@ -4,6 +4,7 @@
4
4
  * Centralized settings management for the CLI.
5
5
  * Persistence in ~/.compilr-dev/settings.json
6
6
  */
7
+ import { log } from '../foundation/logger.js';
7
8
  import * as fs from 'fs';
8
9
  import * as path from 'path';
9
10
  import * as os from 'os';
@@ -89,7 +90,7 @@ function saveToDisk(settings) {
89
90
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(settings, null, 2), 'utf-8');
90
91
  }
91
92
  catch (error) {
92
- console.error('Failed to save settings:', error);
93
+ log.error({ component: 'settings', err: error }, 'Failed to save settings');
93
94
  }
94
95
  }
95
96
  // =============================================================================
@@ -9,6 +9,7 @@
9
9
  * 2. Config file (~/.compilr-dev/settings.json)
10
10
  * 3. Default/derived value
11
11
  */
12
+ import { log } from '../foundation/logger.js';
12
13
  import * as fs from 'fs';
13
14
  import * as path from 'path';
14
15
  import * as os from 'os';
@@ -60,7 +61,7 @@ function savePathConfig(pathConfig) {
60
61
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), 'utf-8');
61
62
  }
62
63
  catch (error) {
63
- console.error('Failed to save path config:', error);
64
+ log.error({ component: 'settings', err: error }, 'Failed to save path config');
64
65
  }
65
66
  }
66
67
  /**
@@ -5,38 +5,8 @@
5
5
  * accessible from tools but are set up at the application level.
6
6
  * This avoids circular imports between index.ts and tool modules.
7
7
  */
8
- export interface AskUserQuestion {
9
- id: string;
10
- header: string;
11
- question: string;
12
- options?: string[];
13
- allowCustom?: boolean;
14
- multiSelect?: boolean;
15
- }
16
- export interface AskUserInput {
17
- questions: AskUserQuestion[];
18
- context?: string;
19
- }
20
- export interface AskUserResult {
21
- answers: Record<string, string | string[]>;
22
- skipped: string[];
23
- }
24
- export type AskUserHandler = (input: AskUserInput) => Promise<AskUserResult>;
25
- export interface AskUserSimpleInput {
26
- /** The question text */
27
- question: string;
28
- /** Predefined options (optional, max 5) */
29
- options?: string[];
30
- /** Allow free-text input (default: true) */
31
- allowCustom?: boolean;
32
- }
33
- export interface AskUserSimpleResult {
34
- /** User's answer */
35
- answer: string;
36
- /** True if user skipped the question */
37
- skipped: boolean;
38
- }
39
- export type AskUserSimpleHandler = (input: AskUserSimpleInput) => Promise<AskUserSimpleResult>;
8
+ import type { AskUserHandler, AskUserSimpleHandler } from '@compilr-dev/sdk';
9
+ export type { AskUserQuestion, AskUserInput, AskUserResult, AskUserHandler, AskUserSimpleInput, AskUserSimpleResult, AskUserSimpleHandler, } from '@compilr-dev/sdk';
40
10
  /**
41
11
  * Register the ask_user handler. Called during app initialization.
42
12
  */
@@ -1,19 +1,8 @@
1
1
  /**
2
- * Ask User Simple Tool - Single question with flat schema
2
+ * Ask User Simple Tool CLI wrapper around SDK factory
3
3
  *
4
- * A simplified version of ask_user designed for smaller models.
5
- * Uses a flat schema (no arrays of objects) that's easier for
6
- * models like Haiku, Flash, and GPT-4o-mini to generate correctly.
7
- *
8
- * Used by /sketch command for quick project outlines.
4
+ * Uses getAskUserSimpleHandler() from shared-handlers.ts which coordinates
5
+ * with the terminal footer (pause/resume) and shows the overlay.
9
6
  */
10
- interface AskUserSimpleInput {
11
- /** The question text */
12
- question: string;
13
- /** Predefined options (optional, max 5) */
14
- options?: string[];
15
- /** Allow free-text input (default: true) */
16
- allowCustom?: boolean;
17
- }
7
+ import { type AskUserSimpleInput } from '@compilr-dev/sdk';
18
8
  export declare const askUserSimpleTool: import("@compilr-dev/sdk").Tool<AskUserSimpleInput>;
19
- export {};
@@ -1,87 +1,15 @@
1
1
  /**
2
- * Ask User Simple Tool - Single question with flat schema
2
+ * Ask User Simple Tool CLI wrapper around SDK factory
3
3
  *
4
- * A simplified version of ask_user designed for smaller models.
5
- * Uses a flat schema (no arrays of objects) that's easier for
6
- * models like Haiku, Flash, and GPT-4o-mini to generate correctly.
7
- *
8
- * Used by /sketch command for quick project outlines.
4
+ * Uses getAskUserSimpleHandler() from shared-handlers.ts which coordinates
5
+ * with the terminal footer (pause/resume) and shows the overlay.
9
6
  */
10
- import { defineTool } from '@compilr-dev/sdk';
7
+ import { createAskUserSimpleTool } from '@compilr-dev/sdk';
11
8
  import { getAskUserSimpleHandler } from '../shared-handlers.js';
12
- // =============================================================================
13
- // Tool Definition
14
- // =============================================================================
15
- export const askUserSimpleTool = defineTool({
16
- name: 'ask_user_simple',
17
- description: 'Ask the user a single question. ' +
18
- 'Provide optional predefined choices or allow free-text input. ' +
19
- 'Use this for simple question flows - one question at a time. ' +
20
- 'Best suited for /sketch command and smaller models.',
21
- inputSchema: {
22
- type: 'object',
23
- properties: {
24
- question: {
25
- type: 'string',
26
- description: 'The question to ask the user',
27
- },
28
- options: {
29
- type: 'array',
30
- description: 'Predefined options for the user to choose from (max 5)',
31
- items: { type: 'string' },
32
- maxItems: 5,
33
- },
34
- allowCustom: {
35
- type: 'boolean',
36
- description: 'Allow free-text input in addition to options (default: true)',
37
- },
38
- },
39
- required: ['question'],
40
- },
41
- execute: async (input) => {
42
- try {
43
- // Validate input
44
- if (!input.question || input.question.trim().length === 0) {
45
- return {
46
- success: false,
47
- error: 'Question text is required',
48
- };
49
- }
50
- if (input.options && input.options.length > 5) {
51
- return {
52
- success: false,
53
- error: 'Maximum 5 options allowed',
54
- };
55
- }
56
- // Get the handler from shared-handlers
57
- const askUserSimpleHandler = getAskUserSimpleHandler();
58
- if (!askUserSimpleHandler) {
59
- return {
60
- success: false,
61
- error: 'ask_user_simple handler not initialized. This tool requires the CLI context.',
62
- };
63
- }
64
- // Show the overlay via the handler (which pauses footer)
65
- const result = await askUserSimpleHandler({
66
- question: input.question,
67
- options: input.options,
68
- allowCustom: input.allowCustom,
69
- });
70
- const output = {
71
- answer: result.answer,
72
- skipped: result.skipped,
73
- };
74
- return {
75
- success: true,
76
- result: output,
77
- };
78
- }
79
- catch (err) {
80
- return {
81
- success: false,
82
- error: `Failed to ask user: ${err instanceof Error ? err.message : String(err)}`,
83
- };
84
- }
85
- },
86
- silent: true,
9
+ export const askUserSimpleTool = createAskUserSimpleTool(async (input) => {
10
+ const handler = getAskUserSimpleHandler();
11
+ if (!handler) {
12
+ throw new Error('ask_user_simple handler not initialized. This tool requires the CLI context.');
13
+ }
14
+ return handler(input);
87
15
  });