@pasko70/pibo 1.4.5 → 1.5.0
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/apps/chat/agent-profiles.js +4 -1
- package/dist/apps/chat/agent-store.js +196 -3
- package/dist/apps/chat/chat-settings-routes.js +24 -1
- package/dist/apps/chat/data/project-service.js +13 -3
- package/dist/apps/chat/telemetry-retention-service.js +69 -0
- package/dist/apps/chat/web-app.js +23 -12
- package/dist/apps/chat-ui/assets/{dist-oLAGkW6G.js → dist-B9sopUkn.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BtF63vik.js → dist-BDQhMN_4.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-85Cc5Hut.js → dist-BEStK5um.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D1Laodgo.js → dist-BiBY_4CK.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-eG89IJAV.js → dist-C2OyzisT.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DE8W5WKg.js → dist-C9stINOY.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-ecTM1pdv.js → dist-CQKLsKIo.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-B8jspmzT.js → dist-CtSZyFkJ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DcME5mJj.js → dist-DPLEwPsG.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CQXtvBTs.js → dist-DRTq4wrN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BGqK-7Ep.js → dist-F2W_jRom.js} +1 -1
- package/dist/apps/chat-ui/assets/index-D_60RTKn.css +1 -0
- package/dist/apps/chat-ui/assets/index-iaNLOwJ-.js +157 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/{index-CRUSv6iR.js → index-lA76A7Pc.js} +4 -4
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/inkMarkdown.js +8 -3
- package/dist/apps/cli-ui/inkSyntaxHighlighter.js +166 -0
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/{pibo-vscode-ext-1.4.5.vsix → pibo-vscode-ext-1.5.0.vsix} +0 -0
- package/dist/cli.js +20 -0
- package/dist/core/runtime-telemetry.js +11 -3
- package/dist/core/session-router.js +7 -1
- package/dist/core/telemetry-retention-settings.js +34 -0
- package/dist/core/user-settings.js +11 -0
- package/dist/data/telemetry.js +11 -0
- package/dist/mcp/agent-context.js +10 -6
- package/dist/mcp/commands/info.js +19 -7
- package/dist/mcp/config.js +98 -65
- package/dist/plugins/builtin.js +15 -1
- package/dist/session-ui/terminalRows.js +56 -2
- package/dist/shared/trace-nodes.js +12 -0
- package/dist/skills/cli.js +25 -1
- package/dist/tools/guides.js +71 -0
- package/dist/tools/index.js +7 -3
- package/dist/tools/python-runtime.js +2 -2
- package/dist/tools/registry.js +25 -1
- package/package.json +93 -93
- package/skills/builtin/graphify/SKILL.md +52 -0
- package/dist/apps/chat-ui/assets/index-B-qaya1G.css +0 -1
- package/dist/apps/chat-ui/assets/index-D4uifikB.js +0 -165
package/dist/mcp/config.js
CHANGED
|
@@ -311,6 +311,17 @@ export function getDefaultConfigPaths() {
|
|
|
311
311
|
paths.push(join(home, '.config', 'mcp', 'mcp_servers.json'));
|
|
312
312
|
return paths;
|
|
313
313
|
}
|
|
314
|
+
export function getConfigSearchPaths(explicitPath) {
|
|
315
|
+
const paths = [];
|
|
316
|
+
if (explicitPath) {
|
|
317
|
+
paths.push(resolve(explicitPath));
|
|
318
|
+
}
|
|
319
|
+
if (process.env.MCP_CONFIG_PATH) {
|
|
320
|
+
paths.push(resolve(process.env.MCP_CONFIG_PATH));
|
|
321
|
+
}
|
|
322
|
+
paths.push(...getDefaultConfigPaths());
|
|
323
|
+
return [...new Set(paths)];
|
|
324
|
+
}
|
|
314
325
|
export function getPreferredConfigPath(explicitPath) {
|
|
315
326
|
if (explicitPath) {
|
|
316
327
|
return resolve(explicitPath);
|
|
@@ -341,38 +352,7 @@ export async function ensureConfigExists(explicitPath) {
|
|
|
341
352
|
await writeFile(configPath, `${JSON.stringify({ mcpServers: {} }, null, 2)}\n`);
|
|
342
353
|
return configPath;
|
|
343
354
|
}
|
|
344
|
-
|
|
345
|
-
* Load and parse MCP servers configuration
|
|
346
|
-
*/
|
|
347
|
-
export async function loadConfig(explicitPath) {
|
|
348
|
-
let configPath;
|
|
349
|
-
// Check explicit path from argument or environment
|
|
350
|
-
if (explicitPath) {
|
|
351
|
-
configPath = resolve(explicitPath);
|
|
352
|
-
}
|
|
353
|
-
else if (process.env.MCP_CONFIG_PATH) {
|
|
354
|
-
configPath = resolve(process.env.MCP_CONFIG_PATH);
|
|
355
|
-
}
|
|
356
|
-
// If explicit path provided, it must exist
|
|
357
|
-
if (configPath) {
|
|
358
|
-
if (!existsSync(configPath)) {
|
|
359
|
-
throw new Error(formatCliError(configNotFoundError(configPath)));
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
// Search default paths
|
|
364
|
-
const searchPaths = getDefaultConfigPaths();
|
|
365
|
-
for (const path of searchPaths) {
|
|
366
|
-
if (existsSync(path)) {
|
|
367
|
-
configPath = path;
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (!configPath) {
|
|
372
|
-
throw new Error(formatCliError(configSearchError()));
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// Read and parse config
|
|
355
|
+
async function readRawConfig(configPath) {
|
|
376
356
|
const content = await readFile(configPath, 'utf-8');
|
|
377
357
|
let config;
|
|
378
358
|
try {
|
|
@@ -381,45 +361,98 @@ export async function loadConfig(explicitPath) {
|
|
|
381
361
|
catch (e) {
|
|
382
362
|
throw new Error(formatCliError(configInvalidJsonError(configPath, e.message)));
|
|
383
363
|
}
|
|
384
|
-
// Validate structure
|
|
385
364
|
if (!config.mcpServers || typeof config.mcpServers !== 'object') {
|
|
386
365
|
throw new Error(formatCliError(configMissingFieldError(configPath)));
|
|
387
366
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
367
|
+
return config;
|
|
368
|
+
}
|
|
369
|
+
function validateServerConfig(serverName, serverConfig) {
|
|
370
|
+
if (!serverConfig || typeof serverConfig !== 'object') {
|
|
371
|
+
throw new Error(formatCliError({
|
|
372
|
+
code: ErrorCode.CLIENT_ERROR,
|
|
373
|
+
type: 'CONFIG_INVALID_SERVER',
|
|
374
|
+
message: `Invalid server configuration for "${serverName}"`,
|
|
375
|
+
details: 'Server config must be an object',
|
|
376
|
+
suggestion: `Use { "command": "..." } for stdio or { "url": "..." } for HTTP`,
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
const hasCommand = 'command' in serverConfig;
|
|
380
|
+
const hasUrl = 'url' in serverConfig;
|
|
381
|
+
if (!hasCommand && !hasUrl) {
|
|
382
|
+
throw new Error(formatCliError({
|
|
383
|
+
code: ErrorCode.CLIENT_ERROR,
|
|
384
|
+
type: 'CONFIG_INVALID_SERVER',
|
|
385
|
+
message: `Server "${serverName}" missing required field`,
|
|
386
|
+
details: `Must have either "command" (for stdio) or "url" (for HTTP)`,
|
|
387
|
+
suggestion: `Add "command": "npx ..." for local servers or "url": "https://..." for remote servers`,
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
if (hasCommand && hasUrl) {
|
|
391
|
+
throw new Error(formatCliError({
|
|
392
|
+
code: ErrorCode.CLIENT_ERROR,
|
|
393
|
+
type: 'CONFIG_INVALID_SERVER',
|
|
394
|
+
message: `Server "${serverName}" has both "command" and "url"`,
|
|
395
|
+
details: 'A server must be either stdio (command) or HTTP (url), not both',
|
|
396
|
+
suggestion: `Remove one of "command" or "url"`,
|
|
397
|
+
}));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Load and merge MCP servers configuration.
|
|
402
|
+
* More specific paths appear first and win server-name conflicts.
|
|
403
|
+
*/
|
|
404
|
+
export async function loadConfig(explicitPath) {
|
|
405
|
+
const explicitOrEnvPath = explicitPath
|
|
406
|
+
? resolve(explicitPath)
|
|
407
|
+
: process.env.MCP_CONFIG_PATH
|
|
408
|
+
? resolve(process.env.MCP_CONFIG_PATH)
|
|
409
|
+
: undefined;
|
|
410
|
+
if (explicitOrEnvPath && !existsSync(explicitOrEnvPath)) {
|
|
411
|
+
throw new Error(formatCliError(configNotFoundError(explicitOrEnvPath)));
|
|
412
|
+
}
|
|
413
|
+
const existingPaths = getConfigSearchPaths(explicitPath).filter((path) => existsSync(path));
|
|
414
|
+
if (existingPaths.length === 0) {
|
|
415
|
+
throw new Error(formatCliError(configSearchError()));
|
|
416
|
+
}
|
|
417
|
+
const merged = { mcpServers: {} };
|
|
418
|
+
for (const configPath of existingPaths) {
|
|
419
|
+
const config = await readRawConfig(configPath);
|
|
420
|
+
for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
|
|
421
|
+
if (!(serverName in merged.mcpServers)) {
|
|
422
|
+
merged.mcpServers[serverName] = serverConfig;
|
|
423
|
+
}
|
|
409
424
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
425
|
+
}
|
|
426
|
+
for (const [serverName, serverConfig] of Object.entries(merged.mcpServers)) {
|
|
427
|
+
validateServerConfig(serverName, serverConfig);
|
|
428
|
+
}
|
|
429
|
+
return substituteEnvVarsInObject(merged);
|
|
430
|
+
}
|
|
431
|
+
export async function getConfigSourceSummaries(explicitPath) {
|
|
432
|
+
const summaries = [];
|
|
433
|
+
for (const configPath of getConfigSearchPaths(explicitPath)) {
|
|
434
|
+
if (!existsSync(configPath)) {
|
|
435
|
+
summaries.push({ path: configPath, exists: false, servers: [] });
|
|
436
|
+
continue;
|
|
418
437
|
}
|
|
438
|
+
const config = await readRawConfig(configPath);
|
|
439
|
+
summaries.push({
|
|
440
|
+
path: configPath,
|
|
441
|
+
exists: true,
|
|
442
|
+
servers: Object.keys(config.mcpServers),
|
|
443
|
+
});
|
|
419
444
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
445
|
+
return summaries;
|
|
446
|
+
}
|
|
447
|
+
export function formatConfigSourceSummaries(summaries) {
|
|
448
|
+
return summaries
|
|
449
|
+
.map((summary) => {
|
|
450
|
+
const serverList = summary.exists
|
|
451
|
+
? summary.servers.join(', ') || '(none)'
|
|
452
|
+
: '(not found)';
|
|
453
|
+
return ` - ${summary.path}: ${serverList}`;
|
|
454
|
+
})
|
|
455
|
+
.join('\n');
|
|
423
456
|
}
|
|
424
457
|
/**
|
|
425
458
|
* Get a specific server config by name
|
package/dist/plugins/builtin.js
CHANGED
|
@@ -133,6 +133,11 @@ export const piboCorePlugin = definePiboPlugin({
|
|
|
133
133
|
path: builtinSkillPath("pibo-docker-system"),
|
|
134
134
|
kind: "builtin",
|
|
135
135
|
});
|
|
136
|
+
api.registerSkill({
|
|
137
|
+
name: "graphify",
|
|
138
|
+
path: builtinSkillPath("graphify"),
|
|
139
|
+
kind: "builtin",
|
|
140
|
+
});
|
|
136
141
|
api.registerSkill({
|
|
137
142
|
name: "prd",
|
|
138
143
|
path: builtinSkillPath("prd"),
|
|
@@ -245,7 +250,16 @@ export const piboCorePlugin = definePiboPlugin({
|
|
|
245
250
|
slashCommands: ["thinking"],
|
|
246
251
|
execute(context, event) {
|
|
247
252
|
const params = getThinkingParams(event);
|
|
248
|
-
|
|
253
|
+
if (!params.level)
|
|
254
|
+
return { ...context.getThinkingLevel(), action: "show_thinking_menu" };
|
|
255
|
+
const previousLevel = context.getThinkingLevel().level;
|
|
256
|
+
const result = context.setThinkingLevel(params.level);
|
|
257
|
+
return {
|
|
258
|
+
...result,
|
|
259
|
+
action: "set_thinking_level",
|
|
260
|
+
previousLevel,
|
|
261
|
+
changed: previousLevel !== result.level,
|
|
262
|
+
};
|
|
249
263
|
},
|
|
250
264
|
});
|
|
251
265
|
api.registerGatewayAction({
|
|
@@ -8,7 +8,7 @@ export function buildCompactTerminalRows(traceView, options) {
|
|
|
8
8
|
const flatNodes = flattenTraceNodes(traceView.nodes)
|
|
9
9
|
.sort((left, right) => compareTraceNodes(left.node, right.node))
|
|
10
10
|
.filter((item) => item.node.type !== "agent.turn" && (options.showThinking || item.node.type !== "model.reasoning"));
|
|
11
|
-
const candidates = flatNodes.map((item) => createRowCandidate(item.node, item.turnId));
|
|
11
|
+
const candidates = syncThinkingToolRows(flatNodes.map((item) => createRowCandidate(item.node, item.turnId)));
|
|
12
12
|
return groupRelatedToolCandidates(candidates).map((candidate) => candidate.row);
|
|
13
13
|
}
|
|
14
14
|
function flattenTraceNodes(nodes, turnId) {
|
|
@@ -311,7 +311,7 @@ function createExecutionCommandRow(node) {
|
|
|
311
311
|
return createStatusToolRow(node);
|
|
312
312
|
}
|
|
313
313
|
if (node.title === "thinking") {
|
|
314
|
-
return createThinkingToolRow(node);
|
|
314
|
+
return isThinkingLevelSetOutput(node.output) ? createThinkingLevelSetRow(node) : createThinkingToolRow(node);
|
|
315
315
|
}
|
|
316
316
|
if (node.title === "fast_mode") {
|
|
317
317
|
return createFastModeToolRow(node);
|
|
@@ -369,6 +369,33 @@ function createThinkingToolRow(node) {
|
|
|
369
369
|
expandable: false,
|
|
370
370
|
};
|
|
371
371
|
}
|
|
372
|
+
function createThinkingLevelSetRow(node) {
|
|
373
|
+
const result = isRecord(node.output) ? node.output : undefined;
|
|
374
|
+
const level = stringValue(result?.level);
|
|
375
|
+
const changed = result?.changed !== false;
|
|
376
|
+
const supported = result?.supported !== false;
|
|
377
|
+
const label = !supported
|
|
378
|
+
? "Thinking level is not supported by this model."
|
|
379
|
+
: level
|
|
380
|
+
? changed ? `Thinking level set to ${level}.` : `Thinking level is already ${level}.`
|
|
381
|
+
: "Thinking level updated.";
|
|
382
|
+
return {
|
|
383
|
+
id: node.id,
|
|
384
|
+
kind: "execution.command",
|
|
385
|
+
status: mapStatus(node.status),
|
|
386
|
+
lines: [
|
|
387
|
+
{
|
|
388
|
+
prefix: "bullet",
|
|
389
|
+
tokens: [token(label, supported ? "green" : "dim", "semibold")],
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
sourceNodeIds: [node.id],
|
|
393
|
+
input: node.input,
|
|
394
|
+
output: node.output,
|
|
395
|
+
error: node.error,
|
|
396
|
+
expandable: false,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
372
399
|
function createFastModeToolRow(node) {
|
|
373
400
|
const result = isRecord(node.output) ? node.output : undefined;
|
|
374
401
|
const mode = result?.mode === "fast" ? "fast" : result?.mode === "normal" ? "normal" : undefined;
|
|
@@ -495,6 +522,33 @@ function sessionErrorDetailLines(details) {
|
|
|
495
522
|
tokens: [token(`${label}: `, "dim"), token(value, "red")],
|
|
496
523
|
}));
|
|
497
524
|
}
|
|
525
|
+
function syncThinkingToolRows(candidates) {
|
|
526
|
+
const latest = candidates.map((candidate) => candidate.row.output).filter(isThinkingOutput).at(-1);
|
|
527
|
+
if (!latest)
|
|
528
|
+
return [...candidates];
|
|
529
|
+
return candidates.map((candidate) => {
|
|
530
|
+
if (candidate.row.kind !== "tool.thinking" || !isRecord(candidate.row.output))
|
|
531
|
+
return candidate;
|
|
532
|
+
return {
|
|
533
|
+
...candidate,
|
|
534
|
+
row: {
|
|
535
|
+
...candidate.row,
|
|
536
|
+
output: {
|
|
537
|
+
...candidate.row.output,
|
|
538
|
+
level: latest.level,
|
|
539
|
+
availableLevels: latest.availableLevels,
|
|
540
|
+
supported: latest.supported,
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
};
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
function isThinkingOutput(value) {
|
|
547
|
+
return isRecord(value) && typeof value.level === "string" && Array.isArray(value.availableLevels);
|
|
548
|
+
}
|
|
549
|
+
function isThinkingLevelSetOutput(value) {
|
|
550
|
+
return isRecord(value) && value.action === "set_thinking_level";
|
|
551
|
+
}
|
|
498
552
|
function groupRelatedToolCandidates(candidates) {
|
|
499
553
|
const grouped = [];
|
|
500
554
|
for (let index = 0; index < candidates.length; index += 1) {
|
|
@@ -19,6 +19,9 @@ function areTraceNodesSorted(nodes) {
|
|
|
19
19
|
return true;
|
|
20
20
|
}
|
|
21
21
|
export function compareTraceNodes(left, right) {
|
|
22
|
+
const bySameTurnPhase = compareSameTurnPhase(left, right);
|
|
23
|
+
if (bySameTurnPhase !== 0)
|
|
24
|
+
return bySameTurnPhase;
|
|
22
25
|
const byStartTime = compareOptionalIsoTime(left.startedAt, right.startedAt);
|
|
23
26
|
if (byStartTime !== 0)
|
|
24
27
|
return byStartTime;
|
|
@@ -27,6 +30,15 @@ export function compareTraceNodes(left, right) {
|
|
|
27
30
|
return byOrder;
|
|
28
31
|
return left.id.localeCompare(right.id);
|
|
29
32
|
}
|
|
33
|
+
function compareSameTurnPhase(left, right) {
|
|
34
|
+
if (!left.eventId || left.eventId !== right.eventId)
|
|
35
|
+
return 0;
|
|
36
|
+
const leftPhase = left.orderKey?.phaseRank;
|
|
37
|
+
const rightPhase = right.orderKey?.phaseRank;
|
|
38
|
+
if (leftPhase === undefined || rightPhase === undefined)
|
|
39
|
+
return 0;
|
|
40
|
+
return leftPhase - rightPhase;
|
|
41
|
+
}
|
|
30
42
|
function compareOptionalIsoTime(left, right) {
|
|
31
43
|
if (!left && !right)
|
|
32
44
|
return 0;
|
package/dist/skills/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import { createDefaultPiboPluginRegistry } from "../plugins/builtin.js";
|
|
2
3
|
import { UserSkillManager } from "../user-skills/manager.js";
|
|
3
4
|
import { readFileSync } from "node:fs";
|
|
4
5
|
import { resolve } from "node:path";
|
|
@@ -9,7 +10,30 @@ function printJson(value) {
|
|
|
9
10
|
export async function runSkillsCli(argv) {
|
|
10
11
|
const manager = new UserSkillManager(os.homedir());
|
|
11
12
|
const program = new Command();
|
|
12
|
-
program
|
|
13
|
+
program
|
|
14
|
+
.name("pibo skills")
|
|
15
|
+
.description("Manage Pibo user skills and inspect the built-in/plugin skill catalog")
|
|
16
|
+
.addHelpText("after", "\nBuilt-in/plugin skills are selected by agent profiles. Run `pibo skills catalog` to list them.\n");
|
|
17
|
+
program
|
|
18
|
+
.command("catalog")
|
|
19
|
+
.description("List built-in and plugin skills available to profiles")
|
|
20
|
+
.option("--json", "Print JSON")
|
|
21
|
+
.action((options) => {
|
|
22
|
+
const registry = createDefaultPiboPluginRegistry();
|
|
23
|
+
const skills = registry.getCapabilityCatalog().skills.filter((skill) => skill.kind !== "user");
|
|
24
|
+
if (options.json) {
|
|
25
|
+
printJson(skills);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (skills.length === 0) {
|
|
29
|
+
console.log("No built-in or plugin skills registered.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.log("NAME\tKIND\tPATH");
|
|
33
|
+
for (const skill of skills) {
|
|
34
|
+
console.log(`${skill.name}\t${skill.kind ?? "plugin"}\t${skill.path}`);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
13
37
|
program
|
|
14
38
|
.command("list")
|
|
15
39
|
.description("List user skills managed by this CLI")
|
package/dist/tools/guides.js
CHANGED
|
@@ -379,6 +379,77 @@ If \`browser-use --connect\` cannot find Chrome, ask the user whether they want
|
|
|
379
379
|
14. If Chrome fails to start with a "SingletonLock" error, the wrapper auto-detects and terminates stale Chrome processes holding the lock. Retry your command.
|
|
380
380
|
`,
|
|
381
381
|
};
|
|
382
|
+
export const GRAPHIFY_GUIDE = {
|
|
383
|
+
name: 'graphify',
|
|
384
|
+
description: 'Generate codebase knowledge graphs with the Graphify CLI.',
|
|
385
|
+
content: `---
|
|
386
|
+
name: graphify
|
|
387
|
+
description: Generates interactive codebase knowledge graphs and markdown reports from a workspace folder.
|
|
388
|
+
allowed-tools: Bash(graphify:*)
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
# Codebase Visualization with Graphify
|
|
392
|
+
|
|
393
|
+
Graphify turns a folder into derived artifacts under \`graphify-out/\`:
|
|
394
|
+
|
|
395
|
+
- \`graphify-out/graph.html\` — an interactive clickable graph;
|
|
396
|
+
- \`graphify-out/graph.json\` — machine-readable graph data;
|
|
397
|
+
- \`graphify-out/GRAPH_REPORT.md\` — a markdown summary with key concepts and suggested questions.
|
|
398
|
+
|
|
399
|
+
Use it when a user asks to graph, map, visualize, or quickly understand a repo/workspace shape.
|
|
400
|
+
|
|
401
|
+
## Prerequisites
|
|
402
|
+
|
|
403
|
+
Install and apply the Pibo tool environment:
|
|
404
|
+
|
|
405
|
+
\`\`\`bash
|
|
406
|
+
pibo tools install graphify
|
|
407
|
+
eval "$(pibo tools env graphify)"
|
|
408
|
+
graphify --help
|
|
409
|
+
\`\`\`
|
|
410
|
+
|
|
411
|
+
Inside the Pibo source repo, use \`npm run --silent dev -- tools ...\` while testing local changes:
|
|
412
|
+
|
|
413
|
+
\`\`\`bash
|
|
414
|
+
npm run --silent dev -- tools install graphify
|
|
415
|
+
eval "$(npm run --silent dev -- tools env graphify)"
|
|
416
|
+
\`\`\`
|
|
417
|
+
|
|
418
|
+
The installer uses the PyPI package \`graphifyy\` and runs \`graphify install --platform pi\` so the CLI is ready for Pi/Pibo workflows.
|
|
419
|
+
|
|
420
|
+
## Core Workflow
|
|
421
|
+
|
|
422
|
+
1. Choose the workspace path. Prefer the active Pibo Room or session workspace boundary when known.
|
|
423
|
+
2. Run Graphify from that folder or pass the target path explicitly. With \`graphifyy==0.9.x\`, \`graphify .\` writes extraction output under \`graphify-out/\`.
|
|
424
|
+
3. For a code-only workspace without an LLM key, run \`graphify cluster-only . --no-label\` after extraction to produce the HTML graph and markdown report.
|
|
425
|
+
4. Put generated artifacts in an ignored room/session artifact directory when possible; do not commit them unless the user explicitly asks.
|
|
426
|
+
5. Read \`graphify-out/GRAPH_REPORT.md\` first, then open \`graphify-out/graph.html\` when an interactive view is useful.
|
|
427
|
+
|
|
428
|
+
\`\`\`bash
|
|
429
|
+
cd /path/to/workspace
|
|
430
|
+
graphify .
|
|
431
|
+
graphify cluster-only . --no-label
|
|
432
|
+
ls graphify-out/graph.html graphify-out/graph.json graphify-out/GRAPH_REPORT.md
|
|
433
|
+
\`\`\`
|
|
434
|
+
|
|
435
|
+
## Pibo Usage Notes
|
|
436
|
+
|
|
437
|
+
- For Room-bound work, graph the Room workspace rather than the agent harness checkout unless the user asks otherwise.
|
|
438
|
+
- If generating inside a Git repo, check \`git status --short\` before and after so graph artifacts are not accidentally included in unrelated commits.
|
|
439
|
+
- Code-only extraction can run without an LLM API key. Including docs, README files, papers, images, or semantic-labeling steps may require a configured Graphify backend/API key; if no key is available, graph a code-only subdirectory or skip semantic labels with \`cluster-only . --no-label\`.
|
|
440
|
+
- For large monorepos, start with a subdirectory such as \`src/\`, a code package, or a docs folder only when the needed LLM backend is configured.
|
|
441
|
+
- Treat graph output as derived data. Recompute on demand when the branch or workspace changes.
|
|
442
|
+
|
|
443
|
+
## Next Commands
|
|
444
|
+
|
|
445
|
+
\`\`\`bash
|
|
446
|
+
pibo tools show graphify
|
|
447
|
+
pibo tools guide graphify graphify
|
|
448
|
+
pibo tools path graphify
|
|
449
|
+
pibo tools doctor graphify
|
|
450
|
+
\`\`\`
|
|
451
|
+
`,
|
|
452
|
+
};
|
|
382
453
|
export const REMOTE_BROWSER_GUIDE = {
|
|
383
454
|
name: 'remote-browser',
|
|
384
455
|
description: 'Browser automation workflow for sandboxed or remote agents.',
|
package/dist/tools/index.js
CHANGED
|
@@ -166,9 +166,13 @@ function printEnv(name) {
|
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
168
|
const binDir = status.executablePath.replace(/\/[^/]+$/, '');
|
|
169
|
-
const wrapperPath = entry.name === '
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
const wrapperPath = entry.name === 'browser-use'
|
|
170
|
+
? ensureBrowserUseWrapper(status)
|
|
171
|
+
: entry.name === 'agent-browser'
|
|
172
|
+
? ensureAgentBrowserWrapper(status)
|
|
173
|
+
: undefined;
|
|
174
|
+
const envBinDirs = wrapperPath ? `${wrapperPath.replace(/\/[^/]+$/, '')}:${binDir}` : binDir;
|
|
175
|
+
console.log(`export PATH="${envBinDirs}:$PATH"`);
|
|
172
176
|
if (entry.runtime.homeEnvVar)
|
|
173
177
|
console.log(`export ${entry.runtime.homeEnvVar}="${status.homeDir}"`);
|
|
174
178
|
if (desktop.display)
|
|
@@ -123,8 +123,8 @@ export async function printToolPythonRuntimeDoctor(name, spec) {
|
|
|
123
123
|
if (name === 'browser-use' && process.platform === 'linux' && !hasDesktopDisplay(detectDesktopEnv())) {
|
|
124
124
|
printLinuxVirtualDisplayHint(' ');
|
|
125
125
|
}
|
|
126
|
-
if (existsSync(paths.executablePath)) {
|
|
127
|
-
const doctor = await runBuffered(paths.executablePath,
|
|
126
|
+
if (existsSync(paths.executablePath) && spec.doctorArgs?.length) {
|
|
127
|
+
const doctor = await runBuffered(paths.executablePath, spec.doctorArgs, getToolPythonRuntimeEnv(paths, spec));
|
|
128
128
|
console.log(` tool doctor: ${doctor.ok ? 'ok' : 'failed'}`);
|
|
129
129
|
if (doctor.output) {
|
|
130
130
|
console.log(doctor.output.split('\n').map((line) => ` ${line}`).join('\n'));
|
package/dist/tools/registry.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { ensureAgentBrowserWrapper } from './agent-browser-wrapper.js';
|
|
3
3
|
import { detectDesktopEnv, hasDesktopDisplay, printDesktopEnvStatus, printLinuxVirtualDisplayHint } from './desktop-env.js';
|
|
4
|
-
import { AGENT_BROWSER_GUIDE, BROWSER_USE_GUIDE, RALPH_GUIDE, REMOTE_BROWSER_GUIDE } from './guides.js';
|
|
4
|
+
import { AGENT_BROWSER_GUIDE, BROWSER_USE_GUIDE, GRAPHIFY_GUIDE, RALPH_GUIDE, REMOTE_BROWSER_GUIDE } from './guides.js';
|
|
5
5
|
import { ensureLinuxVirtualDisplay } from './linux-virtual-display.js';
|
|
6
6
|
import { getToolNpmRuntimePaths, installToolNpmRuntime, printToolNpmRuntimeDoctor, removeToolNpmRuntime, } from './npm-runtime.js';
|
|
7
7
|
import { getToolPythonRuntimePaths, installToolPythonRuntime, runInheritedCommand, printToolPythonRuntimeDoctor, removeToolPythonRuntime, } from './python-runtime.js';
|
|
@@ -16,6 +16,7 @@ const REGISTRY = [
|
|
|
16
16
|
executableName: 'browser-use',
|
|
17
17
|
pythonVersion: '3.12',
|
|
18
18
|
homeEnvVar: 'BROWSER_USE_HOME',
|
|
19
|
+
doctorArgs: ['doctor'],
|
|
19
20
|
},
|
|
20
21
|
guides: [BROWSER_USE_GUIDE, REMOTE_BROWSER_GUIDE],
|
|
21
22
|
notes: [
|
|
@@ -59,6 +60,29 @@ const REGISTRY = [
|
|
|
59
60
|
'Discover details with `npm run dev -- tools show agent-browser` and `npm run dev -- tools guide agent-browser agent-browser`.',
|
|
60
61
|
].join('\n'),
|
|
61
62
|
},
|
|
63
|
+
{
|
|
64
|
+
name: 'graphify',
|
|
65
|
+
description: 'Codebase visualization CLI that generates interactive knowledge graphs and reports.',
|
|
66
|
+
runtime: {
|
|
67
|
+
packageName: 'graphifyy',
|
|
68
|
+
executableName: 'graphify',
|
|
69
|
+
pythonVersion: '3.12',
|
|
70
|
+
postInstallArgs: ['install', '--platform', 'pi'],
|
|
71
|
+
},
|
|
72
|
+
guides: [GRAPHIFY_GUIDE],
|
|
73
|
+
notes: [
|
|
74
|
+
'Installed on demand into an isolated Python virtual environment from the graphifyy package.',
|
|
75
|
+
'The installer runs graphify install --platform pi after package installation so Graphify is ready for Pi/Pibo workflows.',
|
|
76
|
+
'Graphify writes generated graph artifacts under graphify-out/; prefer an ignored room/session artifact directory unless the user explicitly wants repo files.',
|
|
77
|
+
'Guides are available through pibo tools guide and are not loaded into pibo profiles automatically.',
|
|
78
|
+
],
|
|
79
|
+
agentContextSnippet: [
|
|
80
|
+
'Codebase visualization CLI for graphify-out/graph.html, graph.json, and GRAPH_REPORT.md.',
|
|
81
|
+
'Start with `eval "$(npm run --silent dev -- tools env graphify)"`.',
|
|
82
|
+
'No LLM key: run `graphify .` on code, then `graphify cluster-only . --no-label`.',
|
|
83
|
+
'Guide: `npm run dev -- tools guide graphify graphify`.',
|
|
84
|
+
].join('\n'),
|
|
85
|
+
},
|
|
62
86
|
{
|
|
63
87
|
name: 'ralph',
|
|
64
88
|
description: 'Pibo-native continuous agent job runner for implementation and debugging loops.',
|