@compilr-dev/cli 0.5.14 → 0.5.16

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,11 @@
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 { 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
9
  import { isAutoCompactEnabled, isDelegationEnabled, getSetting } from './settings/index.js';
10
10
  import { getApiKey } from './utils/credentials.js';
11
11
  import { createToolRegistry, createMinimalToolRegistry, getDirectTools, getMetaTools, initializeMetaTools, getToolIndexForSystemPrompt, getFilteredToolIndexForSystemPrompt, getToolStats, setMetaToolFilter, createToolFallback, getRegisteredMetaTools, } from './tools.js';
12
- import { TOOL_GROUPS } from '@compilr-dev/sdk';
12
+ // TOOL_GROUPS no longer needed — profile resolution moved to SDK
13
13
  import { setCapabilityManager } from './multi-agent/capability-loader.js';
14
14
  import { getAgentRegistry } from './agents/registry.js';
15
15
  import { SystemPromptBuilder } from './system-prompt/index.js';
@@ -135,144 +135,9 @@ function createProvider(options) {
135
135
  * Derive profile group IDs from a tool filter (array of tool names).
136
136
  * A group is included if any of its tools are in the filter.
137
137
  */
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
- }
138
+ // getProfileGroupsFromToolFilter → replaced by resolveProfileGroups from SDK
139
+ // autoDetectCapabilities replaced by autoDetectCapabilities from SDK
140
+ // createCapabilityHook replaced by createCapabilityHook + CapabilityContext from SDK
276
141
  /**
277
142
  * Creates an Agent instance configured with all tools.
278
143
  */
@@ -322,17 +187,14 @@ export function createAgent(options = {}) {
322
187
  // tools and prompt modules are active per-turn.
323
188
  // ==========================================================================
324
189
  let capabilityManager;
190
+ let capabilityContext;
325
191
  let orphanToolNames = [];
326
192
  if (useMetaTools) {
327
- // Determine profile groups from toolFilter
193
+ // Use SDK's profile resolver instead of local implementation
328
194
  const profileGroups = options.toolFilter
329
- ? getProfileGroupsFromToolFilter(options.toolFilter)
195
+ ? resolveProfileGroups(options.toolFilter)
330
196
  : 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));
197
+ const upfrontGroupIds = resolveUpfrontGroups(profileGroups);
336
198
  capabilityManager = new CapabilityManager({
337
199
  profileGroups,
338
200
  packs: CAPABILITY_PACKS,
@@ -341,7 +203,6 @@ export function createAgent(options = {}) {
341
203
  // Expose the capability manager for slash command handlers (Phase 3)
342
204
  setCapabilityManager(capabilityManager);
343
205
  // Compute orphan tools (meta-registry tools not in any capability pack)
344
- // These are always visible regardless of which packs are loaded.
345
206
  const packedTools = new Set();
346
207
  for (const pack of Object.values(CAPABILITY_PACKS)) {
347
208
  for (const tool of pack.tools) {
@@ -352,8 +213,13 @@ export function createAgent(options = {}) {
352
213
  orphanToolNames = registered
353
214
  .map((t) => t.definition.name)
354
215
  .filter((name) => !packedTools.has(name));
355
- // Set initial meta-tool filter
356
- setMetaToolFilter([...capabilityManager.getActiveToolNames(), ...orphanToolNames]);
216
+ // Create capability context (SDK) for filter sync + auto-load callback
217
+ capabilityContext = new CapabilityContext({
218
+ manager: capabilityManager,
219
+ orphanTools: orphanToolNames,
220
+ onFilterUpdate: (allowed) => { setMetaToolFilter(allowed); },
221
+ });
222
+ capabilityContext.syncFilter();
357
223
  // Log stats (unless quiet mode)
358
224
  if (!options.quiet) {
359
225
  const stats = getToolStats();
@@ -506,9 +372,17 @@ ${options.systemPromptAddition}
506
372
  messages.splice(1, 0, { role: 'user', content: hint });
507
373
  return { messages };
508
374
  },
509
- // Capability-aware system prompt assembly (with auto-detection safety net)
510
- ...(capabilityManager && baseSystemPrompt
511
- ? [createCapabilityHook(capabilityManager, baseSystemPrompt, orphanToolNames)]
375
+ // Capability-aware system prompt assembly (SDK hook with auto-detection)
376
+ ...(capabilityContext && baseSystemPrompt
377
+ ? [createCapabilityHook(capabilityContext, baseSystemPrompt, {
378
+ staticSections: [TOOL_USAGE_META_MODULE.content],
379
+ conditionalModules: [
380
+ { content: GIT_SAFETY_MODULE.content, whenModuleActive: ['git-safety'] },
381
+ { content: PLATFORM_TOOL_HINTS_MODULE.content, whenModuleActive: ['platform-tool-hints'] },
382
+ { content: FACTORY_TOOL_HINTS_MODULE.content, whenModuleActive: ['factory-tool-hints'] },
383
+ ],
384
+ getToolIndex: (allowedNames) => getFilteredToolIndexForSystemPrompt(allowedNames),
385
+ })]
512
386
  : []),
513
387
  ],
514
388
  beforeTool: [createFileLockCheckHook(getActiveProject)],
@@ -518,10 +392,10 @@ ${options.systemPromptAddition}
518
392
  ],
519
393
  },
520
394
  });
521
- // Load persisted anchors into the agent's AnchorManager
395
+ // Load persisted anchors into the agent's pin manager
522
396
  if (options.enableAnchors && options.persistedAnchors) {
523
397
  for (const anchor of options.persistedAnchors) {
524
- agent.addAnchor({
398
+ agent.addPin({
525
399
  id: anchor.id,
526
400
  content: anchor.content,
527
401
  priority: anchor.priority,
@@ -103,6 +103,15 @@ export const resetCommand = {
103
103
  ctx.restoreAgent(freshAgent);
104
104
  if (ctx.team) {
105
105
  ctx.team.setDefaultAgent(freshAgent);
106
+ // Re-inject team roster pin into the fresh agent so it knows about teammates
107
+ if (ctx.team.sharedContext.hasTeamRoster() && freshAgent.hasPins()) {
108
+ freshAgent.addPin({
109
+ id: 'team-roster',
110
+ content: ctx.team.sharedContext.formatTeamRoster(),
111
+ priority: 'info',
112
+ scope: 'session',
113
+ });
114
+ }
106
115
  }
107
116
  }
108
117
  catch {
@@ -93,8 +93,8 @@ export declare function handleProjectSwitch(oldProjectId: number | null, agent:
93
93
  turnCount?: number;
94
94
  }) => Promise<unknown>;
95
95
  clearHistory: () => unknown;
96
- clearAnchors?: (options?: AnchorClearOptions) => number;
97
- addAnchor?: (input: {
96
+ clearPins?: (options?: AnchorClearOptions) => number;
97
+ addPin?: (input: {
98
98
  id?: string;
99
99
  content: string;
100
100
  priority: AnchorPriority;
@@ -440,11 +440,11 @@ export async function handleProjectSwitch(oldProjectId, agent, teamOptions, agen
440
440
  await agentOptions.updateProjectState?.(newProjectId);
441
441
  // 2. Create fresh default agent with new project's system prompt
442
442
  newAgent = await agentOptions.agentFactory({});
443
- // 3. Transfer anchors to the NEW agent
444
- newAgent.clearAnchors({ scope: 'persistent' });
443
+ // 3. Transfer anchors to the NEW agent's pin manager
444
+ newAgent.clearPins({ scope: 'persistent' });
445
445
  const newAnchors = await getProjectAnchors(newProjectId);
446
446
  for (const anchor of newAnchors) {
447
- newAgent.addAnchor({
447
+ newAgent.addPin({
448
448
  id: anchor.id,
449
449
  content: anchor.content,
450
450
  priority: anchor.priority,
@@ -465,12 +465,12 @@ export async function handleProjectSwitch(oldProjectId, agent, teamOptions, agen
465
465
  // If no new agent was created, update the old agent as before
466
466
  if (!newAgent) {
467
467
  try {
468
- // Update old agent's anchors
469
- if (agent?.clearAnchors && agent.addAnchor) {
470
- agent.clearAnchors({ scope: 'persistent' });
468
+ // Update old agent's pins
469
+ if (agent?.clearPins && agent.addPin) {
470
+ agent.clearPins({ scope: 'persistent' });
471
471
  const newAnchors = await getProjectAnchors(newProjectId);
472
472
  for (const anchor of newAnchors) {
473
- agent.addAnchor({
473
+ agent.addPin({
474
474
  id: anchor.id,
475
475
  content: anchor.content,
476
476
  priority: anchor.priority,
@@ -731,9 +731,9 @@ export const resumeCommand = {
731
731
  path: activeProject.path,
732
732
  });
733
733
  }
734
- // Set team roster anchor
735
- if (persistedTeam.sharedContext.hasTeamRoster() && ctx.agent?.hasAnchors()) {
736
- ctx.agent.addAnchor({
734
+ // Set team roster pin
735
+ if (persistedTeam.sharedContext.hasTeamRoster() && ctx.agent?.hasPins()) {
736
+ ctx.agent.addPin({
737
737
  id: 'team-roster',
738
738
  content: persistedTeam.sharedContext.formatTeamRoster(),
739
739
  priority: 'info',
Binary file
@@ -56,9 +56,9 @@ export function handleMemoryInput(input, ctx) {
56
56
  conversation.printSuccess(`Saved${priorityLabel}: "${truncate(note, 50)}" → ${scope}`);
57
57
  terminal.writeLine('');
58
58
  ctx.footer.forceRender();
59
- // Also add to agent's anchor manager if enabled (for current session)
60
- if (ctx.agent.getAnchorManager()) {
61
- ctx.agent.addAnchor({
59
+ // Also add to agent's pin manager if enabled (for current session)
60
+ if (ctx.agent.getPinManager()) {
61
+ ctx.agent.addPin({
62
62
  id: anchor.id,
63
63
  content: note,
64
64
  priority,
package/dist/repl-v2.js CHANGED
@@ -1181,9 +1181,9 @@ export class ReplV2 {
1181
1181
  path: activeProject.path,
1182
1182
  });
1183
1183
  }
1184
- // Set team roster as anchor on the default agent
1185
- if (persistedTeam.sharedContext.hasTeamRoster() && currentAgent?.hasAnchors()) {
1186
- currentAgent.addAnchor({
1184
+ // Set team roster as pin on the default agent
1185
+ if (persistedTeam.sharedContext.hasTeamRoster() && currentAgent?.hasPins()) {
1186
+ currentAgent.addPin({
1187
1187
  id: 'team-roster',
1188
1188
  content: persistedTeam.sharedContext.formatTeamRoster(),
1189
1189
  priority: 'info',
@@ -2122,9 +2122,9 @@ export class ReplV2 {
2122
2122
  const priorityLabel = priority === 'info' ? '' : ` (${priority})`;
2123
2123
  const truncatedNote = truncate(note, 50);
2124
2124
  this.ui.print({ type: 'success', message: `Saved${priorityLabel}: "${truncatedNote}" → ${scope}` });
2125
- // Also add to agent's anchor manager if enabled (for current session)
2126
- if (this.agent?.getAnchorManager()) {
2127
- this.agent.addAnchor({
2125
+ // Also add to agent's pin manager if enabled (for current session)
2126
+ if (this.agent?.getPinManager()) {
2127
+ this.agent.addPin({
2128
2128
  id: anchor.id,
2129
2129
  content: note,
2130
2130
  priority,
@@ -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
  });
@@ -1,32 +1,9 @@
1
1
  /**
2
- * Ask User Tool - Presents structured questions to the user
2
+ * Ask User Tool CLI wrapper around SDK factory
3
3
  *
4
- * This tool allows the agent to ask questions with predefined options
5
- * or free-text input. Used during /design and /refine workflows.
6
- *
7
- * Note: This tool uses a callback pattern to coordinate with the footer.
8
- * The actual overlay is shown via getAskUserHandler() from shared-handlers.ts
9
- * which is registered during app initialization and properly pauses the footer.
4
+ * Uses getAskUserHandler() from shared-handlers.ts which coordinates
5
+ * with the terminal footer (pause/resume) and shows the overlay.
10
6
  */
11
- export interface AskUserQuestion {
12
- /** Unique identifier for the question (e.g., "app_type", "target_users") */
13
- id: string;
14
- /** Short label for tab display (e.g., "App Type", "Users") - max 12 chars */
15
- header: string;
16
- /** The question text */
17
- question: string;
18
- /** Predefined options (optional) */
19
- options?: string[];
20
- /** Allow free-text input (default: true) */
21
- allowCustom?: boolean;
22
- /** Allow multiple selections (default: false) */
23
- multiSelect?: boolean;
24
- }
25
- interface AskUserInput {
26
- /** 1-5 questions to ask */
27
- questions: AskUserQuestion[];
28
- /** Optional context shown above questions */
29
- context?: string;
30
- }
7
+ import { type AskUserInput } from '@compilr-dev/sdk';
8
+ export type { AskUserQuestion } from '@compilr-dev/sdk';
31
9
  export declare const askUserTool: import("@compilr-dev/sdk").Tool<AskUserInput>;
32
- export {};