@bahulam/code 2.6.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/README.md +80 -0
- package/package.json +49 -0
- package/pulse/app/activity/page.tsx +190 -0
- package/pulse/app/api/activity/route.ts +138 -0
- package/pulse/app/api/benchmark/route.ts +113 -0
- package/pulse/app/api/benchmarks/route.ts +195 -0
- package/pulse/app/api/costs/route.ts +88 -0
- package/pulse/app/api/export/route.ts +77 -0
- package/pulse/app/api/history/route.ts +11 -0
- package/pulse/app/api/import/route.ts +31 -0
- package/pulse/app/api/memory/route.ts +50 -0
- package/pulse/app/api/plans/route.ts +9 -0
- package/pulse/app/api/projects/[slug]/route.ts +96 -0
- package/pulse/app/api/projects/route.ts +121 -0
- package/pulse/app/api/sessions/[id]/replay/route.ts +20 -0
- package/pulse/app/api/sessions/[id]/route.ts +31 -0
- package/pulse/app/api/sessions/route.ts +112 -0
- package/pulse/app/api/settings/route.ts +14 -0
- package/pulse/app/api/stats/route.ts +143 -0
- package/pulse/app/api/todos/route.ts +9 -0
- package/pulse/app/api/tools/route.ts +160 -0
- package/pulse/app/benchmarks/page.tsx +224 -0
- package/pulse/app/costs/page.tsx +179 -0
- package/pulse/app/export/page.tsx +465 -0
- package/pulse/app/favicon.ico +0 -0
- package/pulse/app/globals.css +263 -0
- package/pulse/app/help/page.tsx +143 -0
- package/pulse/app/history/page.tsx +157 -0
- package/pulse/app/layout.tsx +46 -0
- package/pulse/app/memory/page.tsx +365 -0
- package/pulse/app/overview-client.tsx +393 -0
- package/pulse/app/page.tsx +14 -0
- package/pulse/app/plans/page.tsx +308 -0
- package/pulse/app/projects/[slug]/page.tsx +390 -0
- package/pulse/app/projects/page.tsx +110 -0
- package/pulse/app/sessions/[id]/page.tsx +243 -0
- package/pulse/app/sessions/page.tsx +39 -0
- package/pulse/app/settings/page.tsx +188 -0
- package/pulse/app/todos/page.tsx +211 -0
- package/pulse/app/tools/page.tsx +249 -0
- package/pulse/cli.js +164 -0
- package/pulse/components/activity/day-of-week-chart.tsx +35 -0
- package/pulse/components/activity/streak-card.tsx +36 -0
- package/pulse/components/costs/cache-efficiency-panel.tsx +76 -0
- package/pulse/components/costs/cost-by-project-chart.tsx +48 -0
- package/pulse/components/costs/cost-over-time-chart.tsx +95 -0
- package/pulse/components/costs/model-token-table.tsx +60 -0
- package/pulse/components/global-search.tsx +193 -0
- package/pulse/components/keyboard-nav-provider.tsx +23 -0
- package/pulse/components/layout/bottom-nav.tsx +53 -0
- package/pulse/components/layout/client-layout.tsx +31 -0
- package/pulse/components/layout/sidebar-context.tsx +50 -0
- package/pulse/components/layout/sidebar.tsx +183 -0
- package/pulse/components/layout/top-bar.tsx +121 -0
- package/pulse/components/overview/activity-heatmap.tsx +107 -0
- package/pulse/components/overview/conversation-table.tsx +148 -0
- package/pulse/components/overview/model-breakdown-donut.tsx +95 -0
- package/pulse/components/overview/peak-hours-chart.tsx +87 -0
- package/pulse/components/overview/project-activity-donut.tsx +96 -0
- package/pulse/components/overview/stat-card.tsx +102 -0
- package/pulse/components/overview/usage-over-time-chart.tsx +166 -0
- package/pulse/components/projects/project-card.tsx +175 -0
- package/pulse/components/sessions/replay/assistant-markdown.tsx +94 -0
- package/pulse/components/sessions/replay/compaction-card.tsx +25 -0
- package/pulse/components/sessions/replay/session-sidebar.tsx +231 -0
- package/pulse/components/sessions/replay/token-accumulation-chart.tsx +98 -0
- package/pulse/components/sessions/replay/tool-call-badge.tsx +127 -0
- package/pulse/components/sessions/replay/turn-cards.tsx +220 -0
- package/pulse/components/sessions/replay/user-tool-result.tsx +158 -0
- package/pulse/components/sessions/session-badges.tsx +49 -0
- package/pulse/components/sessions/session-table.tsx +299 -0
- package/pulse/components/theme-provider.tsx +44 -0
- package/pulse/components/tools/feature-adoption-table.tsx +58 -0
- package/pulse/components/tools/mcp-server-panel.tsx +45 -0
- package/pulse/components/tools/tool-ranking-chart.tsx +57 -0
- package/pulse/components/tools/version-history-table.tsx +32 -0
- package/pulse/components/ui/alert.tsx +66 -0
- package/pulse/components/ui/badge.tsx +48 -0
- package/pulse/components/ui/breadcrumb.tsx +109 -0
- package/pulse/components/ui/button.tsx +64 -0
- package/pulse/components/ui/calendar.tsx +220 -0
- package/pulse/components/ui/card.tsx +92 -0
- package/pulse/components/ui/command.tsx +158 -0
- package/pulse/components/ui/dialog.tsx +158 -0
- package/pulse/components/ui/input.tsx +21 -0
- package/pulse/components/ui/popover.tsx +89 -0
- package/pulse/components/ui/progress.tsx +31 -0
- package/pulse/components/ui/select.tsx +190 -0
- package/pulse/components/ui/separator.tsx +28 -0
- package/pulse/components/ui/sheet.tsx +143 -0
- package/pulse/components/ui/skeleton.tsx +13 -0
- package/pulse/components/ui/table.tsx +116 -0
- package/pulse/components/ui/tabs.tsx +91 -0
- package/pulse/components/ui/tooltip.tsx +57 -0
- package/pulse/components/use-global-keyboard-nav.ts +79 -0
- package/pulse/components.json +23 -0
- package/pulse/eslint.config.mjs +18 -0
- package/pulse/lib/bahulam-paths.ts +23 -0
- package/pulse/lib/claude-reader.ts +592 -0
- package/pulse/lib/decode.ts +129 -0
- package/pulse/lib/pricing.ts +102 -0
- package/pulse/lib/replay-parser.ts +165 -0
- package/pulse/lib/tool-categories.ts +127 -0
- package/pulse/lib/utils.ts +6 -0
- package/pulse/next-env.d.ts +6 -0
- package/pulse/next.config.ts +16 -0
- package/pulse/package.json +45 -0
- package/pulse/postcss.config.mjs +7 -0
- package/pulse/public/activity.png +0 -0
- package/pulse/public/cc-lens.png +0 -0
- package/pulse/public/command-k.png +0 -0
- package/pulse/public/costs.png +0 -0
- package/pulse/public/dashboard-dark.png +0 -0
- package/pulse/public/dashboard-white.png +0 -0
- package/pulse/public/export.png +0 -0
- package/pulse/public/file.svg +1 -0
- package/pulse/public/globe.svg +1 -0
- package/pulse/public/next.svg +1 -0
- package/pulse/public/projects.png +0 -0
- package/pulse/public/session-chat.png +0 -0
- package/pulse/public/todos.png +0 -0
- package/pulse/public/tools.png +0 -0
- package/pulse/public/vercel.svg +1 -0
- package/pulse/public/window.svg +1 -0
- package/pulse/tsconfig.json +34 -0
- package/pulse/types/claude.ts +294 -0
- package/src/agents/loader.mjs +94 -0
- package/src/agents/multi_workflow_loader.mjs +330 -0
- package/src/agents/parser.mjs +205 -0
- package/src/agents/scaffold.mjs +222 -0
- package/src/agents/teams.mjs +123 -0
- package/src/agents/workflow_loader.mjs +122 -0
- package/src/agents/workflow_scaffold.mjs +249 -0
- package/src/auth/oauth.mjs +220 -0
- package/src/auth/tarang-auth.mjs +306 -0
- package/src/commands/agent.mjs +220 -0
- package/src/commands/workflow.mjs +581 -0
- package/src/config/cli-args.mjs +200 -0
- package/src/config/env.mjs +263 -0
- package/src/config/hook-runner.mjs +100 -0
- package/src/config/memory-loader.mjs +32 -0
- package/src/config/settings-loader.mjs +45 -0
- package/src/config/settings.mjs +132 -0
- package/src/context/ast-parser.mjs +298 -0
- package/src/context/bm25.mjs +85 -0
- package/src/context/retriever.mjs +308 -0
- package/src/context/skeleton.mjs +134 -0
- package/src/context/symbol-indexer.mjs +375 -0
- package/src/core/agent-history.mjs +111 -0
- package/src/core/agent-loop.mjs +486 -0
- package/src/core/approval-log.mjs +104 -0
- package/src/core/approval.mjs +476 -0
- package/src/core/attachments.mjs +380 -0
- package/src/core/backend-url.mjs +55 -0
- package/src/core/cache-control.mjs +92 -0
- package/src/core/cache.mjs +105 -0
- package/src/core/callback-client.mjs +180 -0
- package/src/core/checkpoints.mjs +142 -0
- package/src/core/compact-history.mjs +127 -0
- package/src/core/context-envelope.mjs +54 -0
- package/src/core/context-manager.mjs +198 -0
- package/src/core/error-guidance.mjs +311 -0
- package/src/core/file-diff.mjs +217 -0
- package/src/core/headless.mjs +448 -0
- package/src/core/hooks-manager.mjs +87 -0
- package/src/core/jsonl-writer.mjs +449 -0
- package/src/core/local-agent.mjs +537 -0
- package/src/core/local-store.mjs +836 -0
- package/src/core/mode-selector.mjs +51 -0
- package/src/core/output-filter.mjs +177 -0
- package/src/core/paths.mjs +190 -0
- package/src/core/policy-resolver.mjs +156 -0
- package/src/core/pricing.mjs +336 -0
- package/src/core/project-artifacts.mjs +39 -0
- package/src/core/project-context-loader.mjs +139 -0
- package/src/core/providers.mjs +219 -0
- package/src/core/rate-limit-display.mjs +121 -0
- package/src/core/rate-limiter.mjs +119 -0
- package/src/core/resume-mode.mjs +192 -0
- package/src/core/risk-tier.mjs +337 -0
- package/src/core/safety.mjs +203 -0
- package/src/core/scheduler.mjs +173 -0
- package/src/core/session-manager.mjs +360 -0
- package/src/core/session.mjs +143 -0
- package/src/core/settings-sync.mjs +85 -0
- package/src/core/stagnation.mjs +57 -0
- package/src/core/stream-client.mjs +829 -0
- package/src/core/streaming.mjs +182 -0
- package/src/core/system-prompt.mjs +140 -0
- package/src/core/tasks.mjs +196 -0
- package/src/core/tool-executor.mjs +1950 -0
- package/src/core/trust.mjs +158 -0
- package/src/core/work-scope.mjs +248 -0
- package/src/hooks/engine.mjs +162 -0
- package/src/index.mjs +426 -0
- package/src/mcp/client.mjs +253 -0
- package/src/mcp/transport-shttp.mjs +130 -0
- package/src/mcp/transport-sse.mjs +131 -0
- package/src/mcp/transport-ws.mjs +134 -0
- package/src/onboarding/preflight.mjs +360 -0
- package/src/permissions/checker.mjs +57 -0
- package/src/permissions/command-classifier.mjs +652 -0
- package/src/permissions/injection-check.mjs +60 -0
- package/src/permissions/path-check.mjs +102 -0
- package/src/permissions/prompt.mjs +73 -0
- package/src/permissions/sandbox.mjs +112 -0
- package/src/plugins/loader.mjs +138 -0
- package/src/skills/installer.mjs +188 -0
- package/src/skills/loader.mjs +252 -0
- package/src/skills/runner.mjs +55 -0
- package/src/state/orbit.mjs +263 -0
- package/src/state/verbosity.mjs +99 -0
- package/src/telemetry/index.mjs +96 -0
- package/src/terminal/agents.mjs +177 -0
- package/src/terminal/analytics.mjs +292 -0
- package/src/terminal/ansi.mjs +695 -0
- package/src/terminal/init.mjs +145 -0
- package/src/terminal/main.mjs +269 -0
- package/src/terminal/repl-explore.mjs +35 -0
- package/src/terminal/repl-format.mjs +257 -0
- package/src/terminal/repl-render.mjs +561 -0
- package/src/terminal/repl-resume.mjs +625 -0
- package/src/terminal/repl-state.mjs +103 -0
- package/src/terminal/repl-utils.mjs +34 -0
- package/src/terminal/repl.mjs +3832 -0
- package/src/terminal/skills.mjs +54 -0
- package/src/terminal/tool-display.mjs +240 -0
- package/src/tools/agent.mjs +137 -0
- package/src/tools/ask-user.mjs +61 -0
- package/src/tools/bash.mjs +231 -0
- package/src/tools/cron-create.mjs +120 -0
- package/src/tools/cron-delete.mjs +49 -0
- package/src/tools/cron-list.mjs +37 -0
- package/src/tools/edit.mjs +82 -0
- package/src/tools/enter-worktree.mjs +69 -0
- package/src/tools/exit-worktree.mjs +57 -0
- package/src/tools/glob.mjs +117 -0
- package/src/tools/grep.mjs +129 -0
- package/src/tools/lint.mjs +71 -0
- package/src/tools/ls.mjs +58 -0
- package/src/tools/lsp.mjs +115 -0
- package/src/tools/multi-edit.mjs +94 -0
- package/src/tools/notebook-edit.mjs +96 -0
- package/src/tools/project-overview.mjs +641 -0
- package/src/tools/read-mcp-resource.mjs +57 -0
- package/src/tools/read.mjs +138 -0
- package/src/tools/registry.mjs +116 -0
- package/src/tools/remote-trigger.mjs +84 -0
- package/src/tools/send-message.mjs +64 -0
- package/src/tools/skill.mjs +52 -0
- package/src/tools/test-runner.mjs +49 -0
- package/src/tools/todo-write.mjs +68 -0
- package/src/tools/tool-search.mjs +77 -0
- package/src/tools/web-fetch.mjs +65 -0
- package/src/tools/web-search.mjs +89 -0
- package/src/tools/write.mjs +55 -0
- package/src/ui/approval.mjs +263 -0
- package/src/ui/banner.mjs +235 -0
- package/src/ui/commands.mjs +537 -0
- package/src/ui/formatter.mjs +409 -0
- package/src/ui/icons.mjs +164 -0
- package/src/ui/input-dock.mjs +444 -0
- package/src/ui/markdown.mjs +278 -0
- package/src/ui/mission-report.mjs +296 -0
- package/src/ui/palette.mjs +189 -0
- package/src/ui/slash-commands.mjs +245 -0
- package/src/ui/spinner.mjs +116 -0
- package/src/ui/sub-agent.mjs +152 -0
- package/src/ui/term.mjs +159 -0
- package/src/ui/text-layout.mjs +127 -0
- package/src/ui/tool-card.mjs +463 -0
- package/src/ui/tool-details.mjs +312 -0
- package/src/ui/transcript-block.mjs +21 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SkillInstaller } from '../skills/installer.mjs';
|
|
2
|
+
import { SkillsLoader } from '../skills/loader.mjs';
|
|
3
|
+
|
|
4
|
+
function has(args, flag) {
|
|
5
|
+
return args.includes(flag);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function scopeFrom(args) {
|
|
9
|
+
return has(args, '--project') ? 'project' : 'global';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function print(value) {
|
|
13
|
+
process.stdout.write(typeof value === 'string' ? `${value}\n` : `${JSON.stringify(value, null, 2)}\n`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function runSkillsCommand(args, { cwd = process.cwd() } = {}) {
|
|
17
|
+
const action = args[0] || 'list';
|
|
18
|
+
const rest = args.slice(1);
|
|
19
|
+
const scope = scopeFrom(rest);
|
|
20
|
+
const installer = new SkillInstaller({ cwd });
|
|
21
|
+
const loader = new SkillsLoader().load(cwd);
|
|
22
|
+
|
|
23
|
+
if (action === 'list') {
|
|
24
|
+
const rows = loader.list({ scope: has(rest, '--all') ? '' : scope });
|
|
25
|
+
if (has(rest, '--json')) print(rows);
|
|
26
|
+
else if (!rows.length) print('No skills found.');
|
|
27
|
+
else for (const row of rows) print(`${row.name}\t${row.scope}\t${row.source}\t${row.description}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (action === 'view') {
|
|
31
|
+
const name = rest.find(arg => !arg.startsWith('--'));
|
|
32
|
+
if (!name) throw new Error('Usage: bahulam-code skills view <name> [resource-path]');
|
|
33
|
+
const nameIndex = rest.indexOf(name);
|
|
34
|
+
const resource = rest.slice(nameIndex + 1).find(arg => !arg.startsWith('--')) || null;
|
|
35
|
+
print(loader.view(name, resource));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (action === 'install') {
|
|
39
|
+
const source = rest.find(arg => !arg.startsWith('--'));
|
|
40
|
+
print(installer.install(source, { scope, force: has(rest, '--force') }));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (action === 'remove') {
|
|
44
|
+
const name = rest.find(arg => !arg.startsWith('--'));
|
|
45
|
+
print(installer.remove(name, { scope }));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (action === 'update') {
|
|
49
|
+
const name = rest.find(arg => !arg.startsWith('--'));
|
|
50
|
+
print(installer.update(name, { scope }));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`Unknown skills command: ${action}`);
|
|
54
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Present-progressive verbs — read more conversationally than "Read file":
|
|
2
|
+
// "Reading auth.py — 47 lines" reads like the agent narrating, not a log.
|
|
3
|
+
const TOOL_LABELS = Object.freeze({
|
|
4
|
+
shell: 'Running',
|
|
5
|
+
read_file: 'Reading',
|
|
6
|
+
read_files: 'Reading',
|
|
7
|
+
read_batch: 'Reading batch',
|
|
8
|
+
write_file: 'Writing',
|
|
9
|
+
write_project: 'Writing files',
|
|
10
|
+
edit_file: 'Editing',
|
|
11
|
+
delete_file: 'Deleting',
|
|
12
|
+
list_files: 'Listing',
|
|
13
|
+
search_code: 'Searching',
|
|
14
|
+
search_files: 'Searching files',
|
|
15
|
+
grep: 'Searching for',
|
|
16
|
+
get_file_info: 'Inspecting',
|
|
17
|
+
validate_file: 'Validating',
|
|
18
|
+
validate_build: 'Validating build',
|
|
19
|
+
validate_structure: 'Checking structure',
|
|
20
|
+
lint_check: 'Linting',
|
|
21
|
+
run_tests: 'Running tests',
|
|
22
|
+
git_diff: 'Reviewing changes',
|
|
23
|
+
git_status: 'Checking git',
|
|
24
|
+
analyze_code: 'Analyzing',
|
|
25
|
+
get_project_overview: 'Indexing project',
|
|
26
|
+
skills_list: 'Listing skills',
|
|
27
|
+
skill_view: 'Loading skill',
|
|
28
|
+
skill_install: 'Installing skill',
|
|
29
|
+
skill_update: 'Updating skill',
|
|
30
|
+
skill_remove: 'Removing skill',
|
|
31
|
+
agents_list: 'Listing agents',
|
|
32
|
+
agent_create: 'Creating agent',
|
|
33
|
+
agent_sync: 'Syncing agents',
|
|
34
|
+
workflow_list: 'Listing workflows',
|
|
35
|
+
workflow_create_multi: 'Creating workflow',
|
|
36
|
+
workflow_sync_multi: 'Syncing workflows',
|
|
37
|
+
workflow_run_multi: 'Running workflow',
|
|
38
|
+
explore: 'Exploring',
|
|
39
|
+
plan: 'Planning',
|
|
40
|
+
verify: 'Verifying',
|
|
41
|
+
debug: 'Debugging',
|
|
42
|
+
refactor: 'Refactoring',
|
|
43
|
+
ask_user: 'Asking',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export function toolDisplayLabel(tool) {
|
|
47
|
+
if (!tool) return 'Use tool';
|
|
48
|
+
if (TOOL_LABELS[tool]) return TOOL_LABELS[tool];
|
|
49
|
+
return tool
|
|
50
|
+
.replace(/^mcp[_-]?/i, '')
|
|
51
|
+
.split(/[_-]+/)
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.map((part, index) => index === 0
|
|
54
|
+
? part.charAt(0).toUpperCase() + part.slice(1)
|
|
55
|
+
: part.toLowerCase())
|
|
56
|
+
.join(' ');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function currentWorkingDirectory() {
|
|
60
|
+
try {
|
|
61
|
+
return process.cwd();
|
|
62
|
+
} catch {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function shortPath(filePath, cwd = currentWorkingDirectory()) {
|
|
68
|
+
const value = String(filePath || '');
|
|
69
|
+
if (cwd && value.startsWith(`${cwd}/`)) return value.slice(cwd.length + 1);
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Sub-agent prompts (explore/plan/verify/debug/refactor) are often multi-
|
|
74
|
+
// paragraph briefs with bullet lists — dumping the whole thing into a tool
|
|
75
|
+
// card head made the terminal render N lines of noise per call. Show just
|
|
76
|
+
// the first paragraph (up to the first blank line), whitespace-collapsed to
|
|
77
|
+
// a single line, and add an ellipsis if more content was hidden.
|
|
78
|
+
function firstParagraph(text) {
|
|
79
|
+
const raw = String(text || '').replace(/\r\n?/g, '\n').trim();
|
|
80
|
+
if (!raw) return '';
|
|
81
|
+
const paraEnd = raw.indexOf('\n\n');
|
|
82
|
+
const head = paraEnd === -1 ? raw : raw.slice(0, paraEnd);
|
|
83
|
+
const collapsed = head.replace(/\s+/g, ' ').trim();
|
|
84
|
+
return raw.length > head.length ? `${collapsed} …` : collapsed;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function toolDisplaySummary(tool, args = {}, { cwd } = {}) {
|
|
88
|
+
switch (tool) {
|
|
89
|
+
case 'shell':
|
|
90
|
+
return args.command || '(empty command)';
|
|
91
|
+
case 'read_file': {
|
|
92
|
+
const filePath = shortPath(args.file_path || args.path, cwd);
|
|
93
|
+
if (args.start_line && args.end_line) {
|
|
94
|
+
return `${filePath} · lines ${args.start_line}-${args.end_line}`;
|
|
95
|
+
}
|
|
96
|
+
if (args.start_line) return `${filePath} · from line ${args.start_line}`;
|
|
97
|
+
return filePath;
|
|
98
|
+
}
|
|
99
|
+
case 'read_files':
|
|
100
|
+
case 'read_batch': {
|
|
101
|
+
const files = (args.file_paths || args.paths || [])
|
|
102
|
+
.concat((args.items || []).map(item => typeof item === 'string' ? item : item?.file_path || item?.path).filter(Boolean))
|
|
103
|
+
.map(filePath => shortPath(filePath, cwd));
|
|
104
|
+
if (files.length <= 6) return files.join(', ');
|
|
105
|
+
return `${files.slice(0, 3).join(', ')} · +${files.length - 3} more`;
|
|
106
|
+
}
|
|
107
|
+
case 'write_file': {
|
|
108
|
+
const filePath = shortPath(args.file_path || args.path, cwd);
|
|
109
|
+
const lineCount = typeof args.content === 'string'
|
|
110
|
+
? args.content.split('\n').length
|
|
111
|
+
: null;
|
|
112
|
+
return lineCount ? `${filePath} · ${lineCount} lines` : filePath;
|
|
113
|
+
}
|
|
114
|
+
case 'write_project':
|
|
115
|
+
return (args.files || [])
|
|
116
|
+
.map(file => shortPath(file.path || file.file_path, cwd))
|
|
117
|
+
.filter(Boolean)
|
|
118
|
+
.join(', ') || 'Project files';
|
|
119
|
+
case 'edit_file': {
|
|
120
|
+
const filePath = shortPath(args.file_path || args.path, cwd);
|
|
121
|
+
const search = String(args.search || '').trim();
|
|
122
|
+
return search ? `${filePath} · match "${search.slice(0, 40)}${search.length > 40 ? '...' : ''}"` : filePath;
|
|
123
|
+
}
|
|
124
|
+
case 'delete_file':
|
|
125
|
+
return shortPath(args.file_path || args.path, cwd);
|
|
126
|
+
case 'search_code':
|
|
127
|
+
case 'search_files':
|
|
128
|
+
case 'grep':
|
|
129
|
+
return `"${args.query || args.pattern || ''}"${args.path ? ` in ${shortPath(args.path, cwd)}` : ''}`;
|
|
130
|
+
case 'list_files':
|
|
131
|
+
return `${args.pattern || '*'}${args.path ? ` in ${shortPath(args.path, cwd)}` : ''}`;
|
|
132
|
+
case 'run_tests':
|
|
133
|
+
case 'validate_build':
|
|
134
|
+
case 'lint_check':
|
|
135
|
+
return args.command || args.path || args.file_path || '';
|
|
136
|
+
case 'git_diff':
|
|
137
|
+
case 'git_status':
|
|
138
|
+
return args.path ? shortPath(args.path, cwd) : '';
|
|
139
|
+
case 'skills_list':
|
|
140
|
+
return [args.query ? `"${args.query}"` : '', args.scope || '', args.source || ''].filter(Boolean).join(' · ');
|
|
141
|
+
case 'skill_view':
|
|
142
|
+
return [args.name || '', args.path || '', args.source_id || ''].filter(Boolean).join(' · ');
|
|
143
|
+
case 'skill_install':
|
|
144
|
+
return `${args.source || ''}${args.scope ? ` → ${args.scope}` : ''}${args.force ? ' · force' : ''}`;
|
|
145
|
+
case 'skill_update':
|
|
146
|
+
case 'skill_remove':
|
|
147
|
+
return `${args.name || ''}${args.scope ? ` · ${args.scope}` : ''}`;
|
|
148
|
+
case 'agents_list':
|
|
149
|
+
return [args.query ? `"${args.query}"` : '', args.scope || ''].filter(Boolean).join(' · ');
|
|
150
|
+
case 'agent_create':
|
|
151
|
+
return [args.name || '', args.role || '', args.model || ''].filter(Boolean).join(' · ');
|
|
152
|
+
case 'agent_sync':
|
|
153
|
+
return args.name || args.slug || 'all local agents';
|
|
154
|
+
case 'workflow_list':
|
|
155
|
+
return [args.query ? `"${args.query}"` : '', args.scope || ''].filter(Boolean).join(' · ');
|
|
156
|
+
case 'workflow_create_multi':
|
|
157
|
+
return [args.name || '', args.pattern || 'sequential', Array.isArray(args.agents) ? `${args.agents.length} agents` : ''].filter(Boolean).join(' · ');
|
|
158
|
+
case 'workflow_sync_multi':
|
|
159
|
+
return args.name || args.slug || 'all local workflows';
|
|
160
|
+
case 'workflow_run_multi':
|
|
161
|
+
return [args.workflow_id || args.workflowId || args.name || '', args.pattern || 'sequential'].filter(Boolean).join(' · ');
|
|
162
|
+
case 'explore':
|
|
163
|
+
case 'plan':
|
|
164
|
+
case 'verify':
|
|
165
|
+
case 'debug':
|
|
166
|
+
case 'refactor':
|
|
167
|
+
return firstParagraph(args.query || args.task || args.prompt || '');
|
|
168
|
+
case 'ask_user':
|
|
169
|
+
return args.question || args.prompt || '';
|
|
170
|
+
default:
|
|
171
|
+
return Object.values(args)
|
|
172
|
+
.filter(value => typeof value === 'string' && value)
|
|
173
|
+
.join(', ')
|
|
174
|
+
.slice(0, 120);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function shellCommandDisplay(command, { cwd = currentWorkingDirectory() } = {}) {
|
|
179
|
+
const parsed = parseLeadingCd(command);
|
|
180
|
+
if (!parsed) return { command: String(command || '(empty command)'), cwdLabel: '' };
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
command: parsed.command || '(empty command)',
|
|
184
|
+
cwdLabel: compactShellCwd(parsed.cwd, cwd),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function formatShellCommand(command, colors) {
|
|
189
|
+
const tokens = String(command || '').match(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|&&|\|\||[|;<>]|[^\s]+|\s+/g) || [];
|
|
190
|
+
let expectsCommand = true;
|
|
191
|
+
|
|
192
|
+
return tokens.map(token => {
|
|
193
|
+
if (/^\s+$/.test(token)) return token;
|
|
194
|
+
if (/^(?:&&|\|\||[|;<>])$/.test(token)) {
|
|
195
|
+
expectsCommand = true;
|
|
196
|
+
return colors.red(token);
|
|
197
|
+
}
|
|
198
|
+
if (expectsCommand) {
|
|
199
|
+
expectsCommand = false;
|
|
200
|
+
return colors.blue(token);
|
|
201
|
+
}
|
|
202
|
+
if (/^-{1,2}[\w-]+(?:=.*)?$/.test(token) || /^["']/.test(token)) {
|
|
203
|
+
return colors.yellow(token);
|
|
204
|
+
}
|
|
205
|
+
return colors.white(token);
|
|
206
|
+
}).join('');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function parseLeadingCd(command) {
|
|
210
|
+
const text = String(command || '').trim();
|
|
211
|
+
const match = text.match(/^cd\s+((?:"(?:\\.|[^"\\])*")|'(?:\\.|[^'\\])*'|(?:\\.|[^\s&])+)\s*&&\s*([\s\S]+)$/);
|
|
212
|
+
if (!match) return null;
|
|
213
|
+
return {
|
|
214
|
+
cwd: unquoteShellToken(match[1]),
|
|
215
|
+
command: match[2].trim(),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function unquoteShellToken(token) {
|
|
220
|
+
const text = String(token || '');
|
|
221
|
+
if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) {
|
|
222
|
+
const body = text.slice(1, -1);
|
|
223
|
+
return text.startsWith('"')
|
|
224
|
+
? body.replace(/\\(["\\$`])/g, '$1')
|
|
225
|
+
: body;
|
|
226
|
+
}
|
|
227
|
+
return text.replace(/\\(.)/g, '$1');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function compactShellCwd(dir, cwd) {
|
|
231
|
+
const value = String(dir || '');
|
|
232
|
+
if (!value) return '';
|
|
233
|
+
if (cwd && value === cwd) return '';
|
|
234
|
+
if (cwd && value.startsWith(`${cwd}/`)) return value.slice(cwd.length + 1);
|
|
235
|
+
|
|
236
|
+
const parts = value.split('/').filter(Boolean);
|
|
237
|
+
if (parts.length <= 2) return value || dir;
|
|
238
|
+
if (/\s/.test(parts[parts.length - 2] || '')) return parts[parts.length - 1];
|
|
239
|
+
return parts.slice(-2).join('/');
|
|
240
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Tool — spawn a subagent with its own agent loop.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - subagent_type parameter
|
|
6
|
+
* - isolation: "worktree" option
|
|
7
|
+
* - run_in_background option
|
|
8
|
+
* - model override
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createAgentLoop } from '../core/agent-loop.mjs';
|
|
12
|
+
import { createToolRegistry } from './registry.mjs';
|
|
13
|
+
import { createPermissionChecker } from '../permissions/checker.mjs';
|
|
14
|
+
|
|
15
|
+
export const AgentTool = {
|
|
16
|
+
name: 'Agent',
|
|
17
|
+
description: 'Spawn a subagent to handle a task. The subagent has its own context and tools.',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
prompt: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'The task for the subagent to perform',
|
|
24
|
+
},
|
|
25
|
+
allowed_tools: {
|
|
26
|
+
type: 'array',
|
|
27
|
+
items: { type: 'string' },
|
|
28
|
+
description: 'List of tool names the subagent can use (default: all)',
|
|
29
|
+
},
|
|
30
|
+
subagent_type: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Type of subagent (e.g. coder, reviewer, researcher)',
|
|
33
|
+
},
|
|
34
|
+
isolation: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
enum: ['default', 'worktree'],
|
|
37
|
+
description: 'Isolation mode. "worktree" uses a git worktree.',
|
|
38
|
+
},
|
|
39
|
+
run_in_background: {
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
description: 'Run in background and return immediately',
|
|
42
|
+
},
|
|
43
|
+
model: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Override model for this subagent',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ['prompt'],
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
validateInput(input) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
if (!input.prompt) errors.push('prompt is required');
|
|
54
|
+
return errors;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// Track background subagents
|
|
58
|
+
_backgroundAgents: new Map(),
|
|
59
|
+
_nextBgId: 0,
|
|
60
|
+
|
|
61
|
+
async call(input) {
|
|
62
|
+
const model = input.model || process.env.SUBAGENT_MODEL || 'claude-sonnet-4-6';
|
|
63
|
+
const tools = createToolRegistry();
|
|
64
|
+
const permissions = createPermissionChecker({ defaultMode: 'bypassPermissions' });
|
|
65
|
+
|
|
66
|
+
// Build type-specific system prompt prefix
|
|
67
|
+
let systemPrefix = '';
|
|
68
|
+
if (input.subagent_type) {
|
|
69
|
+
const typePrompts = {
|
|
70
|
+
coder: 'You are a coding agent. Write clean, tested code.',
|
|
71
|
+
reviewer: 'You are a code reviewer. Analyze code for bugs and improvements.',
|
|
72
|
+
researcher: 'You are a research agent. Find and summarize information.',
|
|
73
|
+
tester: 'You are a testing agent. Write and run tests.',
|
|
74
|
+
planner: 'You are a planning agent. Break down tasks into steps.',
|
|
75
|
+
};
|
|
76
|
+
systemPrefix = typePrompts[input.subagent_type] || `You are a ${input.subagent_type} agent.`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const fullPrompt = systemPrefix
|
|
80
|
+
? `${systemPrefix}\n\nTask: ${input.prompt}`
|
|
81
|
+
: input.prompt;
|
|
82
|
+
|
|
83
|
+
const loop = createAgentLoop({
|
|
84
|
+
model,
|
|
85
|
+
tools,
|
|
86
|
+
permissions,
|
|
87
|
+
settings: {
|
|
88
|
+
stream: false,
|
|
89
|
+
stagnationDetection: !['0', 'false', 'no', 'off'].includes(
|
|
90
|
+
(process.env.KEPLER_STAGNATION_DETECTION ?? '0').toLowerCase(),
|
|
91
|
+
),
|
|
92
|
+
stagnationThreshold: Number.parseInt(
|
|
93
|
+
process.env.KEPLER_STAGNATION_THRESHOLD
|
|
94
|
+
?? '3',
|
|
95
|
+
10,
|
|
96
|
+
),
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (input.run_in_background) {
|
|
101
|
+
const bgId = ++AgentTool._nextBgId;
|
|
102
|
+
const entry = { id: bgId, status: 'running', result: null, prompt: input.prompt };
|
|
103
|
+
AgentTool._backgroundAgents.set(bgId, entry);
|
|
104
|
+
|
|
105
|
+
// Run in background
|
|
106
|
+
runSubagent(loop, fullPrompt).then(result => {
|
|
107
|
+
entry.status = 'completed';
|
|
108
|
+
entry.result = result;
|
|
109
|
+
}).catch(err => {
|
|
110
|
+
entry.status = 'error';
|
|
111
|
+
entry.result = err.message;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return `Subagent started in background: id=${bgId}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return runSubagent(loop, fullPrompt);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
async function runSubagent(loop, prompt) {
|
|
122
|
+
const results = [];
|
|
123
|
+
try {
|
|
124
|
+
for await (const event of loop.run(prompt)) {
|
|
125
|
+
if (event.type === 'assistant' && event.content) {
|
|
126
|
+
results.push(event.content);
|
|
127
|
+
}
|
|
128
|
+
if (event.type === 'result') {
|
|
129
|
+
results.push(`[tool:${event.tool}] ${String(event.result).slice(0, 500)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
return `Subagent error: ${err.message}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return results.join('\n') || 'Subagent completed with no output.';
|
|
137
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AskUser Tool — prompt the user with a question and return their response.
|
|
3
|
+
*
|
|
4
|
+
* Used when the agent needs clarification or confirmation from the user.
|
|
5
|
+
* In non-interactive mode, returns a default or times out.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import readline from 'readline';
|
|
9
|
+
|
|
10
|
+
export const AskUserTool = {
|
|
11
|
+
name: 'AskUser',
|
|
12
|
+
description: 'Ask the user a question and wait for their response.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
question: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'The question to ask the user',
|
|
19
|
+
},
|
|
20
|
+
default_value: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Default value if user provides no input',
|
|
23
|
+
},
|
|
24
|
+
timeout: {
|
|
25
|
+
type: 'number',
|
|
26
|
+
description: 'Timeout in milliseconds (default: 60000)',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ['question'],
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
validateInput(input) {
|
|
33
|
+
return input.question ? [] : ['question is required'];
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
async call(input) {
|
|
37
|
+
// In non-interactive mode, return default
|
|
38
|
+
if (!process.stdin.isTTY) {
|
|
39
|
+
return input.default_value || '[non-interactive: no user input available]';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const rl = readline.createInterface({
|
|
44
|
+
input: process.stdin,
|
|
45
|
+
output: process.stderr,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const timeout = setTimeout(() => {
|
|
49
|
+
rl.close();
|
|
50
|
+
resolve(input.default_value || '[timeout: no response]');
|
|
51
|
+
}, input.timeout || 60000);
|
|
52
|
+
|
|
53
|
+
process.stderr.write(`\n\x1b[36m? ${input.question}\x1b[0m\n> `);
|
|
54
|
+
rl.question('', (answer) => {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
rl.close();
|
|
57
|
+
resolve(answer.trim() || input.default_value || '');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
};
|