@oh-my-pi/pi-coding-agent 1.337.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/CHANGELOG.md +1228 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +637 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/reviewer.md +35 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +81 -0
- package/src/cli/args.ts +246 -0
- package/src/cli/file-processor.ts +72 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +650 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli.ts +10 -0
- package/src/commands/init.md +20 -0
- package/src/config.ts +159 -0
- package/src/core/agent-session.ts +1900 -0
- package/src/core/auth-storage.ts +236 -0
- package/src/core/bash-executor.ts +196 -0
- package/src/core/compaction/branch-summarization.ts +343 -0
- package/src/core/compaction/compaction.ts +742 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +154 -0
- package/src/core/custom-tools/index.ts +21 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +169 -0
- package/src/core/custom-tools/wrapper.ts +28 -0
- package/src/core/exec.ts +129 -0
- package/src/core/export-html/index.ts +211 -0
- package/src/core/export-html/template.css +781 -0
- package/src/core/export-html/template.html +54 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/vendor/highlight.min.js +1213 -0
- package/src/core/export-html/vendor/marked.min.js +6 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +312 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +99 -0
- package/src/core/hooks/types.ts +773 -0
- package/src/core/index.ts +52 -0
- package/src/core/mcp/client.ts +158 -0
- package/src/core/mcp/config.ts +154 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +68 -0
- package/src/core/mcp/manager.ts +181 -0
- package/src/core/mcp/tool-bridge.ts +148 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +220 -0
- package/src/core/messages.ts +189 -0
- package/src/core/model-registry.ts +317 -0
- package/src/core/model-resolver.ts +393 -0
- package/src/core/plugins/doctor.ts +59 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +338 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +32 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +760 -0
- package/src/core/session-manager.ts +1128 -0
- package/src/core/settings-manager.ts +443 -0
- package/src/core/skills.ts +437 -0
- package/src/core/slash-commands.ts +248 -0
- package/src/core/system-prompt.ts +439 -0
- package/src/core/timings.ts +25 -0
- package/src/core/tools/ask.ts +211 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +250 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +475 -0
- package/src/core/tools/edit.ts +208 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +64 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/logger.ts +56 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +196 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +337 -0
- package/src/core/tools/exa/types.ts +168 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +261 -0
- package/src/core/tools/grep.ts +555 -0
- package/src/core/tools/index.ts +202 -0
- package/src/core/tools/ls.ts +140 -0
- package/src/core/tools/lsp/client.ts +605 -0
- package/src/core/tools/lsp/config.ts +147 -0
- package/src/core/tools/lsp/edits.ts +101 -0
- package/src/core/tools/lsp/index.ts +804 -0
- package/src/core/tools/lsp/render.ts +447 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +463 -0
- package/src/core/tools/lsp/utils.ts +486 -0
- package/src/core/tools/notebook.ts +229 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +240 -0
- package/src/core/tools/renderers.ts +540 -0
- package/src/core/tools/task/agents.ts +153 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/bundled-agents/browser.md +71 -0
- package/src/core/tools/task/bundled-agents/explore.md +82 -0
- package/src/core/tools/task/bundled-agents/plan.md +54 -0
- package/src/core/tools/task/bundled-agents/reviewer.md +59 -0
- package/src/core/tools/task/bundled-agents/task.md +53 -0
- package/src/core/tools/task/bundled-commands/architect-plan.md +10 -0
- package/src/core/tools/task/bundled-commands/implement-with-critic.md +11 -0
- package/src/core/tools/task/bundled-commands/implement.md +11 -0
- package/src/core/tools/task/commands.ts +213 -0
- package/src/core/tools/task/discovery.ts +208 -0
- package/src/core/tools/task/executor.ts +367 -0
- package/src/core/tools/task/index.ts +388 -0
- package/src/core/tools/task/model-resolver.ts +115 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +232 -0
- package/src/core/tools/task/types.ts +99 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2370 -0
- package/src/core/tools/web-search/auth.ts +193 -0
- package/src/core/tools/web-search/index.ts +537 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +302 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +182 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +99 -0
- package/src/index.ts +176 -0
- package/src/main.ts +464 -0
- package/src/migrations.ts +135 -0
- package/src/modes/index.ts +43 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +196 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/footer.ts +381 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +247 -0
- package/src/modes/interactive/components/oauth-selector.ts +120 -0
- package/src/modes/interactive/components/plugin-settings.ts +479 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +204 -0
- package/src/modes/interactive/components/settings-selector.ts +453 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +675 -0
- package/src/modes/interactive/components/tree-selector.ts +866 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +183 -0
- package/src/modes/interactive/interactive-mode.ts +2516 -0
- package/src/modes/interactive/theme/dark.json +101 -0
- package/src/modes/interactive/theme/light.json +98 -0
- package/src/modes/interactive/theme/theme-schema.json +308 -0
- package/src/modes/interactive/theme/theme.ts +998 -0
- package/src/modes/print-mode.ts +128 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +483 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell.ts +276 -0
- package/src/utils/tools-manager.ts +274 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System prompt construction and project context loading
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { existsSync, readFileSync } from "fs";
|
|
7
|
+
import { join, resolve } from "path";
|
|
8
|
+
import { getAgentDir, getDocsPath, getExamplesPath, getReadmePath } from "../config.js";
|
|
9
|
+
import type { SkillsSettings } from "./settings-manager.js";
|
|
10
|
+
import { formatSkillsForPrompt, loadSkills, type Skill } from "./skills.js";
|
|
11
|
+
import type { ToolName } from "./tools/index.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Execute a git command synchronously and return stdout or null on failure.
|
|
15
|
+
*/
|
|
16
|
+
function execGit(args: string[], cwd: string): string | null {
|
|
17
|
+
const result = Bun.spawnSync(["git", ...args], { cwd, stdin: "ignore", stdout: "pipe", stderr: "pipe" });
|
|
18
|
+
if (result.exitCode !== 0) return null;
|
|
19
|
+
return result.stdout.toString().trim() || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Load git context for the system prompt.
|
|
24
|
+
* Returns formatted git status or null if not in a git repo.
|
|
25
|
+
*/
|
|
26
|
+
export function loadGitContext(cwd: string): string | null {
|
|
27
|
+
// Check if inside a git repo
|
|
28
|
+
const isGitRepo = execGit(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
29
|
+
if (isGitRepo !== "true") return null;
|
|
30
|
+
|
|
31
|
+
// Get current branch
|
|
32
|
+
const currentBranch = execGit(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
33
|
+
if (!currentBranch) return null;
|
|
34
|
+
|
|
35
|
+
// Detect main branch (check for 'main' first, then 'master')
|
|
36
|
+
let mainBranch = "main";
|
|
37
|
+
const mainExists = execGit(["rev-parse", "--verify", "main"], cwd);
|
|
38
|
+
if (mainExists === null) {
|
|
39
|
+
const masterExists = execGit(["rev-parse", "--verify", "master"], cwd);
|
|
40
|
+
if (masterExists !== null) mainBranch = "master";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get git status (porcelain format for parsing)
|
|
44
|
+
const gitStatus = execGit(["status", "--porcelain"], cwd);
|
|
45
|
+
const statusText = gitStatus?.trim() || "(clean)";
|
|
46
|
+
|
|
47
|
+
// Get recent commits
|
|
48
|
+
const recentCommits = execGit(["log", "--oneline", "-5"], cwd);
|
|
49
|
+
const commitsText = recentCommits?.trim() || "(no commits)";
|
|
50
|
+
|
|
51
|
+
return `This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.
|
|
52
|
+
Current branch: ${currentBranch}
|
|
53
|
+
|
|
54
|
+
Main branch (you will usually use this for PRs): ${mainBranch}
|
|
55
|
+
|
|
56
|
+
Status:
|
|
57
|
+
${statusText}
|
|
58
|
+
|
|
59
|
+
Recent commits:
|
|
60
|
+
${commitsText}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Tool descriptions for system prompt */
|
|
64
|
+
const toolDescriptions: Record<ToolName, string> = {
|
|
65
|
+
ask: "Ask user for input or clarification",
|
|
66
|
+
read: "Read file contents",
|
|
67
|
+
bash: "Execute bash commands (git, npm, docker, etc.)",
|
|
68
|
+
edit: "Make surgical edits to files (find exact text and replace)",
|
|
69
|
+
write: "Create or overwrite files",
|
|
70
|
+
grep: "Search file contents for patterns (respects .gitignore)",
|
|
71
|
+
find: "Find files by glob pattern (respects .gitignore)",
|
|
72
|
+
ls: "List directory contents",
|
|
73
|
+
lsp: "PREFERRED for semantic code queries: go-to-definition, find-all-references, hover (type info), call hierarchy. Returns precise, deterministic results. Use BEFORE grep for symbol lookups.",
|
|
74
|
+
notebook: "Edit Jupyter notebook cells",
|
|
75
|
+
task: "Spawn a sub-agent to handle complex tasks",
|
|
76
|
+
web_fetch: "Fetch and render URLs into clean text for LLM consumption",
|
|
77
|
+
web_search: "Search the web for information",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate anti-bash rules section if the agent has both bash and specialized tools.
|
|
82
|
+
* Only include rules for tools that are actually available.
|
|
83
|
+
*/
|
|
84
|
+
function generateAntiBashRules(tools: ToolName[]): string | null {
|
|
85
|
+
const hasBash = tools.includes("bash");
|
|
86
|
+
if (!hasBash) return null;
|
|
87
|
+
|
|
88
|
+
const hasRead = tools.includes("read");
|
|
89
|
+
const hasGrep = tools.includes("grep");
|
|
90
|
+
const hasFind = tools.includes("find");
|
|
91
|
+
const hasLs = tools.includes("ls");
|
|
92
|
+
const hasEdit = tools.includes("edit");
|
|
93
|
+
const hasLsp = tools.includes("lsp");
|
|
94
|
+
|
|
95
|
+
// Only show rules if we have specialized tools that should be preferred
|
|
96
|
+
const hasSpecializedTools = hasRead || hasGrep || hasFind || hasLs || hasEdit;
|
|
97
|
+
if (!hasSpecializedTools) return null;
|
|
98
|
+
|
|
99
|
+
const lines: string[] = [];
|
|
100
|
+
lines.push("## Tool Usage Rules — MANDATORY\n");
|
|
101
|
+
lines.push("### Forbidden Bash Patterns");
|
|
102
|
+
lines.push("NEVER use bash for these operations:\n");
|
|
103
|
+
|
|
104
|
+
if (hasRead) lines.push("- **File reading**: Use `read` instead of cat/head/tail/less/more");
|
|
105
|
+
if (hasGrep) lines.push("- **Content search**: Use `grep` instead of grep/rg/ag/ack");
|
|
106
|
+
if (hasFind) lines.push("- **File finding**: Use `find` instead of find/fd/locate");
|
|
107
|
+
if (hasLs) lines.push("- **Directory listing**: Use `ls` instead of bash ls");
|
|
108
|
+
if (hasEdit) lines.push("- **File editing**: Use `edit` instead of sed/awk/perl -pi/echo >/cat <<EOF");
|
|
109
|
+
|
|
110
|
+
lines.push("\n### Tool Preference (highest → lowest priority)");
|
|
111
|
+
const ladder: string[] = [];
|
|
112
|
+
if (hasLsp) ladder.push("lsp (go-to-definition, references, type info) — DETERMINISTIC");
|
|
113
|
+
if (hasGrep) ladder.push("grep (text/regex search)");
|
|
114
|
+
if (hasFind) ladder.push("find (locate files by pattern)");
|
|
115
|
+
if (hasRead) ladder.push("read (view file contents)");
|
|
116
|
+
if (hasEdit) ladder.push("edit (precise text replacement)");
|
|
117
|
+
ladder.push("bash (ONLY for git, npm, docker, make, cargo, etc.)");
|
|
118
|
+
lines.push(ladder.map((t, i) => `${i + 1}. ${t}`).join("\n"));
|
|
119
|
+
|
|
120
|
+
// Add LSP guidance if available
|
|
121
|
+
if (hasLsp) {
|
|
122
|
+
lines.push("\n### LSP — Preferred for Semantic Queries");
|
|
123
|
+
lines.push("Use `lsp` instead of grep/bash when you need:");
|
|
124
|
+
lines.push("- **Where is X defined?** → `lsp definition`");
|
|
125
|
+
lines.push("- **What calls X?** → `lsp incoming_calls`");
|
|
126
|
+
lines.push("- **What does X call?** → `lsp outgoing_calls`");
|
|
127
|
+
lines.push("- **What type is X?** → `lsp hover`");
|
|
128
|
+
lines.push("- **What symbols are in this file?** → `lsp symbols`");
|
|
129
|
+
lines.push("- **Find symbol across codebase** → `lsp workspace_symbols`\n");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add search-first protocol
|
|
133
|
+
if (hasGrep || hasFind) {
|
|
134
|
+
lines.push("\n### Search-First Protocol");
|
|
135
|
+
lines.push("Before reading any file:");
|
|
136
|
+
if (hasFind) lines.push("1. Unknown structure → `find` to see file layout");
|
|
137
|
+
if (hasGrep) lines.push("2. Known location → `grep` for specific symbol/error");
|
|
138
|
+
if (hasRead) lines.push("3. Use `read offset/limit` for line ranges, not entire large files");
|
|
139
|
+
lines.push("4. Never read a large file hoping to find something — search first");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Resolve input as file path or literal string */
|
|
146
|
+
export function resolvePromptInput(input: string | undefined, description: string): string | undefined {
|
|
147
|
+
if (!input) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (existsSync(input)) {
|
|
152
|
+
try {
|
|
153
|
+
return readFileSync(input, "utf-8");
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));
|
|
156
|
+
return input;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return input;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Look for AGENTS.md or CLAUDE.md in a directory (prefers AGENTS.md) */
|
|
164
|
+
function loadContextFileFromDir(dir: string): { path: string; content: string } | null {
|
|
165
|
+
const candidates = ["AGENTS.md", "CLAUDE.md"];
|
|
166
|
+
for (const filename of candidates) {
|
|
167
|
+
const filePath = join(dir, filename);
|
|
168
|
+
if (existsSync(filePath)) {
|
|
169
|
+
try {
|
|
170
|
+
return {
|
|
171
|
+
path: filePath,
|
|
172
|
+
content: readFileSync(filePath, "utf-8"),
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface LoadContextFilesOptions {
|
|
183
|
+
/** Working directory to start walking up from. Default: process.cwd() */
|
|
184
|
+
cwd?: string;
|
|
185
|
+
/** Agent config directory for global context. Default: from getAgentDir() */
|
|
186
|
+
agentDir?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Load all project context files in order:
|
|
191
|
+
* 1. Global: agentDir/AGENTS.md or CLAUDE.md
|
|
192
|
+
* 2. Parent directories (top-most first) down to cwd
|
|
193
|
+
* Each returns {path, content} for separate messages
|
|
194
|
+
*/
|
|
195
|
+
export function loadProjectContextFiles(
|
|
196
|
+
options: LoadContextFilesOptions = {},
|
|
197
|
+
): Array<{ path: string; content: string }> {
|
|
198
|
+
const resolvedCwd = options.cwd ?? process.cwd();
|
|
199
|
+
const resolvedAgentDir = options.agentDir ?? getAgentDir();
|
|
200
|
+
|
|
201
|
+
const contextFiles: Array<{ path: string; content: string }> = [];
|
|
202
|
+
const seenPaths = new Set<string>();
|
|
203
|
+
|
|
204
|
+
// 1. Load global context from agentDir
|
|
205
|
+
const globalContext = loadContextFileFromDir(resolvedAgentDir);
|
|
206
|
+
if (globalContext) {
|
|
207
|
+
contextFiles.push(globalContext);
|
|
208
|
+
seenPaths.add(globalContext.path);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 2. Walk up from cwd to root, collecting all context files
|
|
212
|
+
const ancestorContextFiles: Array<{ path: string; content: string }> = [];
|
|
213
|
+
|
|
214
|
+
let currentDir = resolvedCwd;
|
|
215
|
+
const root = resolve("/");
|
|
216
|
+
|
|
217
|
+
while (true) {
|
|
218
|
+
const contextFile = loadContextFileFromDir(currentDir);
|
|
219
|
+
if (contextFile && !seenPaths.has(contextFile.path)) {
|
|
220
|
+
// Add to beginning so we get top-most parent first
|
|
221
|
+
ancestorContextFiles.unshift(contextFile);
|
|
222
|
+
seenPaths.add(contextFile.path);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Stop if we've reached root
|
|
226
|
+
if (currentDir === root) break;
|
|
227
|
+
|
|
228
|
+
// Move up one directory
|
|
229
|
+
const parentDir = resolve(currentDir, "..");
|
|
230
|
+
if (parentDir === currentDir) break; // Safety check
|
|
231
|
+
currentDir = parentDir;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Add ancestor files in order (top-most → cwd)
|
|
235
|
+
contextFiles.push(...ancestorContextFiles);
|
|
236
|
+
|
|
237
|
+
return contextFiles;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface BuildSystemPromptOptions {
|
|
241
|
+
/** Custom system prompt (replaces default). */
|
|
242
|
+
customPrompt?: string;
|
|
243
|
+
/** Tools to include in prompt. Default: [read, bash, edit, write] */
|
|
244
|
+
selectedTools?: ToolName[];
|
|
245
|
+
/** Text to append to system prompt. */
|
|
246
|
+
appendSystemPrompt?: string;
|
|
247
|
+
/** Skills settings for discovery. */
|
|
248
|
+
skillsSettings?: SkillsSettings;
|
|
249
|
+
/** Working directory. Default: process.cwd() */
|
|
250
|
+
cwd?: string;
|
|
251
|
+
/** Agent config directory. Default: from getAgentDir() */
|
|
252
|
+
agentDir?: string;
|
|
253
|
+
/** Pre-loaded context files (skips discovery if provided). */
|
|
254
|
+
contextFiles?: Array<{ path: string; content: string }>;
|
|
255
|
+
/** Pre-loaded skills (skips discovery if provided). */
|
|
256
|
+
skills?: Skill[];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Build the system prompt with tools, guidelines, and context */
|
|
260
|
+
export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {
|
|
261
|
+
const {
|
|
262
|
+
customPrompt,
|
|
263
|
+
selectedTools,
|
|
264
|
+
appendSystemPrompt,
|
|
265
|
+
skillsSettings,
|
|
266
|
+
cwd,
|
|
267
|
+
agentDir,
|
|
268
|
+
contextFiles: providedContextFiles,
|
|
269
|
+
skills: providedSkills,
|
|
270
|
+
} = options;
|
|
271
|
+
const resolvedCwd = cwd ?? process.cwd();
|
|
272
|
+
const resolvedCustomPrompt = resolvePromptInput(customPrompt, "system prompt");
|
|
273
|
+
const resolvedAppendPrompt = resolvePromptInput(appendSystemPrompt, "append system prompt");
|
|
274
|
+
|
|
275
|
+
const now = new Date();
|
|
276
|
+
const dateTime = now.toLocaleString("en-US", {
|
|
277
|
+
weekday: "long",
|
|
278
|
+
year: "numeric",
|
|
279
|
+
month: "long",
|
|
280
|
+
day: "numeric",
|
|
281
|
+
hour: "2-digit",
|
|
282
|
+
minute: "2-digit",
|
|
283
|
+
second: "2-digit",
|
|
284
|
+
timeZoneName: "short",
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const appendSection = resolvedAppendPrompt ? `\n\n${resolvedAppendPrompt}` : "";
|
|
288
|
+
|
|
289
|
+
// Resolve context files: use provided or discover
|
|
290
|
+
const contextFiles = providedContextFiles ?? loadProjectContextFiles({ cwd: resolvedCwd, agentDir });
|
|
291
|
+
|
|
292
|
+
// Resolve skills: use provided or discover
|
|
293
|
+
const skills =
|
|
294
|
+
providedSkills ??
|
|
295
|
+
(skillsSettings?.enabled !== false ? loadSkills({ ...skillsSettings, cwd: resolvedCwd, agentDir }).skills : []);
|
|
296
|
+
|
|
297
|
+
if (resolvedCustomPrompt) {
|
|
298
|
+
let prompt = resolvedCustomPrompt;
|
|
299
|
+
|
|
300
|
+
if (appendSection) {
|
|
301
|
+
prompt += appendSection;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Append project context files
|
|
305
|
+
if (contextFiles.length > 0) {
|
|
306
|
+
prompt += "\n\n# Project Context\n\n";
|
|
307
|
+
prompt += "The following project context files have been loaded:\n\n";
|
|
308
|
+
for (const { path: filePath, content } of contextFiles) {
|
|
309
|
+
prompt += `## ${filePath}\n\n${content}\n\n`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Append git context if in a git repo
|
|
314
|
+
const gitContext = loadGitContext(resolvedCwd);
|
|
315
|
+
if (gitContext) {
|
|
316
|
+
prompt += `\n\n# Git Status\n\n${gitContext}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Append skills section (only if read tool is available)
|
|
320
|
+
const customPromptHasRead = !selectedTools || selectedTools.includes("read");
|
|
321
|
+
if (customPromptHasRead && skills.length > 0) {
|
|
322
|
+
prompt += formatSkillsForPrompt(skills);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Add date/time and working directory last
|
|
326
|
+
prompt += `\nCurrent date and time: ${dateTime}`;
|
|
327
|
+
prompt += `\nCurrent working directory: ${resolvedCwd}`;
|
|
328
|
+
|
|
329
|
+
return prompt;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Get absolute paths to documentation and examples
|
|
333
|
+
const readmePath = getReadmePath();
|
|
334
|
+
const docsPath = getDocsPath();
|
|
335
|
+
const examplesPath = getExamplesPath();
|
|
336
|
+
|
|
337
|
+
// Build tools list based on selected tools
|
|
338
|
+
const tools = selectedTools || (["read", "bash", "edit", "write"] as ToolName[]);
|
|
339
|
+
const toolsList = tools.map((t) => `- ${t}: ${toolDescriptions[t]}`).join("\n");
|
|
340
|
+
|
|
341
|
+
// Generate anti-bash rules (returns null if not applicable)
|
|
342
|
+
const antiBashSection = generateAntiBashRules(tools);
|
|
343
|
+
|
|
344
|
+
// Build guidelines based on which tools are actually available
|
|
345
|
+
const guidelinesList: string[] = [];
|
|
346
|
+
|
|
347
|
+
const hasBash = tools.includes("bash");
|
|
348
|
+
const hasEdit = tools.includes("edit");
|
|
349
|
+
const hasWrite = tools.includes("write");
|
|
350
|
+
const hasRead = tools.includes("read");
|
|
351
|
+
|
|
352
|
+
// Read-only mode notice (no bash, edit, or write)
|
|
353
|
+
if (!hasBash && !hasEdit && !hasWrite) {
|
|
354
|
+
guidelinesList.push("You are in READ-ONLY mode - you cannot modify files or execute arbitrary commands");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Bash without edit/write = read-only bash mode
|
|
358
|
+
if (hasBash && !hasEdit && !hasWrite) {
|
|
359
|
+
guidelinesList.push(
|
|
360
|
+
"Use bash ONLY for read-only operations (git log, gh issue view, curl, etc.) - do NOT modify any files",
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Read before edit guideline
|
|
365
|
+
if (hasRead && hasEdit) {
|
|
366
|
+
guidelinesList.push("Use read to examine files before editing");
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Edit guideline
|
|
370
|
+
if (hasEdit) {
|
|
371
|
+
guidelinesList.push(
|
|
372
|
+
"Use edit for precise changes (old text must match exactly, fuzzy matching handles whitespace)",
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Write guideline
|
|
377
|
+
if (hasWrite) {
|
|
378
|
+
guidelinesList.push("Use write only for new files or complete rewrites");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Output guideline (only when actually writing/executing)
|
|
382
|
+
if (hasEdit || hasWrite) {
|
|
383
|
+
guidelinesList.push(
|
|
384
|
+
"When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did",
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Always include these
|
|
389
|
+
guidelinesList.push("Be concise in your responses");
|
|
390
|
+
guidelinesList.push("Show file paths clearly when working with files");
|
|
391
|
+
|
|
392
|
+
const guidelines = guidelinesList.map((g) => `- ${g}`).join("\n");
|
|
393
|
+
|
|
394
|
+
// Build the prompt with anti-bash rules prominently placed
|
|
395
|
+
let prompt = `You are an expert coding assistant. You help users with coding tasks by reading files, executing commands, editing code, and writing new files.
|
|
396
|
+
|
|
397
|
+
Available tools:
|
|
398
|
+
${toolsList}
|
|
399
|
+
${antiBashSection ? `\n${antiBashSection}\n` : ""}
|
|
400
|
+
Guidelines:
|
|
401
|
+
${guidelines}
|
|
402
|
+
|
|
403
|
+
Documentation:
|
|
404
|
+
- Main documentation: ${readmePath}
|
|
405
|
+
- Additional docs: ${docsPath}
|
|
406
|
+
- Examples: ${examplesPath} (hooks, custom tools, SDK)
|
|
407
|
+
- When asked to create: custom models/providers (README.md), hooks (docs/hooks.md, examples/hooks/), custom tools (docs/custom-tools.md, docs/tui.md, examples/custom-tools/), themes (docs/theme.md), skills (docs/skills.md)
|
|
408
|
+
- Always read the doc, examples, AND follow .md cross-references before implementing`;
|
|
409
|
+
|
|
410
|
+
if (appendSection) {
|
|
411
|
+
prompt += appendSection;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Append project context files
|
|
415
|
+
if (contextFiles.length > 0) {
|
|
416
|
+
prompt += "\n\n# Project Context\n\n";
|
|
417
|
+
prompt += "The following project context files have been loaded:\n\n";
|
|
418
|
+
for (const { path: filePath, content } of contextFiles) {
|
|
419
|
+
prompt += `## ${filePath}\n\n${content}\n\n`;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Append git context if in a git repo
|
|
424
|
+
const gitContext = loadGitContext(resolvedCwd);
|
|
425
|
+
if (gitContext) {
|
|
426
|
+
prompt += `\n\n# Git Status\n\n${gitContext}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Append skills section (only if read tool is available)
|
|
430
|
+
if (hasRead && skills.length > 0) {
|
|
431
|
+
prompt += formatSkillsForPrompt(skills);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Add date/time and working directory last
|
|
435
|
+
prompt += `\nCurrent date and time: ${dateTime}`;
|
|
436
|
+
prompt += `\nCurrent working directory: ${resolvedCwd}`;
|
|
437
|
+
|
|
438
|
+
return prompt;
|
|
439
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central timing instrumentation for startup profiling.
|
|
3
|
+
* Enable with PI_TIMING=1 environment variable.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const ENABLED = process.env.PI_TIMING === "1";
|
|
7
|
+
const timings: Array<{ label: string; ms: number }> = [];
|
|
8
|
+
let lastTime = Date.now();
|
|
9
|
+
|
|
10
|
+
export function time(label: string): void {
|
|
11
|
+
if (!ENABLED) return;
|
|
12
|
+
const now = Date.now();
|
|
13
|
+
timings.push({ label, ms: now - lastTime });
|
|
14
|
+
lastTime = now;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function printTimings(): void {
|
|
18
|
+
if (!ENABLED || timings.length === 0) return;
|
|
19
|
+
console.error("\n--- Startup Timings ---");
|
|
20
|
+
for (const t of timings) {
|
|
21
|
+
console.error(` ${t.label}: ${t.ms}ms`);
|
|
22
|
+
}
|
|
23
|
+
console.error(` TOTAL: ${timings.reduce((a, b) => a + b.ms, 0)}ms`);
|
|
24
|
+
console.error("------------------------\n");
|
|
25
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask Tool - Interactive user prompting during execution
|
|
3
|
+
*
|
|
4
|
+
* Use this tool when you need to ask the user questions during execution.
|
|
5
|
+
* This allows you to:
|
|
6
|
+
* 1. Gather user preferences or requirements
|
|
7
|
+
* 2. Clarify ambiguous instructions
|
|
8
|
+
* 3. Get decisions on implementation choices as you work
|
|
9
|
+
* 4. Offer choices to the user about what direction to take
|
|
10
|
+
*
|
|
11
|
+
* Usage notes:
|
|
12
|
+
* - Users will always be able to select "Other" to provide custom text input
|
|
13
|
+
* - Use multi: true to allow multiple answers to be selected for a question
|
|
14
|
+
* - If you recommend a specific option, make that the first option in the list
|
|
15
|
+
* and add "(Recommended)" at the end of the label
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
19
|
+
import { Type } from "@sinclair/typebox";
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Types
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
const OptionItem = Type.Object({
|
|
26
|
+
label: Type.String({ description: "Display label for this option" }),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const askSchema = Type.Object({
|
|
30
|
+
question: Type.String({ description: "The question to ask the user" }),
|
|
31
|
+
options: Type.Array(OptionItem, {
|
|
32
|
+
description: "Available options for the user to choose from.",
|
|
33
|
+
minItems: 1,
|
|
34
|
+
}),
|
|
35
|
+
multi: Type.Optional(
|
|
36
|
+
Type.Boolean({
|
|
37
|
+
description: "Allow multiple options to be selected (default: false)",
|
|
38
|
+
default: false,
|
|
39
|
+
}),
|
|
40
|
+
),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export interface AskToolDetails {
|
|
44
|
+
question: string;
|
|
45
|
+
options: string[];
|
|
46
|
+
multi: boolean;
|
|
47
|
+
selectedOptions: string[];
|
|
48
|
+
customInput?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Constants
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
const OTHER_OPTION = "Other (type your own)";
|
|
56
|
+
const DONE_OPTION = "✓ Done selecting";
|
|
57
|
+
|
|
58
|
+
const DESCRIPTION = `Use this tool when you need to ask the user questions during execution. This allows you to:
|
|
59
|
+
1. Gather user preferences or requirements
|
|
60
|
+
2. Clarify ambiguous instructions
|
|
61
|
+
3. Get decisions on implementation choices as you work
|
|
62
|
+
4. Offer choices to the user about what direction to take.
|
|
63
|
+
|
|
64
|
+
Usage notes:
|
|
65
|
+
- Users will always be able to select "Other" to provide custom text input
|
|
66
|
+
- Use multi: true to allow multiple answers to be selected for a question
|
|
67
|
+
- If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
|
|
68
|
+
|
|
69
|
+
Example usage:
|
|
70
|
+
|
|
71
|
+
<example>
|
|
72
|
+
assistant: Let me ask which features you want to include.
|
|
73
|
+
assistant: Uses the ask tool:
|
|
74
|
+
{
|
|
75
|
+
"question": "Which features should I implement?",
|
|
76
|
+
"options": [
|
|
77
|
+
{"label": "Authentication"},
|
|
78
|
+
{"label": "API endpoints"},
|
|
79
|
+
{"label": "Database models"},
|
|
80
|
+
{"label": "Unit tests"},
|
|
81
|
+
{"label": "Documentation"}
|
|
82
|
+
],
|
|
83
|
+
"multi": true
|
|
84
|
+
}
|
|
85
|
+
</example>`;
|
|
86
|
+
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// Tool Implementation
|
|
89
|
+
// =============================================================================
|
|
90
|
+
|
|
91
|
+
export function createAskTool(_cwd: string): AgentTool<typeof askSchema, AskToolDetails> {
|
|
92
|
+
return {
|
|
93
|
+
name: "ask",
|
|
94
|
+
label: "Ask",
|
|
95
|
+
description: DESCRIPTION,
|
|
96
|
+
parameters: askSchema,
|
|
97
|
+
|
|
98
|
+
async execute(
|
|
99
|
+
_toolCallId: string,
|
|
100
|
+
params: { question: string; options: Array<{ label: string }>; multi?: boolean },
|
|
101
|
+
_signal?: AbortSignal,
|
|
102
|
+
_onUpdate?: AgentToolUpdateCallback<AskToolDetails>,
|
|
103
|
+
context?: AgentToolContext,
|
|
104
|
+
) {
|
|
105
|
+
const { question, options, multi = false } = params;
|
|
106
|
+
const optionLabels = options.map((o) => o.label);
|
|
107
|
+
|
|
108
|
+
// Headless fallback - return error if no UI available
|
|
109
|
+
if (!context?.hasUI || !context.ui) {
|
|
110
|
+
return {
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: "text" as const,
|
|
114
|
+
text: "Error: User prompt requires interactive mode",
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
details: {
|
|
118
|
+
question,
|
|
119
|
+
options: optionLabels,
|
|
120
|
+
multi,
|
|
121
|
+
selectedOptions: [],
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const { ui } = context;
|
|
127
|
+
let selectedOptions: string[] = [];
|
|
128
|
+
let customInput: string | undefined;
|
|
129
|
+
|
|
130
|
+
if (multi) {
|
|
131
|
+
// Multi-select: show checkboxes in the label to indicate selection state
|
|
132
|
+
const selected = new Set<string>();
|
|
133
|
+
|
|
134
|
+
while (true) {
|
|
135
|
+
// Build options with checkbox indicators
|
|
136
|
+
const opts: string[] = [];
|
|
137
|
+
|
|
138
|
+
// Add "Done" option if any selected
|
|
139
|
+
if (selected.size > 0) {
|
|
140
|
+
opts.push(DONE_OPTION);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Add all options with [X] or [ ] prefix
|
|
144
|
+
for (const opt of optionLabels) {
|
|
145
|
+
const checkbox = selected.has(opt) ? "[X]" : "[ ]";
|
|
146
|
+
opts.push(`${checkbox} ${opt}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Add "Other" option
|
|
150
|
+
opts.push(OTHER_OPTION);
|
|
151
|
+
|
|
152
|
+
const prefix = selected.size > 0 ? `(${selected.size} selected) ` : "";
|
|
153
|
+
const choice = await ui.select(`${prefix}${question}`, opts);
|
|
154
|
+
|
|
155
|
+
if (choice === undefined || choice === DONE_OPTION) break;
|
|
156
|
+
|
|
157
|
+
if (choice === OTHER_OPTION) {
|
|
158
|
+
const input = await ui.input("Enter your response:");
|
|
159
|
+
if (input) customInput = input;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Toggle selection - extract the actual option name
|
|
164
|
+
const optMatch = choice.match(/^\[.\] (.+)$/);
|
|
165
|
+
if (optMatch) {
|
|
166
|
+
const opt = optMatch[1];
|
|
167
|
+
if (selected.has(opt)) {
|
|
168
|
+
selected.delete(opt);
|
|
169
|
+
} else {
|
|
170
|
+
selected.add(opt);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
selectedOptions = Array.from(selected);
|
|
175
|
+
} else {
|
|
176
|
+
// Single select with "Other" option
|
|
177
|
+
const choice = await ui.select(question, [...optionLabels, OTHER_OPTION]);
|
|
178
|
+
if (choice === OTHER_OPTION) {
|
|
179
|
+
const input = await ui.input("Enter your response:");
|
|
180
|
+
if (input) customInput = input;
|
|
181
|
+
} else if (choice) {
|
|
182
|
+
selectedOptions = [choice];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const details: AskToolDetails = {
|
|
187
|
+
question,
|
|
188
|
+
options: optionLabels,
|
|
189
|
+
multi,
|
|
190
|
+
selectedOptions,
|
|
191
|
+
customInput,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
let responseText: string;
|
|
195
|
+
if (customInput) {
|
|
196
|
+
responseText = `User provided custom input: ${customInput}`;
|
|
197
|
+
} else if (selectedOptions.length > 0) {
|
|
198
|
+
responseText = multi
|
|
199
|
+
? `User selected: ${selectedOptions.join(", ")}`
|
|
200
|
+
: `User selected: ${selectedOptions[0]}`;
|
|
201
|
+
} else {
|
|
202
|
+
responseText = "User cancelled the selection";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { content: [{ type: "text" as const, text: responseText }], details };
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Default ask tool using process.cwd() - for backwards compatibility (no UI) */
|
|
211
|
+
export const askTool = createAskTool(process.cwd());
|