@compilr-dev/cli 0.5.5 → 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.
@@ -73,6 +73,9 @@ export function formatToolResult(toolName, result) {
73
73
  else if (toolLower === 'find_project_root') {
74
74
  ({ content, summary } = formatFindProjectRoot(innerResult));
75
75
  }
76
+ else if (toolLower === 'workitem_status_counts') {
77
+ ({ content, summary } = formatWorkitemStatusCounts(innerResult));
78
+ }
76
79
  else if (toolLower === 'backlog_write' || toolLower.startsWith('workitem_')) {
77
80
  ({ content, summary } = formatBacklogWrite(innerResult));
78
81
  }
@@ -193,6 +196,54 @@ export function formatToolResult(toolName, result) {
193
196
  else if (toolLower === 'read_function' || toolLower === 'read_class' || toolLower === 'read_type') {
194
197
  ({ content, summary } = formatReadSymbol(innerResult));
195
198
  }
199
+ else if (toolLower === 'web_fetch') {
200
+ ({ content, summary } = formatWebFetch(innerResult));
201
+ }
202
+ else if (toolLower === 'ask_user' || toolLower === 'ask_user_simple') {
203
+ ({ content, summary } = formatAskUser(innerResult));
204
+ }
205
+ else if (toolLower === 'compilr_guide') {
206
+ ({ content, summary } = formatCompilrGuide(innerResult));
207
+ }
208
+ else if (toolLower === 'delegation_status') {
209
+ ({ content, summary } = formatDelegationStatus(innerResult));
210
+ }
211
+ else if (toolLower === 'find_symbol') {
212
+ ({ content, summary } = formatFindSymbol(innerResult));
213
+ }
214
+ else if (toolLower === 'get_imports') {
215
+ ({ content, summary } = formatGetImports(innerResult));
216
+ }
217
+ else if (toolLower === 'get_exports') {
218
+ ({ content, summary } = formatGetExports(innerResult));
219
+ }
220
+ else if (toolLower === 'get_call_graph') {
221
+ ({ content, summary } = formatGetCallGraph(innerResult));
222
+ }
223
+ else if (toolLower === 'get_dependency_graph') {
224
+ ({ content, summary } = formatGetDependencyGraph(innerResult));
225
+ }
226
+ else if (toolLower === 'find_implementations') {
227
+ ({ content, summary } = formatFindImplementations(innerResult));
228
+ }
229
+ else if (toolLower === 'get_type_hierarchy' || toolLower === 'get_class_hierarchy') {
230
+ ({ content, summary } = formatGetTypeHierarchy(innerResult));
231
+ }
232
+ else if (toolLower === 'find_dead_code') {
233
+ ({ content, summary } = formatFindDeadCode(innerResult));
234
+ }
235
+ else if (toolLower === 'find_duplicates') {
236
+ ({ content, summary } = formatFindDuplicates(innerResult));
237
+ }
238
+ else if (toolLower === 'find_patterns') {
239
+ ({ content, summary } = formatFindPatterns(innerResult));
240
+ }
241
+ else if (toolLower === 'get_signature') {
242
+ ({ content, summary } = formatGetSignature(innerResult));
243
+ }
244
+ else if (toolLower === 'get_documentation' || toolLower === 'extract_docstrings') {
245
+ ({ content, summary } = formatGetDocumentation(innerResult));
246
+ }
196
247
  else {
197
248
  // Generic handling
198
249
  ({ content, summary } = formatGeneric(innerResult));
@@ -1186,6 +1237,323 @@ function formatFactoryListToolkits(result) {
1186
1237
  });
1187
1238
  return { content: lines.join('\n'), summary };
1188
1239
  }
1240
+ // =============================================================================
1241
+ // Missing Formatters — SDK, Agents, CLI
1242
+ // =============================================================================
1243
+ function formatWorkitemStatusCounts(result) {
1244
+ const summary_str = result.summary;
1245
+ const counts = result.counts;
1246
+ const total = result.total;
1247
+ const progress = result.progress;
1248
+ if (summary_str) {
1249
+ return { content: '', summary: summary_str };
1250
+ }
1251
+ if (counts) {
1252
+ const parts = Object.entries(counts)
1253
+ .filter(([, v]) => v > 0)
1254
+ .map(([k, v]) => `${String(v)} ${k}`);
1255
+ const pct = progress !== undefined ? ` (${String(Math.round(progress))}%)` : '';
1256
+ return { content: parts.join('\n'), summary: `${String(total ?? 0)} items${pct}` };
1257
+ }
1258
+ return { content: '', summary: 'No items' };
1259
+ }
1260
+ function formatWebFetch(result) {
1261
+ const url = result.url;
1262
+ const status = result.status;
1263
+ const truncated = result.truncated;
1264
+ const content = typeof result.content === 'string' ? result.content : '';
1265
+ const urlShort = url ? url.replace(/^https?:\/\//, '').slice(0, 40) : '?';
1266
+ const statusInfo = status ? ` (${String(status)})` : '';
1267
+ const truncInfo = truncated ? ' [truncated]' : '';
1268
+ return { content, summary: `${urlShort}${statusInfo}${truncInfo}` };
1269
+ }
1270
+ function formatAskUser(result) {
1271
+ // ask_user returns { answers: Record<string, string|string[]> }
1272
+ const answers = result.answers;
1273
+ const answer = result.answer;
1274
+ if (answer) {
1275
+ return { content: '', summary: `Answer: ${answer.slice(0, 60)}` };
1276
+ }
1277
+ if (answers) {
1278
+ const entries = Object.entries(answers);
1279
+ if (entries.length === 1) {
1280
+ const val = Array.isArray(entries[0][1]) ? entries[0][1].join(', ') : entries[0][1];
1281
+ return { content: '', summary: `Answer: ${val.slice(0, 60)}` };
1282
+ }
1283
+ return { content: '', summary: `${String(entries.length)} answer(s)` };
1284
+ }
1285
+ return { content: '', summary: 'User responded' };
1286
+ }
1287
+ function formatCompilrGuide(result) {
1288
+ const output = result.output;
1289
+ const content = output || '';
1290
+ const firstLine = content.split('\n').find((l) => l.trim().length > 0) || 'Guide';
1291
+ return { content, summary: firstLine.slice(0, 60) };
1292
+ }
1293
+ function formatDelegationStatus(result) {
1294
+ const delegations = result.delegations;
1295
+ if (!delegations?.length) {
1296
+ return { content: '', summary: 'No active delegations' };
1297
+ }
1298
+ const summary = `${String(delegations.length)} delegation(s)`;
1299
+ const lines = delegations.slice(0, 5).map((d) => {
1300
+ const agent = d.targetAgent || '?';
1301
+ const status = d.status || '?';
1302
+ const dur = d.duration ? ` (${d.duration})` : '';
1303
+ const task = (d.task || '').slice(0, 40);
1304
+ return `[${status}] ${agent}: ${task}${dur}`;
1305
+ });
1306
+ if (delegations.length > 5)
1307
+ lines.push(`... and ${String(delegations.length - 5)} more`);
1308
+ return { content: lines.join('\n'), summary };
1309
+ }
1310
+ // =============================================================================
1311
+ // Code Analysis Formatters (TS/Python/Go)
1312
+ // =============================================================================
1313
+ function formatFindSymbol(result) {
1314
+ const definitions = result.definitions;
1315
+ if (!definitions?.length) {
1316
+ return { content: '', summary: 'No symbols found' };
1317
+ }
1318
+ const summary = `Found ${String(definitions.length)} symbol(s)`;
1319
+ const lines = definitions.slice(0, 8).map((d) => {
1320
+ const exp = d.exported ? ' [exported]' : '';
1321
+ const container = d.container ? ` in ${d.container}` : '';
1322
+ return `${d.path || '?'}:${String(d.line ?? 0)} [${d.kind || '?'}] ${d.name || '?'}${container}${exp}`;
1323
+ });
1324
+ if (definitions.length > 8)
1325
+ lines.push(`... and ${String(definitions.length - 8)} more`);
1326
+ return { content: lines.join('\n'), summary };
1327
+ }
1328
+ function formatGetImports(result) {
1329
+ const imports = result.imports;
1330
+ const stats = result.stats;
1331
+ if (!imports?.length) {
1332
+ return { content: '', summary: 'No imports' };
1333
+ }
1334
+ const ext = stats?.externalPackages ?? 0;
1335
+ const int = stats?.internalModules ?? 0;
1336
+ const summary = `${String(stats?.totalImports ?? imports.length)} import(s) (${String(ext)} external, ${String(int)} internal)`;
1337
+ const lines = imports.slice(0, 10).map((imp) => {
1338
+ const prefix = imp.isExternal ? '[ext]' : '[int]';
1339
+ const typeOnly = imp.typeOnly ? ' (type)' : '';
1340
+ const symbolCount = imp.symbols?.length ? ` (${String(imp.symbols.length)} symbols)` : '';
1341
+ return `${prefix} ${imp.source || '?'}${typeOnly}${symbolCount}`;
1342
+ });
1343
+ if (imports.length > 10)
1344
+ lines.push(`... and ${String(imports.length - 10)} more`);
1345
+ return { content: lines.join('\n'), summary };
1346
+ }
1347
+ function formatGetExports(result) {
1348
+ const namedExports = result.namedExports;
1349
+ const defaultExport = result.defaultExport;
1350
+ const stats = result.stats;
1351
+ const total = stats?.totalExports ?? (namedExports?.length ?? 0) + (defaultExport ? 1 : 0);
1352
+ if (total === 0) {
1353
+ return { content: '', summary: 'No exports' };
1354
+ }
1355
+ const parts = [];
1356
+ if (stats?.functions)
1357
+ parts.push(`${String(stats.functions)} fn`);
1358
+ if (stats?.classes)
1359
+ parts.push(`${String(stats.classes)} class`);
1360
+ if (stats?.types)
1361
+ parts.push(`${String(stats.types)} type`);
1362
+ if (stats?.variables)
1363
+ parts.push(`${String(stats.variables)} var`);
1364
+ const summary = `${String(total)} export(s)${parts.length ? ` (${parts.join(', ')})` : ''}`;
1365
+ const lines = [];
1366
+ if (defaultExport) {
1367
+ lines.push(`default: ${defaultExport.name || '?'} [${defaultExport.kind || '?'}]`);
1368
+ }
1369
+ if (namedExports?.length) {
1370
+ for (const exp of namedExports.slice(0, 10)) {
1371
+ const typeOnly = exp.typeOnly ? ' (type)' : '';
1372
+ lines.push(`${exp.name || '?'} [${exp.kind || '?'}]${typeOnly}`);
1373
+ }
1374
+ if (namedExports.length > 10)
1375
+ lines.push(`... and ${String(namedExports.length - 10)} more`);
1376
+ }
1377
+ return { content: lines.join('\n'), summary };
1378
+ }
1379
+ function formatGetCallGraph(result) {
1380
+ const graph = result.graph;
1381
+ const stats = result.stats;
1382
+ if (!graph?.length) {
1383
+ return { content: '', summary: 'Empty call graph' };
1384
+ }
1385
+ const fns = stats?.totalFunctions ?? graph.length;
1386
+ const calls = stats?.totalCalls ?? 0;
1387
+ const summary = `${String(fns)} function(s), ${String(calls)} call(s)`;
1388
+ const lines = graph.slice(0, 8).map((node) => {
1389
+ const fn = node.function;
1390
+ const name = fn?.className ? `${fn.className}.${fn.name || '?'}` : fn?.name || '?';
1391
+ const callCount = node.calls?.length ?? 0;
1392
+ const callNames = node.calls?.slice(0, 3).map((c) => c.function?.name || '?').join(', ') || '';
1393
+ const more = callCount > 3 ? `, +${String(callCount - 3)}` : '';
1394
+ return `${name} → ${callNames}${more}` + (callCount === 0 ? ' (leaf)' : '');
1395
+ });
1396
+ if (graph.length > 8)
1397
+ lines.push(`... and ${String(graph.length - 8)} more`);
1398
+ return { content: lines.join('\n'), summary };
1399
+ }
1400
+ function formatGetDependencyGraph(result) {
1401
+ const circularDependencies = result.circularDependencies;
1402
+ const stats = result.stats;
1403
+ if (!stats) {
1404
+ return { content: '', summary: 'Dependency graph' };
1405
+ }
1406
+ const circ = stats.circularDependencies ?? circularDependencies?.length ?? 0;
1407
+ const circLabel = circ > 0 ? `, ${String(circ)} circular` : '';
1408
+ const summary = `${String(stats.totalModules ?? 0)} modules, ${String(stats.totalEdges ?? 0)} edges${circLabel}`;
1409
+ const lines = [];
1410
+ lines.push(`Internal: ${String(stats.internalModules ?? 0)}, External: ${String(stats.externalPackages ?? 0)}`);
1411
+ if (stats.mostDependencies?.module) {
1412
+ lines.push(`Most deps: ${stats.mostDependencies.module} (${String(stats.mostDependencies.count ?? 0)})`);
1413
+ }
1414
+ if (circularDependencies?.length) {
1415
+ lines.push('Circular:');
1416
+ for (const c of circularDependencies.slice(0, 3)) {
1417
+ lines.push(` ${c.cycle?.join(' → ') || '?'}`);
1418
+ }
1419
+ if (circularDependencies.length > 3)
1420
+ lines.push(` ... and ${String(circularDependencies.length - 3)} more`);
1421
+ }
1422
+ return { content: lines.join('\n'), summary };
1423
+ }
1424
+ function formatFindImplementations(result) {
1425
+ const target = result.target;
1426
+ const implementations = result.implementations;
1427
+ if (!implementations?.length) {
1428
+ return { content: '', summary: `No implementations of ${target || '?'}` };
1429
+ }
1430
+ const summary = `Found ${String(implementations.length)} implementation(s) of ${target || '?'}`;
1431
+ const lines = implementations.slice(0, 8).map((impl) => {
1432
+ const exp = impl.exported ? ' [exported]' : '';
1433
+ const missing = impl.missingMethods?.length ? ` (${String(impl.missingMethods.length)} missing)` : '';
1434
+ return `${impl.path || '?'}:${String(impl.line ?? 0)} ${impl.name || '?'} [${impl.kind || '?'}]${exp}${missing}`;
1435
+ });
1436
+ if (implementations.length > 8)
1437
+ lines.push(`... and ${String(implementations.length - 8)} more`);
1438
+ return { content: lines.join('\n'), summary };
1439
+ }
1440
+ function formatGetTypeHierarchy(result) {
1441
+ // TS format: root + allTypes
1442
+ const root = result.root;
1443
+ const allTypes = result.allTypes;
1444
+ // Python format: className + parents + children + mro
1445
+ const className = result.className;
1446
+ const parents = result.parents;
1447
+ const children = result.children;
1448
+ const mro = result.mro;
1449
+ if (className) {
1450
+ const parentNames = parents?.map((p) => p.name || '?').join(', ') || 'none';
1451
+ const childNames = children?.map((c) => c.name || '?').join(', ') || 'none';
1452
+ const summary = `${className}: ${String(parents?.length ?? 0)} parent(s), ${String(children?.length ?? 0)} child(ren)`;
1453
+ const lines = [];
1454
+ lines.push(`Parents: ${parentNames}`);
1455
+ lines.push(`Children: ${childNames}`);
1456
+ if (mro?.length)
1457
+ lines.push(`MRO: ${mro.join(' → ')}`);
1458
+ return { content: lines.join('\n'), summary };
1459
+ }
1460
+ if (root) {
1461
+ const total = allTypes?.length ?? 0;
1462
+ const summary = `${root.name || '?'} [${root.kind || '?'}]: ${String(total)} type(s) in hierarchy`;
1463
+ const lines = [];
1464
+ if (root.parents && Array.isArray(root.parents) && root.parents.length > 0) {
1465
+ lines.push(`Parents: ${String(root.parents.length)}`);
1466
+ }
1467
+ if (root.children && Array.isArray(root.children) && root.children.length > 0) {
1468
+ lines.push(`Children: ${String(root.children.length)}`);
1469
+ }
1470
+ return { content: lines.join('\n'), summary };
1471
+ }
1472
+ return { content: '', summary: 'Type hierarchy' };
1473
+ }
1474
+ function formatFindDeadCode(result) {
1475
+ const deadCode = result.deadCode;
1476
+ if (!deadCode?.length) {
1477
+ return { content: '', summary: 'No dead code found' };
1478
+ }
1479
+ const summary = `Found ${String(deadCode.length)} unused item(s)`;
1480
+ const lines = deadCode.slice(0, 8).map((d) => {
1481
+ const conf = d.confidence ? ` (${d.confidence})` : '';
1482
+ return `${d.path || '?'}:${String(d.line ?? 0)} [${d.kind || '?'}] ${d.name || '?'}${conf}`;
1483
+ });
1484
+ if (deadCode.length > 8)
1485
+ lines.push(`... and ${String(deadCode.length - 8)} more`);
1486
+ return { content: lines.join('\n'), summary };
1487
+ }
1488
+ function formatFindDuplicates(result) {
1489
+ const duplicates = result.duplicates;
1490
+ const stats = result.stats;
1491
+ if (!duplicates?.length) {
1492
+ return { content: '', summary: 'No duplicates found' };
1493
+ }
1494
+ const pct = stats?.percentageDuplicate !== undefined ? ` (${stats.percentageDuplicate.toFixed(1)}%)` : '';
1495
+ const summary = `${String(stats?.duplicateGroups ?? duplicates.length)} duplicate group(s), ${String(stats?.totalDuplicateLines ?? 0)} lines${pct}`;
1496
+ const lines = duplicates.slice(0, 5).map((d, i) => {
1497
+ const locs = d.locations?.map((l) => `${l.path || '?'}:${String(l.startLine ?? 0)}-${String(l.endLine ?? 0)}`).join(', ') || '?';
1498
+ return `Group ${String(i + 1)} (${String(d.lines ?? 0)} lines): ${locs}`;
1499
+ });
1500
+ if (duplicates.length > 5)
1501
+ lines.push(`... and ${String(duplicates.length - 5)} more`);
1502
+ return { content: lines.join('\n'), summary };
1503
+ }
1504
+ function formatFindPatterns(result) {
1505
+ const matches = result.matches;
1506
+ const stats = result.stats;
1507
+ if (!matches?.length) {
1508
+ return { content: '', summary: 'No patterns matched' };
1509
+ }
1510
+ const sev = stats?.bySeverity;
1511
+ const sevParts = [];
1512
+ if (sev?.error)
1513
+ sevParts.push(`${String(sev.error)} error`);
1514
+ if (sev?.warning)
1515
+ sevParts.push(`${String(sev.warning)} warning`);
1516
+ if (sev?.info)
1517
+ sevParts.push(`${String(sev.info)} info`);
1518
+ const summary = `${String(stats?.totalMatches ?? matches.length)} match(es)${sevParts.length ? ` (${sevParts.join(', ')})` : ''}`;
1519
+ const lines = matches.slice(0, 8).map((m) => {
1520
+ const loc = `${m.path || '?'}:${String(m.line ?? 0)}`;
1521
+ return `[${m.severity || '?'}] ${loc} ${(m.snippet || '').slice(0, 40)}`;
1522
+ });
1523
+ if (matches.length > 8)
1524
+ lines.push(`... and ${String(matches.length - 8)} more`);
1525
+ return { content: lines.join('\n'), summary };
1526
+ }
1527
+ function formatGetSignature(result) {
1528
+ const name = result.name;
1529
+ const kind = result.kind;
1530
+ const formattedSignature = result.formattedSignature;
1531
+ const signature = result.signature;
1532
+ const exported = result.exported;
1533
+ const exp = exported ? ' [exported]' : '';
1534
+ const summary = `[${kind || 'symbol'}] ${name || '?'}${exp}`;
1535
+ const content = formattedSignature || signature || '';
1536
+ return { content, summary };
1537
+ }
1538
+ function formatGetDocumentation(result) {
1539
+ const summary_data = result.summary;
1540
+ const undocumentedExports = result.undocumentedExports;
1541
+ if (summary_data) {
1542
+ const pct = summary_data.coveragePercent !== undefined ? summary_data.coveragePercent.toFixed(0) : '?';
1543
+ const summary = `${String(summary_data.documentedSymbols ?? 0)}/${String(summary_data.totalSymbols ?? 0)} documented (${pct}%)`;
1544
+ const lines = [];
1545
+ if (undocumentedExports?.length) {
1546
+ lines.push('Undocumented:');
1547
+ for (const u of undocumentedExports.slice(0, 8)) {
1548
+ lines.push(` ${u.path || '?'}:${String(u.line ?? 0)} [${u.kind || '?'}] ${u.name || '?'}`);
1549
+ }
1550
+ if (undocumentedExports.length > 8)
1551
+ lines.push(` ... and ${String(undocumentedExports.length - 8)} more`);
1552
+ }
1553
+ return { content: lines.join('\n'), summary };
1554
+ }
1555
+ return { content: '', summary: 'Documentation analysis complete' };
1556
+ }
1189
1557
  function formatGeneric(result) {
1190
1558
  const content = typeof result.content === 'string'
1191
1559
  ? result.content
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Read-only tool classification.
3
+ *
4
+ * Uses CAPABILITY_PACKS from SDK to determine which tools are read-only,
5
+ * plus a suffix-based heuristic for tools not in any pack.
6
+ * Read-only tools are hidden in focused mode (spinner only, no scroll output).
7
+ */
8
+ export declare function isReadOnlyTool(toolName: string): boolean;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Read-only tool classification.
3
+ *
4
+ * Uses CAPABILITY_PACKS from SDK to determine which tools are read-only,
5
+ * plus a suffix-based heuristic for tools not in any pack.
6
+ * Read-only tools are hidden in focused mode (spinner only, no scroll output).
7
+ */
8
+ import { CAPABILITY_PACKS } from '@compilr-dev/sdk';
9
+ const READ_ONLY_TOOLS = new Set();
10
+ // 1. Add all tools from read-only capability packs
11
+ for (const pack of Object.values(CAPABILITY_PACKS)) {
12
+ if (pack.readOnly) {
13
+ for (const tool of pack.tools) {
14
+ READ_ONLY_TOOLS.add(tool);
15
+ }
16
+ }
17
+ }
18
+ // 2. Suffixes that indicate a read-only tool (query/inspect, no mutations)
19
+ const READ_ONLY_SUFFIXES = [
20
+ '_read', '_get', '_list', '_query', '_status', '_counts',
21
+ '_search', '_find', '_check', '_info', '_view',
22
+ ];
23
+ export function isReadOnlyTool(toolName) {
24
+ if (READ_ONLY_TOOLS.has(toolName))
25
+ return true;
26
+ // Suffix heuristic for platform/meta tools not explicitly in a read-only pack
27
+ const lower = toolName.toLowerCase();
28
+ return READ_ONLY_SUFFIXES.some(suffix => lower.endsWith(suffix));
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/cli",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "AI-powered coding assistant CLI using @compilr-dev/agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",