@compilr-dev/cli 0.5.4 → 0.5.6
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/.tsbuildinfo.app +1 -1
- package/dist/.tsbuildinfo.data +1 -1
- package/dist/.tsbuildinfo.domain +1 -1
- package/dist/.tsbuildinfo.foundation +1 -1
- package/dist/agent.js +225 -16
- package/dist/commands-v2/handlers/project.js +35 -5
- package/dist/commands-v2/handlers/settings.js +13 -7
- package/dist/compilr-diff-companion.vsix +0 -0
- package/dist/multi-agent/capability-loader.d.ts +29 -0
- package/dist/multi-agent/capability-loader.js +54 -0
- package/dist/repl-v2.js +8 -1
- package/dist/settings/index.d.ts +11 -0
- package/dist/settings/index.js +24 -1
- package/dist/tool-names.d.ts +1 -0
- package/dist/tool-names.js +2 -0
- package/dist/tools/meta-tools.d.ts +5 -0
- package/dist/tools/meta-tools.js +11 -0
- package/dist/tools.d.ts +6 -0
- package/dist/tools.js +9 -1
- package/dist/ui/keyboard-handler.d.ts +6 -4
- package/dist/ui/keyboard-handler.js +11 -11
- package/dist/ui/live-region-facade.js +1 -0
- package/dist/ui/overlay/impl/config-overlay-v2.js +6 -5
- package/dist/ui/terminal-render-item.js +25 -3
- package/dist/ui/terminal-types.d.ts +3 -0
- package/dist/ui/terminal-types.js +1 -0
- package/dist/ui/terminal-ui.d.ts +6 -6
- package/dist/ui/terminal-ui.js +38 -24
- package/dist/ui/tool-formatters.js +368 -0
- package/dist/utils/readonly-tools.d.ts +8 -0
- package/dist/utils/readonly-tools.js +29 -0
- package/package.json +3 -3
package/dist/agent.js
CHANGED
|
@@ -5,10 +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, } from '@compilr-dev/sdk';
|
|
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';
|
|
9
9
|
import { isAutoCompactEnabled, isDelegationEnabled, getSetting } from './settings/index.js';
|
|
10
10
|
import { getApiKey } from './utils/credentials.js';
|
|
11
|
-
import { createToolRegistry, createMinimalToolRegistry, getDirectTools, getMetaTools, initializeMetaTools, getToolIndexForSystemPrompt, getToolStats, setMetaToolFilter, createToolFallback, } from './tools.js';
|
|
11
|
+
import { createToolRegistry, createMinimalToolRegistry, getDirectTools, getMetaTools, initializeMetaTools, getToolIndexForSystemPrompt, getFilteredToolIndexForSystemPrompt, getToolStats, setMetaToolFilter, createToolFallback, getRegisteredMetaTools, } from './tools.js';
|
|
12
|
+
import { TOOL_GROUPS } from './multi-agent/tool-config.js';
|
|
13
|
+
import { setCapabilityManager } from './multi-agent/capability-loader.js';
|
|
12
14
|
import { getAgentRegistry } from './agents/registry.js';
|
|
13
15
|
import { SystemPromptBuilder } from './system-prompt/index.js';
|
|
14
16
|
import { getDefaultModelForTier, getModelContextWindow } from './models/index.js';
|
|
@@ -126,6 +128,151 @@ function createProvider(options) {
|
|
|
126
128
|
estimateTokens,
|
|
127
129
|
});
|
|
128
130
|
}
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// Dynamic Capability Loading Helpers
|
|
133
|
+
// =============================================================================
|
|
134
|
+
/**
|
|
135
|
+
* Derive profile group IDs from a tool filter (array of tool names).
|
|
136
|
+
* A group is included if any of its tools are in the filter.
|
|
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
|
+
}
|
|
129
276
|
/**
|
|
130
277
|
* Creates an Agent instance configured with all tools.
|
|
131
278
|
*/
|
|
@@ -165,18 +312,64 @@ export function createAgent(options = {}) {
|
|
|
165
312
|
// which re-injects them on every LLM call (survives compaction)
|
|
166
313
|
// Initialize meta-tools if enabled (needed for tool index)
|
|
167
314
|
// includeWarmTools=true moves 11 warm tools into meta-registry (hot-tools mode)
|
|
168
|
-
|
|
169
|
-
if (
|
|
315
|
+
const useMetaTools = options.enableMetaTools && !options.minimal;
|
|
316
|
+
if (useMetaTools) {
|
|
170
317
|
initializeMetaTools(true);
|
|
171
|
-
|
|
172
|
-
|
|
318
|
+
}
|
|
319
|
+
// ==========================================================================
|
|
320
|
+
// Dynamic Capability Loading
|
|
321
|
+
// When meta-tools are enabled, create a CapabilityManager to control which
|
|
322
|
+
// tools and prompt modules are active per-turn.
|
|
323
|
+
// ==========================================================================
|
|
324
|
+
let capabilityManager;
|
|
325
|
+
let orphanToolNames = [];
|
|
326
|
+
if (useMetaTools) {
|
|
327
|
+
// Determine profile groups from toolFilter
|
|
328
|
+
const profileGroups = options.toolFilter
|
|
329
|
+
? getProfileGroupsFromToolFilter(options.toolFilter)
|
|
330
|
+
: 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));
|
|
336
|
+
capabilityManager = new CapabilityManager({
|
|
337
|
+
profileGroups,
|
|
338
|
+
packs: CAPABILITY_PACKS,
|
|
339
|
+
upfrontGroups: upfrontGroupIds,
|
|
340
|
+
});
|
|
341
|
+
// Expose the capability manager for slash command handlers (Phase 3)
|
|
342
|
+
setCapabilityManager(capabilityManager);
|
|
343
|
+
// Compute orphan tools (meta-registry tools not in any capability pack)
|
|
344
|
+
// These are always visible regardless of which packs are loaded.
|
|
345
|
+
const packedTools = new Set();
|
|
346
|
+
for (const pack of Object.values(CAPABILITY_PACKS)) {
|
|
347
|
+
for (const tool of pack.tools) {
|
|
348
|
+
packedTools.add(tool);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const registered = getRegisteredMetaTools();
|
|
352
|
+
orphanToolNames = registered
|
|
353
|
+
.map((t) => t.definition.name)
|
|
354
|
+
.filter((name) => !packedTools.has(name));
|
|
355
|
+
// Set initial meta-tool filter
|
|
356
|
+
setMetaToolFilter([...capabilityManager.getActiveToolNames(), ...orphanToolNames]);
|
|
357
|
+
// Log stats (unless quiet mode)
|
|
173
358
|
if (!options.quiet) {
|
|
174
359
|
const stats = getToolStats();
|
|
360
|
+
const loadedPacks = capabilityManager.getLoadedPackIds();
|
|
361
|
+
const loadableCount = capabilityManager.getCatalog().length;
|
|
175
362
|
console.log(`[Meta-tools] Direct: ${String(stats.directTools)}, ` +
|
|
176
363
|
`Meta: ${String(stats.metaRegistryTools)}, ` +
|
|
177
364
|
`Est. savings: ~${String(stats.tokenSavings)} tokens`);
|
|
365
|
+
console.log(`[Capabilities] Upfront: ${String(loadedPacks.length)} packs, ` +
|
|
366
|
+
`Loadable: ${String(loadableCount)} packs`);
|
|
178
367
|
}
|
|
179
368
|
}
|
|
369
|
+
// Generate tool index for static system prompt (only when NOT using capability loading)
|
|
370
|
+
const metaToolsIndex = useMetaTools && !capabilityManager
|
|
371
|
+
? getToolIndexForSystemPrompt()
|
|
372
|
+
: undefined;
|
|
180
373
|
let systemPrompt;
|
|
181
374
|
// Check if we should use minimal system prompt (for testing role identity)
|
|
182
375
|
if (options.useMinimalSystemPrompt && options.systemPromptAddition) {
|
|
@@ -185,16 +378,18 @@ export function createAgent(options = {}) {
|
|
|
185
378
|
}
|
|
186
379
|
else {
|
|
187
380
|
// Use modular system prompt builder
|
|
188
|
-
// When
|
|
381
|
+
// When capability loading is active, exclude conditional modules:
|
|
382
|
+
// git-safety, platform-tool-hints, factory-tool-hints, tool-usage-meta
|
|
383
|
+
// These are dynamically added by the BeforeLLM hook based on loaded capabilities.
|
|
189
384
|
const promptBuilder = new SystemPromptBuilder({
|
|
190
|
-
enableMetaTools:
|
|
385
|
+
enableMetaTools: capabilityManager ? false : useMetaTools,
|
|
386
|
+
hasGit: capabilityManager ? false : undefined, // false = skip git-safety; undefined = auto-detect
|
|
191
387
|
projectName: options.projectName,
|
|
192
388
|
projectContext: options.projectContext,
|
|
193
389
|
guidedModeContext: options.guidedModeContext,
|
|
194
390
|
planModeContext: options.planModeContext,
|
|
195
391
|
metaToolsIndex,
|
|
196
392
|
hasRoleIdentity: !!options.systemPromptAddition, // Skip default identity when role is specified
|
|
197
|
-
// hasGit is auto-detected by builder
|
|
198
393
|
});
|
|
199
394
|
const buildResult = promptBuilder.build();
|
|
200
395
|
systemPrompt = buildResult.prompt;
|
|
@@ -219,6 +414,8 @@ ${options.systemPromptAddition}
|
|
|
219
414
|
systemPrompt = options.systemPromptAddition + '\n\n' + systemPrompt + roleEnding;
|
|
220
415
|
}
|
|
221
416
|
}
|
|
417
|
+
// Store base prompt for capability-aware BeforeLLM hook
|
|
418
|
+
const baseSystemPrompt = capabilityManager ? systemPrompt : undefined;
|
|
222
419
|
const agent = new Agent({
|
|
223
420
|
provider,
|
|
224
421
|
systemPrompt,
|
|
@@ -284,9 +481,10 @@ ${options.systemPromptAddition}
|
|
|
284
481
|
},
|
|
285
482
|
}
|
|
286
483
|
: undefined,
|
|
287
|
-
// Hooks: context pressure, file lock check/acquire
|
|
484
|
+
// Hooks: context pressure, capability loading, file lock check/acquire
|
|
288
485
|
hooks: {
|
|
289
486
|
beforeLLM: [
|
|
487
|
+
// Context pressure hook
|
|
290
488
|
(ctx) => {
|
|
291
489
|
const utilization = contextManager.getUtilization();
|
|
292
490
|
if (utilization < 0.3)
|
|
@@ -306,6 +504,10 @@ ${options.systemPromptAddition}
|
|
|
306
504
|
messages.splice(1, 0, { role: 'user', content: hint });
|
|
307
505
|
return { messages };
|
|
308
506
|
},
|
|
507
|
+
// Capability-aware system prompt assembly (with auto-detection safety net)
|
|
508
|
+
...(capabilityManager && baseSystemPrompt
|
|
509
|
+
? [createCapabilityHook(capabilityManager, baseSystemPrompt, orphanToolNames)]
|
|
510
|
+
: []),
|
|
309
511
|
],
|
|
310
512
|
beforeTool: [createFileLockCheckHook(getActiveProject)],
|
|
311
513
|
afterTool: [
|
|
@@ -338,6 +540,10 @@ ${options.systemPromptAddition}
|
|
|
338
540
|
// Hot-tools mode: only hot tools + get_tool_info (for schema introspection)
|
|
339
541
|
// Warm tools + meta-registry tools are accessed transparently via fallback handler
|
|
340
542
|
tools = [...getDirectTools(true), ...getMetaTools()];
|
|
543
|
+
// Add load_capability tool when the agent has loadable packs
|
|
544
|
+
if (capabilityManager?.hasLoadablePacks()) {
|
|
545
|
+
tools.push(createLoadCapabilityTool(capabilityManager));
|
|
546
|
+
}
|
|
341
547
|
}
|
|
342
548
|
else {
|
|
343
549
|
// Legacy mode: all tools declared directly
|
|
@@ -347,14 +553,17 @@ ${options.systemPromptAddition}
|
|
|
347
553
|
// Apply tool filter if specified (filter direct tools)
|
|
348
554
|
if (options.toolFilter && options.toolFilter.length > 0) {
|
|
349
555
|
const allowedTools = new Set(options.toolFilter);
|
|
350
|
-
// get_tool_info
|
|
351
|
-
const alwaysAllowed = new Set(['get_tool_info']);
|
|
556
|
+
// get_tool_info and load_capability are always allowed
|
|
557
|
+
const alwaysAllowed = new Set(['get_tool_info', 'load_capability']);
|
|
352
558
|
tools = tools.filter((tool) => allowedTools.has(tool.definition.name) || alwaysAllowed.has(tool.definition.name));
|
|
353
|
-
//
|
|
354
|
-
|
|
559
|
+
// When capability loading is active, the BeforeLLM hook manages the meta-tool filter.
|
|
560
|
+
// Otherwise, set it to the full tool filter.
|
|
561
|
+
if (!capabilityManager) {
|
|
562
|
+
setMetaToolFilter(options.toolFilter);
|
|
563
|
+
}
|
|
355
564
|
}
|
|
356
|
-
else {
|
|
357
|
-
// No filter - clear any previous filter
|
|
565
|
+
else if (!capabilityManager) {
|
|
566
|
+
// No filter and no capability loading - clear any previous filter
|
|
358
567
|
setMetaToolFilter(null);
|
|
359
568
|
}
|
|
360
569
|
// Type assertion needed for tool registry compatibility
|
|
@@ -10,6 +10,25 @@ import { setCurrentProject, getCurrentProject } from '../../tools/project-db.js'
|
|
|
10
10
|
import { getSkillPrompt } from '../../repl-helpers.js';
|
|
11
11
|
import { getModelTier, modelMeetsTier } from '../../utils/model-tiers.js';
|
|
12
12
|
import { getCurrentProjectIdForTracking, handleProjectSwitch } from './session.js';
|
|
13
|
+
import { loadCapabilitiesForSkill } from '../../multi-agent/capability-loader.js';
|
|
14
|
+
/**
|
|
15
|
+
* Load capabilities for a skill and warn about forbidden packs.
|
|
16
|
+
* Called before queuing a skill-driven agent message.
|
|
17
|
+
*/
|
|
18
|
+
function loadSkillCapabilities(skillName, ctx) {
|
|
19
|
+
const result = loadCapabilitiesForSkill(skillName);
|
|
20
|
+
if (!result)
|
|
21
|
+
return; // No capability loading active or no requirements
|
|
22
|
+
if (result.forbidden.length > 0) {
|
|
23
|
+
const suggestions = result.forbidden
|
|
24
|
+
.map((f) => `${f.packId}${f.suggestedAgent ? ` (try ${f.suggestedAgent})` : ''}`)
|
|
25
|
+
.join(', ');
|
|
26
|
+
ctx.ui.print({
|
|
27
|
+
type: 'warning',
|
|
28
|
+
message: `Some capabilities are restricted: ${suggestions}`,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
13
32
|
// =============================================================================
|
|
14
33
|
// Helper Functions
|
|
15
34
|
// =============================================================================
|
|
@@ -335,6 +354,8 @@ export const designCommand = {
|
|
|
335
354
|
break;
|
|
336
355
|
}
|
|
337
356
|
}
|
|
357
|
+
// Load capabilities for the design skill
|
|
358
|
+
loadSkillCapabilities('design', ctx);
|
|
338
359
|
// Get design skill prompt
|
|
339
360
|
const designPrompt = getSkillPrompt('design');
|
|
340
361
|
if (!designPrompt) {
|
|
@@ -384,6 +405,8 @@ export const sketchCommand = {
|
|
|
384
405
|
ctx.ui.print({ type: 'error', message: 'No project selected. Run /new or /projects first.' });
|
|
385
406
|
return Promise.resolve(true);
|
|
386
407
|
}
|
|
408
|
+
// Load capabilities for the sketch skill
|
|
409
|
+
loadSkillCapabilities('sketch', ctx);
|
|
387
410
|
// Get sketch skill prompt
|
|
388
411
|
const sketchPrompt = getSkillPrompt('sketch');
|
|
389
412
|
if (!sketchPrompt) {
|
|
@@ -474,8 +497,9 @@ export const refineCommand = {
|
|
|
474
497
|
}
|
|
475
498
|
}
|
|
476
499
|
}
|
|
477
|
-
// Get appropriate skill prompt
|
|
500
|
+
// Get appropriate skill prompt and load capabilities
|
|
478
501
|
const skillName = isFocusedRefine ? 'refine-item' : 'refine';
|
|
502
|
+
loadSkillCapabilities(skillName, ctx);
|
|
479
503
|
const refinePrompt = getSkillPrompt(skillName);
|
|
480
504
|
if (!refinePrompt) {
|
|
481
505
|
ctx.ui.print({ type: 'error', message: `${skillName} skill not found.` });
|
|
@@ -605,7 +629,8 @@ export const buildCommand = {
|
|
|
605
629
|
}
|
|
606
630
|
}
|
|
607
631
|
}
|
|
608
|
-
//
|
|
632
|
+
// Load capabilities and get build skill prompt
|
|
633
|
+
loadSkillCapabilities('build', ctx);
|
|
609
634
|
let buildPrompt = getSkillPrompt('build');
|
|
610
635
|
if (!buildPrompt) {
|
|
611
636
|
ctx.ui.print({ type: 'error', message: 'Build skill not found.' });
|
|
@@ -683,7 +708,8 @@ export const noteCommand = {
|
|
|
683
708
|
ctx.ui.print({ type: 'error', message: 'No project selected. Run /new or /projects first.' });
|
|
684
709
|
return Promise.resolve(true);
|
|
685
710
|
}
|
|
686
|
-
//
|
|
711
|
+
// Load capabilities and get session-notes skill prompt
|
|
712
|
+
loadSkillCapabilities('session-notes', ctx);
|
|
687
713
|
const notePrompt = getSkillPrompt('session-notes');
|
|
688
714
|
if (!notePrompt) {
|
|
689
715
|
ctx.ui.print({ type: 'error', message: 'Session-notes skill not found.' });
|
|
@@ -741,7 +767,8 @@ export const prdCommand = {
|
|
|
741
767
|
ctx.ui.print({ type: 'error', message: 'No project selected. Run /new or /projects first.' });
|
|
742
768
|
return Promise.resolve(true);
|
|
743
769
|
}
|
|
744
|
-
//
|
|
770
|
+
// Load capabilities and get PRD skill prompt
|
|
771
|
+
loadSkillCapabilities('prd', ctx);
|
|
745
772
|
const prdPrompt = getSkillPrompt('prd');
|
|
746
773
|
if (!prdPrompt) {
|
|
747
774
|
ctx.ui.print({ type: 'error', message: 'PRD skill not found.' });
|
|
@@ -866,6 +893,8 @@ export const scaffoldCommand = {
|
|
|
866
893
|
else {
|
|
867
894
|
skillName = 'scaffold';
|
|
868
895
|
}
|
|
896
|
+
// Load capability packs for this skill
|
|
897
|
+
loadSkillCapabilities(skillName, ctx);
|
|
869
898
|
// Get skill prompt and replace path placeholder
|
|
870
899
|
let scaffoldPrompt = getSkillPrompt(skillName);
|
|
871
900
|
if (!scaffoldPrompt) {
|
|
@@ -946,7 +975,8 @@ export const archCommand = {
|
|
|
946
975
|
// User cancelled
|
|
947
976
|
return true;
|
|
948
977
|
}
|
|
949
|
-
//
|
|
978
|
+
// Load capabilities and get architecture skill prompt
|
|
979
|
+
loadSkillCapabilities('architecture', ctx);
|
|
950
980
|
const archPrompt = getSkillPrompt('architecture');
|
|
951
981
|
if (!archPrompt) {
|
|
952
982
|
ctx.ui.print({ type: 'error', message: 'Architecture skill not found.' });
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Commands for configuration: theme, config, verbose, model, tools, agents
|
|
5
5
|
*/
|
|
6
6
|
import { ThemeOverlayV2, ModelOverlayV2, ToolsOverlayV2, KeysOverlayV2, MascotOverlayV2, AgentsOverlayV2, ConfigOverlayV2, PermissionsOverlayV2 } from '../../ui/overlay/index.js';
|
|
7
|
-
import { getSetting, getSettings, recordUpdateCheck, permissionModeToAgentMode } from '../../settings/index.js';
|
|
7
|
+
import { getSetting, getSettings, setSetting, recordUpdateCheck, permissionModeToAgentMode, getVerbosity } from '../../settings/index.js';
|
|
8
8
|
import { getToolPermissionInfo } from '../../agent.js';
|
|
9
9
|
import { checkForUpdates, performUpdate } from '../../utils/update-checker.js';
|
|
10
10
|
import { getRegisteredMetaTools, TOOL_NAMES, getToolStats } from '../../tools.js';
|
|
@@ -84,6 +84,9 @@ export const configCommand = {
|
|
|
84
84
|
});
|
|
85
85
|
const result = await ctx.ui.showOverlay(configOverlay);
|
|
86
86
|
if (result?.settingsChanged) {
|
|
87
|
+
// Sync TerminalUI config with persisted settings
|
|
88
|
+
const updated = getSettings();
|
|
89
|
+
ctx.ui.setConfig({ verbosity: updated.verbosity });
|
|
87
90
|
ctx.ui.print({ type: 'info', message: 'Settings updated' });
|
|
88
91
|
}
|
|
89
92
|
return true;
|
|
@@ -94,16 +97,19 @@ export const configCommand = {
|
|
|
94
97
|
// =============================================================================
|
|
95
98
|
export const verboseCommand = {
|
|
96
99
|
name: 'verbose',
|
|
97
|
-
description: '
|
|
98
|
-
details: '
|
|
100
|
+
description: 'Cycle verbosity level',
|
|
101
|
+
details: 'Cycles verbosity through Normal → Focused → Verbose. Normal shows compact output. Focused hides read-only tools (spinner only). Verbose shows thinking and full output.',
|
|
99
102
|
examples: [{ code: '/verbose' }],
|
|
100
103
|
execute(_args, ctx) {
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
+
const current = getVerbosity();
|
|
105
|
+
const cycle = { normal: 'focused', focused: 'verbose', verbose: 'normal' };
|
|
106
|
+
const next = cycle[current];
|
|
107
|
+
setSetting('verbosity', next);
|
|
108
|
+
ctx.ui.setConfig({ verbosity: next });
|
|
109
|
+
const labels = { normal: 'Normal', focused: 'Focused', verbose: 'Verbose' };
|
|
104
110
|
ctx.ui.print({
|
|
105
111
|
type: 'info',
|
|
106
|
-
message: `
|
|
112
|
+
message: `Verbosity: ${labels[next]}`,
|
|
107
113
|
});
|
|
108
114
|
return Promise.resolve(true);
|
|
109
115
|
},
|
|
Binary file
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Loader — Bridge between slash commands and the CapabilityManager.
|
|
3
|
+
*
|
|
4
|
+
* Stores a module-level reference to the current CapabilityManager
|
|
5
|
+
* (set by agent.ts during createAgent()). Provides a high-level function
|
|
6
|
+
* for slash command handlers to load capability packs required by a skill.
|
|
7
|
+
*/
|
|
8
|
+
import type { CapabilityManager, CapabilityLoadResult } from '@compilr-dev/sdk';
|
|
9
|
+
/**
|
|
10
|
+
* Set the current CapabilityManager instance.
|
|
11
|
+
* Called by createAgent() after constructing the manager.
|
|
12
|
+
*/
|
|
13
|
+
export declare function setCapabilityManager(manager: CapabilityManager | null): void;
|
|
14
|
+
/**
|
|
15
|
+
* Get the current CapabilityManager instance.
|
|
16
|
+
* Returns null if capability loading is not active.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getCapabilityManager(): CapabilityManager | null;
|
|
19
|
+
/**
|
|
20
|
+
* Load capability packs required by a skill invocation.
|
|
21
|
+
*
|
|
22
|
+
* Maps the skill's required + optional tool names to capability pack IDs,
|
|
23
|
+
* then loads each loadable pack. Returns a result indicating what was loaded,
|
|
24
|
+
* what was already loaded, and what is forbidden.
|
|
25
|
+
*
|
|
26
|
+
* @param skillName - Skill name (e.g., 'design', 'build', 'refactor')
|
|
27
|
+
* @returns Load result, or null if capability loading is not active or skill has no requirements
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadCapabilitiesForSkill(skillName: string): CapabilityLoadResult | null;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Loader — Bridge between slash commands and the CapabilityManager.
|
|
3
|
+
*
|
|
4
|
+
* Stores a module-level reference to the current CapabilityManager
|
|
5
|
+
* (set by agent.ts during createAgent()). Provides a high-level function
|
|
6
|
+
* for slash command handlers to load capability packs required by a skill.
|
|
7
|
+
*/
|
|
8
|
+
import { SKILL_REQUIREMENTS } from './skill-requirements.js';
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Module-level state
|
|
11
|
+
// =============================================================================
|
|
12
|
+
let currentManager = null;
|
|
13
|
+
/**
|
|
14
|
+
* Set the current CapabilityManager instance.
|
|
15
|
+
* Called by createAgent() after constructing the manager.
|
|
16
|
+
*/
|
|
17
|
+
export function setCapabilityManager(manager) {
|
|
18
|
+
currentManager = manager;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the current CapabilityManager instance.
|
|
22
|
+
* Returns null if capability loading is not active.
|
|
23
|
+
*/
|
|
24
|
+
export function getCapabilityManager() {
|
|
25
|
+
return currentManager;
|
|
26
|
+
}
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Skill-based capability loading
|
|
29
|
+
// =============================================================================
|
|
30
|
+
/**
|
|
31
|
+
* Load capability packs required by a skill invocation.
|
|
32
|
+
*
|
|
33
|
+
* Maps the skill's required + optional tool names to capability pack IDs,
|
|
34
|
+
* then loads each loadable pack. Returns a result indicating what was loaded,
|
|
35
|
+
* what was already loaded, and what is forbidden.
|
|
36
|
+
*
|
|
37
|
+
* @param skillName - Skill name (e.g., 'design', 'build', 'refactor')
|
|
38
|
+
* @returns Load result, or null if capability loading is not active or skill has no requirements
|
|
39
|
+
*/
|
|
40
|
+
export function loadCapabilitiesForSkill(skillName) {
|
|
41
|
+
if (!currentManager) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (!(skillName in SKILL_REQUIREMENTS)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const requirements = SKILL_REQUIREMENTS[skillName];
|
|
48
|
+
// Combine required + optional tools
|
|
49
|
+
const allTools = [
|
|
50
|
+
...requirements.required,
|
|
51
|
+
...(requirements.optional ?? []),
|
|
52
|
+
];
|
|
53
|
+
return currentManager.loadForSkill(allTools, 'slash-command');
|
|
54
|
+
}
|
package/dist/repl-v2.js
CHANGED
|
@@ -24,7 +24,8 @@ import { getCurrentProject } from './tools/project-db.js';
|
|
|
24
24
|
import { TerminalUI } from './ui/terminal-ui.js';
|
|
25
25
|
import * as terminal from './ui/terminal.js';
|
|
26
26
|
import { getStyles } from './themes/index.js';
|
|
27
|
-
import { getSettings, getStartupMode, syncPermissionModeFromUI, getPermissionMode, permissionModeToAgentMode, getProjectSessionMode, isFirstRunComplete, getSessionRetentionDays, updateSettings } from './settings/index.js';
|
|
27
|
+
import { getSettings, getStartupMode, syncPermissionModeFromUI, getPermissionMode, permissionModeToAgentMode, getProjectSessionMode, isFirstRunComplete, getSessionRetentionDays, updateSettings, getVerbosity } from './settings/index.js';
|
|
28
|
+
import { isReadOnlyTool } from './utils/readonly-tools.js';
|
|
28
29
|
import { renderMascotWithLogo } from './ui/mascot/renderer.js';
|
|
29
30
|
import { getStartupHighlights } from './changelog/index.js';
|
|
30
31
|
import { registerCommands, executeCommand, allCommands, getAutocompleteCommands, saveCurrentSession, saveCurrentTeam, loadProjectSession, archiveCurrentSession, convertMessagesToItems, } from './commands-v2/index.js';
|
|
@@ -801,6 +802,7 @@ export class ReplV2 {
|
|
|
801
802
|
const initialUiMode = permissionModeToAgentMode(savedPermissionMode);
|
|
802
803
|
this.ui = new TerminalUI({
|
|
803
804
|
initialMode: initialUiMode,
|
|
805
|
+
config: { verbosity: getVerbosity() },
|
|
804
806
|
});
|
|
805
807
|
// Print welcome screen
|
|
806
808
|
// Skip if: login overlay shows its own branded welcome,
|
|
@@ -2483,6 +2485,7 @@ export class ReplV2 {
|
|
|
2483
2485
|
params: categoryParam,
|
|
2484
2486
|
summary: `${String(count)} tools available`,
|
|
2485
2487
|
success: result.success,
|
|
2488
|
+
readOnly: isReadOnlyTool('list_tools'),
|
|
2486
2489
|
agentId: activeAgentId,
|
|
2487
2490
|
});
|
|
2488
2491
|
this.ui.setCurrentTool(null);
|
|
@@ -2517,6 +2520,7 @@ export class ReplV2 {
|
|
|
2517
2520
|
content: content || undefined,
|
|
2518
2521
|
diffLines: innerDiffLines,
|
|
2519
2522
|
success,
|
|
2523
|
+
readOnly: isReadOnlyTool(innerToolName),
|
|
2520
2524
|
agentId: activeAgentId,
|
|
2521
2525
|
});
|
|
2522
2526
|
this.ui.setCurrentTool(null);
|
|
@@ -2545,6 +2549,7 @@ export class ReplV2 {
|
|
|
2545
2549
|
params: name,
|
|
2546
2550
|
summary: `Schema: ${name}`,
|
|
2547
2551
|
success: result.success,
|
|
2552
|
+
readOnly: isReadOnlyTool('get_tool_info'),
|
|
2548
2553
|
agentId: activeAgentId,
|
|
2549
2554
|
});
|
|
2550
2555
|
this.ui.setCurrentTool(null);
|
|
@@ -2590,6 +2595,7 @@ export class ReplV2 {
|
|
|
2590
2595
|
content: content || undefined,
|
|
2591
2596
|
diffLines,
|
|
2592
2597
|
success,
|
|
2598
|
+
readOnly: isReadOnlyTool(toolName),
|
|
2593
2599
|
agentId: activeAgentId,
|
|
2594
2600
|
});
|
|
2595
2601
|
this.ui.setCurrentTool(null);
|
|
@@ -3154,6 +3160,7 @@ export class ReplV2 {
|
|
|
3154
3160
|
params: '**/*.ts',
|
|
3155
3161
|
summary: 'Found 42 files',
|
|
3156
3162
|
content: 'src/index.ts\nsrc/config.ts\nsrc/utils/helpers.ts\n... and 39 more',
|
|
3163
|
+
readOnly: true,
|
|
3157
3164
|
});
|
|
3158
3165
|
this.ui.setTodos([
|
|
3159
3166
|
{ content: 'Analyze the request', status: 'completed' },
|
package/dist/settings/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type ProjectStartupMode = 'last' | 'off';
|
|
|
13
13
|
export type MascotSetting = 'none' | 'random' | 'neutral' | 'thinking' | 'looking_left' | 'looking_right' | 'sleeping' | 'alert' | 'error' | 'success' | 'success_minimal' | 'searching' | 'skeptical';
|
|
14
14
|
export type ProjectSessionMode = 'auto' | 'ask' | 'fresh';
|
|
15
15
|
export type CompactMode = 'active' | 'all' | 'auto';
|
|
16
|
+
export type Verbosity = 'normal' | 'focused' | 'verbose';
|
|
16
17
|
/**
|
|
17
18
|
* Permission rule for a tool or pattern.
|
|
18
19
|
* Supports wildcards: git_* matches git_commit, git_branch, etc.
|
|
@@ -34,6 +35,7 @@ export interface Settings {
|
|
|
34
35
|
showTips: boolean;
|
|
35
36
|
reviseCode: boolean;
|
|
36
37
|
verbose: boolean;
|
|
38
|
+
verbosity: Verbosity;
|
|
37
39
|
progressBar: boolean;
|
|
38
40
|
checkUpdates: boolean;
|
|
39
41
|
lastUpdateCheck: number | null;
|
|
@@ -82,6 +84,7 @@ export declare const getDefaultModel: () => string | null;
|
|
|
82
84
|
export declare const isAutoCompactEnabled: () => boolean;
|
|
83
85
|
export declare const isShowTipsEnabled: () => boolean;
|
|
84
86
|
export declare const isReviseCodeEnabled: () => boolean;
|
|
87
|
+
export declare const getVerbosity: () => Verbosity;
|
|
85
88
|
export declare const isVerboseEnabled: () => boolean;
|
|
86
89
|
export declare const isProgressBarEnabled: () => boolean;
|
|
87
90
|
export declare const isTelemetryEnabled: () => boolean;
|
|
@@ -166,6 +169,14 @@ export declare function sessionRetentionToDisplay(days: number): string;
|
|
|
166
169
|
* Map display value to session retention days
|
|
167
170
|
*/
|
|
168
171
|
export declare function displayToSessionRetention(display: string): number;
|
|
172
|
+
/**
|
|
173
|
+
* Map internal verbosity to display value
|
|
174
|
+
*/
|
|
175
|
+
export declare function verbosityToDisplay(v: Verbosity): string;
|
|
176
|
+
/**
|
|
177
|
+
* Map display value to internal verbosity
|
|
178
|
+
*/
|
|
179
|
+
export declare function displayToVerbosity(d: string): Verbosity;
|
|
169
180
|
/**
|
|
170
181
|
* Map internal compact mode to display value
|
|
171
182
|
*/
|