@oh-my-pi/pi-coding-agent 13.2.0 → 13.3.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 +54 -1
- package/package.json +7 -7
- package/scripts/format-prompts.ts +33 -14
- package/scripts/generate-docs-index.ts +2 -2
- package/src/capability/index.ts +1 -2
- package/src/cli/args.ts +3 -3
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -2
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/jupyter-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +1 -2
- package/src/cli/update-cli.ts +1 -2
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/launch.ts +2 -1
- package/src/commit/agentic/agent.ts +2 -1
- package/src/commit/agentic/index.ts +1 -2
- package/src/commit/agentic/prompts/system.md +3 -3
- package/src/commit/agentic/tools/propose-changelog.ts +30 -19
- package/src/commit/changelog/generate.ts +16 -6
- package/src/commit/changelog/index.ts +2 -1
- package/src/commit/pipeline.ts +1 -2
- package/src/commit/prompts/reduce-system.md +1 -1
- package/src/commit/types.ts +10 -1
- package/src/config/keybindings.ts +1 -2
- package/src/config/model-registry.ts +1 -1
- package/src/config/prompt-templates.ts +14 -2
- package/src/config/settings-schema.ts +36 -4
- package/src/config/settings.ts +19 -2
- package/src/config.ts +1 -2
- package/src/debug/index.ts +1 -1
- package/src/debug/report-bundle.ts +1 -2
- package/src/debug/system-info.ts +1 -2
- package/src/discovery/agents.ts +2 -2
- package/src/discovery/builtin.ts +8 -9
- package/src/discovery/claude-plugins.ts +2 -2
- package/src/discovery/claude.ts +30 -12
- package/src/discovery/codex.ts +3 -3
- package/src/discovery/cursor.ts +5 -4
- package/src/discovery/gemini.ts +5 -5
- package/src/discovery/helpers.ts +47 -69
- package/src/discovery/mcp-json.ts +3 -3
- package/src/discovery/opencode.ts +7 -8
- package/src/discovery/ssh.ts +3 -3
- package/src/discovery/vscode.ts +3 -2
- package/src/discovery/windsurf.ts +3 -2
- package/src/exa/company.ts +1 -1
- package/src/exa/factory.ts +1 -6
- package/src/exa/linkedin.ts +1 -1
- package/src/exa/mcp-client.ts +19 -8
- package/src/exa/search.ts +2 -2
- package/src/exa/types.ts +3 -3
- package/src/exec/bash-executor.ts +2 -1
- package/src/exec/non-interactive-env.ts +43 -0
- package/src/export/custom-share.ts +1 -1
- package/src/export/html/index.ts +1 -2
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/plugins/installer.ts +1 -2
- package/src/extensibility/plugins/loader.ts +1 -2
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/skills.ts +59 -115
- package/src/index.ts +1 -3
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/ipy/executor.ts +1 -2
- package/src/ipy/gateway-coordinator.ts +1 -2
- package/src/ipy/modules.ts +1 -1
- package/src/ipy/runtime.ts +2 -3
- package/src/main.ts +1 -2
- package/src/mcp/config.ts +2 -2
- package/src/mcp/transports/stdio.ts +1 -2
- package/src/memories/index.ts +1 -2
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +8 -2
- package/src/modes/components/footer.ts +1 -2
- package/src/modes/components/settings-defs.ts +17 -1
- package/src/modes/components/status-line/segments.ts +1 -2
- package/src/modes/components/status-line.ts +7 -5
- package/src/modes/components/tool-execution.ts +3 -10
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +1 -2
- package/src/modes/controllers/mcp-command-controller.ts +5 -4
- package/src/modes/controllers/selector-controller.ts +22 -1
- package/src/modes/controllers/ssh-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +11 -3
- package/src/modes/oauth-manual-input.ts +42 -0
- package/src/modes/shared.ts +1 -2
- package/src/modes/theme/theme.ts +1 -2
- package/src/modes/types.ts +2 -0
- package/src/patch/hashline.ts +19 -1
- package/src/patch/index.ts +1 -25
- package/src/prompts/agents/designer.md +7 -10
- package/src/prompts/agents/explore.md +15 -23
- package/src/prompts/agents/init.md +23 -23
- package/src/prompts/agents/plan.md +14 -77
- package/src/prompts/agents/reviewer.md +6 -5
- package/src/prompts/agents/task.md +13 -11
- package/src/prompts/compaction/branch-summary.md +3 -3
- package/src/prompts/compaction/compaction-short-summary.md +7 -7
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +5 -5
- package/src/prompts/compaction/compaction-turn-prefix.md +3 -3
- package/src/prompts/compaction/compaction-update-summary.md +11 -11
- package/src/prompts/memories/consolidation.md +5 -5
- package/src/prompts/memories/read-path.md +6 -6
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +5 -5
- package/src/prompts/review-request.md +4 -4
- package/src/prompts/system/agent-creation-architect.md +17 -17
- package/src/prompts/system/agent-creation-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +2 -0
- package/src/prompts/system/custom-system-prompt.md +4 -4
- package/src/prompts/system/plan-mode-active.md +20 -20
- package/src/prompts/system/plan-mode-approved.md +7 -7
- package/src/prompts/system/plan-mode-reference.md +2 -2
- package/src/prompts/system/plan-mode-subagent.md +8 -8
- package/src/prompts/system/subagent-submit-reminder.md +5 -5
- package/src/prompts/system/subagent-system-prompt.md +29 -22
- package/src/prompts/system/subagent-user-prompt.md +7 -3
- package/src/prompts/system/summarization-system.md +1 -1
- package/src/prompts/system/system-prompt.md +214 -226
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/system/web-search.md +16 -16
- package/src/prompts/tools/ask.md +1 -3
- package/src/prompts/tools/await.md +2 -4
- package/src/prompts/tools/bash.md +5 -7
- package/src/prompts/tools/browser.md +4 -6
- package/src/prompts/tools/calculator.md +1 -3
- package/src/prompts/tools/cancel-job.md +2 -4
- package/src/prompts/tools/exit-plan-mode.md +7 -7
- package/src/prompts/tools/fetch.md +0 -2
- package/src/prompts/tools/find.md +3 -5
- package/src/prompts/tools/gemini-image.md +6 -22
- package/src/prompts/tools/grep.md +4 -6
- package/src/prompts/tools/hashline.md +56 -15
- package/src/prompts/tools/lsp.md +1 -3
- package/src/prompts/tools/patch.md +7 -9
- package/src/prompts/tools/python.md +10 -14
- package/src/prompts/tools/read.md +0 -2
- package/src/prompts/tools/replace.md +5 -7
- package/src/prompts/tools/ssh.md +3 -5
- package/src/prompts/tools/task-summary.md +4 -4
- package/src/prompts/tools/task.md +7 -9
- package/src/prompts/tools/todo-write.md +7 -9
- package/src/prompts/tools/web-search.md +3 -5
- package/src/prompts/tools/write.md +3 -5
- package/src/sdk.ts +4 -2
- package/src/session/agent-session.ts +10 -26
- package/src/session/agent-storage.ts +1 -2
- package/src/session/history-storage.ts +1 -2
- package/src/session/session-manager.ts +10 -2
- package/src/slash-commands/builtin-registry.ts +26 -1
- package/src/ssh/connection-manager.ts +11 -2
- package/src/ssh/sshfs-mount.ts +7 -1
- package/src/system-prompt.ts +29 -103
- package/src/task/agents.ts +1 -1
- package/src/task/index.ts +211 -70
- package/src/task/render.ts +24 -8
- package/src/task/types.ts +6 -1
- package/src/task/worktree.ts +394 -32
- package/src/tools/ask.ts +0 -1
- package/src/tools/bash-interactive.ts +2 -45
- package/src/tools/bash.ts +5 -5
- package/src/tools/browser.ts +1 -2
- package/src/tools/gemini-image.ts +8 -28
- package/src/tools/json-tree.ts +2 -1
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +1 -2
- package/src/tools/submit-result.ts +22 -23
- package/src/utils/commit-message-generator.ts +132 -0
- package/src/utils/tools-manager.ts +1 -2
- package/src/web/scrapers/artifacthub.ts +2 -1
- package/src/web/scrapers/aur.ts +2 -1
- package/src/web/scrapers/biorxiv.ts +2 -1
- package/src/web/scrapers/bluesky.ts +2 -1
- package/src/web/scrapers/chocolatey.ts +2 -1
- package/src/web/scrapers/cisa-kev.ts +2 -1
- package/src/web/scrapers/clojars.ts +2 -1
- package/src/web/scrapers/coingecko.ts +2 -1
- package/src/web/scrapers/crates-io.ts +2 -1
- package/src/web/scrapers/crossref.ts +2 -1
- package/src/web/scrapers/discogs.ts +3 -1
- package/src/web/scrapers/discourse.ts +2 -1
- package/src/web/scrapers/dockerhub.ts +2 -1
- package/src/web/scrapers/fdroid.ts +2 -1
- package/src/web/scrapers/firefox-addons.ts +2 -1
- package/src/web/scrapers/flathub.ts +2 -1
- package/src/web/scrapers/gitlab.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +2 -1
- package/src/web/scrapers/hackage.ts +2 -1
- package/src/web/scrapers/hackernews.ts +2 -1
- package/src/web/scrapers/hex.ts +2 -1
- package/src/web/scrapers/huggingface.ts +2 -1
- package/src/web/scrapers/jetbrains-marketplace.ts +2 -1
- package/src/web/scrapers/lemmy.ts +2 -1
- package/src/web/scrapers/lobsters.ts +2 -1
- package/src/web/scrapers/mastodon.ts +2 -1
- package/src/web/scrapers/maven.ts +2 -1
- package/src/web/scrapers/mdn.ts +2 -1
- package/src/web/scrapers/metacpan.ts +2 -1
- package/src/web/scrapers/musicbrainz.ts +3 -1
- package/src/web/scrapers/npm.ts +2 -1
- package/src/web/scrapers/nuget.ts +2 -1
- package/src/web/scrapers/nvd.ts +2 -1
- package/src/web/scrapers/ollama.ts +2 -1
- package/src/web/scrapers/open-vsx.ts +2 -1
- package/src/web/scrapers/opencorporates.ts +2 -1
- package/src/web/scrapers/openlibrary.ts +2 -1
- package/src/web/scrapers/orcid.ts +3 -1
- package/src/web/scrapers/osv.ts +2 -1
- package/src/web/scrapers/packagist.ts +2 -1
- package/src/web/scrapers/pub-dev.ts +2 -1
- package/src/web/scrapers/pubmed.ts +2 -1
- package/src/web/scrapers/pypi.ts +2 -1
- package/src/web/scrapers/rawg.ts +2 -8
- package/src/web/scrapers/reddit.ts +2 -1
- package/src/web/scrapers/repology.ts +2 -1
- package/src/web/scrapers/rfc.ts +2 -1
- package/src/web/scrapers/rubygems.ts +2 -1
- package/src/web/scrapers/searchcode.ts +2 -1
- package/src/web/scrapers/sec-edgar.ts +2 -1
- package/src/web/scrapers/semantic-scholar.ts +2 -1
- package/src/web/scrapers/snapcraft.ts +2 -1
- package/src/web/scrapers/sourcegraph.ts +2 -1
- package/src/web/scrapers/spdx.ts +2 -1
- package/src/web/scrapers/stackoverflow.ts +2 -1
- package/src/web/scrapers/terraform.ts +2 -1
- package/src/web/scrapers/types.ts +0 -11
- package/src/web/scrapers/vimeo.ts +2 -1
- package/src/web/scrapers/vscode-marketplace.ts +2 -1
- package/src/web/scrapers/w3c.ts +2 -1
- package/src/web/scrapers/wikidata.ts +2 -1
- package/src/web/search/index.ts +10 -14
- package/src/web/search/provider.ts +2 -2
- package/src/web/search/providers/codex.ts +1 -2
- package/src/web/search/providers/exa.ts +42 -10
- package/src/web/search/providers/gemini.ts +1 -1
- package/src/web/search/providers/perplexity.ts +20 -9
- package/src/web/search/providers/utils.ts +1 -1
package/src/task/worktree.ts
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
|
+
import type { Dirent } from "node:fs";
|
|
1
2
|
import * as fs from "node:fs/promises";
|
|
2
3
|
import * as os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import { isEnoent, Snowflake } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import { getWorktreeDir } from "@oh-my-pi/pi-utils/dirs";
|
|
5
|
+
import { getWorktreeDir, isEnoent, logger, Snowflake } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import { $ } from "bun";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/** Baseline state for a single git repository. */
|
|
9
|
+
export interface RepoBaseline {
|
|
9
10
|
repoRoot: string;
|
|
11
|
+
headCommit: string;
|
|
10
12
|
staged: string;
|
|
11
13
|
unstaged: string;
|
|
12
14
|
untracked: string[];
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
/** Baseline state for the project, including any nested git repos. */
|
|
18
|
+
export interface WorktreeBaseline {
|
|
19
|
+
root: RepoBaseline;
|
|
20
|
+
/** Nested git repos (path relative to root.repoRoot). */
|
|
21
|
+
nested: Array<{ relativePath: string; baseline: RepoBaseline }>;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
export function getEncodedProjectName(cwd: string): string {
|
|
16
25
|
return `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
17
26
|
}
|
|
@@ -39,7 +48,56 @@ export async function ensureWorktree(baseCwd: string, id: string): Promise<strin
|
|
|
39
48
|
return worktreeDir;
|
|
40
49
|
}
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
/** Find nested git repositories (non-submodule) under the given root. */
|
|
52
|
+
async function discoverNestedRepos(repoRoot: string): Promise<string[]> {
|
|
53
|
+
// Get submodule paths so we can exclude them
|
|
54
|
+
const submoduleRaw = await $`git submodule --quiet foreach --recursive 'echo $sm_path'`
|
|
55
|
+
.cwd(repoRoot)
|
|
56
|
+
.quiet()
|
|
57
|
+
.nothrow()
|
|
58
|
+
.text();
|
|
59
|
+
const submodulePaths = new Set(
|
|
60
|
+
submoduleRaw
|
|
61
|
+
.split("\n")
|
|
62
|
+
.map(l => l.trim())
|
|
63
|
+
.filter(Boolean),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Find all .git dirs/files that aren't the root or known submodules
|
|
67
|
+
const result: string[] = [];
|
|
68
|
+
async function walk(dir: string): Promise<void> {
|
|
69
|
+
let entries: Dirent[];
|
|
70
|
+
try {
|
|
71
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
72
|
+
} catch {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
77
|
+
if (!entry.isDirectory()) continue;
|
|
78
|
+
const full = path.join(dir, entry.name);
|
|
79
|
+
const rel = path.relative(repoRoot, full);
|
|
80
|
+
// Check if this directory is itself a git repo
|
|
81
|
+
const gitDir = path.join(full, ".git");
|
|
82
|
+
let hasGit = false;
|
|
83
|
+
try {
|
|
84
|
+
await fs.access(gitDir);
|
|
85
|
+
hasGit = true;
|
|
86
|
+
} catch {}
|
|
87
|
+
if (hasGit && !submodulePaths.has(rel)) {
|
|
88
|
+
result.push(rel);
|
|
89
|
+
// Don't recurse into nested repos — they manage their own tree
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
await walk(full);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
await walk(repoRoot);
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function captureRepoBaseline(repoRoot: string): Promise<RepoBaseline> {
|
|
100
|
+
const headCommit = (await $`git rev-parse HEAD`.cwd(repoRoot).quiet().text()).trim();
|
|
43
101
|
const staged = await $`git diff --cached --binary`.cwd(repoRoot).quiet().text();
|
|
44
102
|
const unstaged = await $`git diff --binary`.cwd(repoRoot).quiet().text();
|
|
45
103
|
const untrackedRaw = await $`git ls-files --others --exclude-standard`.cwd(repoRoot).quiet().text();
|
|
@@ -47,13 +105,18 @@ export async function captureBaseline(repoRoot: string): Promise<WorktreeBaselin
|
|
|
47
105
|
.split("\n")
|
|
48
106
|
.map(line => line.trim())
|
|
49
107
|
.filter(line => line.length > 0);
|
|
108
|
+
return { repoRoot, headCommit, staged, unstaged, untracked };
|
|
109
|
+
}
|
|
50
110
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
111
|
+
export async function captureBaseline(repoRoot: string): Promise<WorktreeBaseline> {
|
|
112
|
+
const [root, nestedPaths] = await Promise.all([captureRepoBaseline(repoRoot), discoverNestedRepos(repoRoot)]);
|
|
113
|
+
const nested = await Promise.all(
|
|
114
|
+
nestedPaths.map(async relativePath => ({
|
|
115
|
+
relativePath,
|
|
116
|
+
baseline: await captureRepoBaseline(path.join(repoRoot, relativePath)),
|
|
117
|
+
})),
|
|
118
|
+
);
|
|
119
|
+
return { root, nested };
|
|
57
120
|
}
|
|
58
121
|
|
|
59
122
|
async function writeTempPatchFile(patch: string): Promise<string> {
|
|
@@ -81,13 +144,13 @@ async function applyPatch(
|
|
|
81
144
|
}
|
|
82
145
|
}
|
|
83
146
|
|
|
84
|
-
|
|
85
|
-
await applyPatch(worktreeDir,
|
|
86
|
-
await applyPatch(worktreeDir,
|
|
87
|
-
await applyPatch(worktreeDir,
|
|
147
|
+
async function applyRepoBaseline(worktreeDir: string, rb: RepoBaseline, sourceRoot: string): Promise<void> {
|
|
148
|
+
await applyPatch(worktreeDir, rb.staged, { cached: true });
|
|
149
|
+
await applyPatch(worktreeDir, rb.staged);
|
|
150
|
+
await applyPatch(worktreeDir, rb.unstaged);
|
|
88
151
|
|
|
89
|
-
for (const entry of
|
|
90
|
-
const source = path.join(
|
|
152
|
+
for (const entry of rb.untracked) {
|
|
153
|
+
const source = path.join(sourceRoot, entry);
|
|
91
154
|
const destination = path.join(worktreeDir, entry);
|
|
92
155
|
try {
|
|
93
156
|
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
@@ -99,6 +162,39 @@ export async function applyBaseline(worktreeDir: string, baseline: WorktreeBasel
|
|
|
99
162
|
}
|
|
100
163
|
}
|
|
101
164
|
|
|
165
|
+
export async function applyBaseline(worktreeDir: string, baseline: WorktreeBaseline): Promise<void> {
|
|
166
|
+
await applyRepoBaseline(worktreeDir, baseline.root, baseline.root.repoRoot);
|
|
167
|
+
|
|
168
|
+
// Restore nested repos into the worktree
|
|
169
|
+
for (const entry of baseline.nested) {
|
|
170
|
+
const nestedDir = path.join(worktreeDir, entry.relativePath);
|
|
171
|
+
// Copy the nested repo wholesale (it's not managed by root git)
|
|
172
|
+
const sourceDir = path.join(baseline.root.repoRoot, entry.relativePath);
|
|
173
|
+
try {
|
|
174
|
+
await fs.cp(sourceDir, nestedDir, { recursive: true });
|
|
175
|
+
} catch (err) {
|
|
176
|
+
if (isEnoent(err)) continue;
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
// Apply any uncommitted changes from the nested baseline
|
|
180
|
+
await applyRepoBaseline(nestedDir, entry.baseline, entry.baseline.repoRoot);
|
|
181
|
+
// Commit baseline state so captureRepoDeltaPatch can cleanly subtract it.
|
|
182
|
+
// Without this, `git add -A && git commit` by the task would include
|
|
183
|
+
// baseline untracked files in the diff-tree output.
|
|
184
|
+
const hasChanges = (await $`git status --porcelain`.cwd(nestedDir).quiet().nothrow().text()).trim();
|
|
185
|
+
if (hasChanges) {
|
|
186
|
+
await $`git add -A`.cwd(nestedDir).quiet();
|
|
187
|
+
await $`git commit -m omp-baseline --allow-empty`.cwd(nestedDir).quiet();
|
|
188
|
+
// Update baseline to reflect the committed state — prevents double-apply
|
|
189
|
+
// in captureRepoDeltaPatch's temp-index path
|
|
190
|
+
entry.baseline.headCommit = (await $`git rev-parse HEAD`.cwd(nestedDir).quiet().text()).trim();
|
|
191
|
+
entry.baseline.staged = "";
|
|
192
|
+
entry.baseline.unstaged = "";
|
|
193
|
+
entry.baseline.untracked = [];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
102
198
|
async function applyPatchToIndex(cwd: string, patch: string, indexFile: string): Promise<void> {
|
|
103
199
|
if (!patch.trim()) return;
|
|
104
200
|
const tempPath = await writeTempPatchFile(patch);
|
|
@@ -122,31 +218,62 @@ async function listUntracked(cwd: string): Promise<string[]> {
|
|
|
122
218
|
.filter(line => line.length > 0);
|
|
123
219
|
}
|
|
124
220
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
})
|
|
221
|
+
async function captureRepoDeltaPatch(repoDir: string, rb: RepoBaseline): Promise<string> {
|
|
222
|
+
// Check if HEAD advanced (task committed changes)
|
|
223
|
+
const currentHead = (await $`git rev-parse HEAD`.cwd(repoDir).quiet().nothrow().text()).trim();
|
|
224
|
+
const headAdvanced = currentHead && currentHead !== rb.headCommit;
|
|
225
|
+
|
|
226
|
+
if (headAdvanced) {
|
|
227
|
+
// HEAD moved: use diff-tree to capture committed changes, plus any uncommitted on top
|
|
228
|
+
const parts: string[] = [];
|
|
229
|
+
|
|
230
|
+
// Committed changes since baseline
|
|
231
|
+
const committedDiff = await $`git diff-tree -r -p --binary ${rb.headCommit} ${currentHead}`
|
|
232
|
+
.cwd(repoDir)
|
|
138
233
|
.quiet()
|
|
234
|
+
.nothrow()
|
|
139
235
|
.text();
|
|
236
|
+
if (committedDiff.trim()) parts.push(committedDiff);
|
|
237
|
+
|
|
238
|
+
// Uncommitted changes on top of the new HEAD
|
|
239
|
+
const staged = await $`git diff --cached --binary`.cwd(repoDir).quiet().text();
|
|
240
|
+
const unstaged = await $`git diff --binary`.cwd(repoDir).quiet().text();
|
|
241
|
+
if (staged.trim()) parts.push(staged);
|
|
242
|
+
if (unstaged.trim()) parts.push(unstaged);
|
|
243
|
+
|
|
244
|
+
// New untracked files (relative to both baseline and current tracking)
|
|
245
|
+
const currentUntracked = await listUntracked(repoDir);
|
|
246
|
+
const baselineUntracked = new Set(rb.untracked);
|
|
247
|
+
const newUntracked = currentUntracked.filter(entry => !baselineUntracked.has(entry));
|
|
248
|
+
if (newUntracked.length > 0) {
|
|
249
|
+
const untrackedDiffs = await Promise.all(
|
|
250
|
+
newUntracked.map(entry =>
|
|
251
|
+
$`git diff --binary --no-index /dev/null ${entry}`.cwd(repoDir).quiet().nothrow().text(),
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
parts.push(...untrackedDiffs.filter(d => d.trim()));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return parts.join("\n");
|
|
258
|
+
}
|
|
140
259
|
|
|
141
|
-
|
|
142
|
-
|
|
260
|
+
// HEAD unchanged: use temp index approach (subtracts baseline from delta)
|
|
261
|
+
const tempIndex = path.join(os.tmpdir(), `omp-task-index-${Snowflake.next()}`);
|
|
262
|
+
try {
|
|
263
|
+
await $`git read-tree ${rb.headCommit}`.cwd(repoDir).env({ GIT_INDEX_FILE: tempIndex });
|
|
264
|
+
await applyPatchToIndex(repoDir, rb.staged, tempIndex);
|
|
265
|
+
await applyPatchToIndex(repoDir, rb.unstaged, tempIndex);
|
|
266
|
+
const diff = await $`git diff --binary`.cwd(repoDir).env({ GIT_INDEX_FILE: tempIndex }).quiet().text();
|
|
267
|
+
|
|
268
|
+
const currentUntracked = await listUntracked(repoDir);
|
|
269
|
+
const baselineUntracked = new Set(rb.untracked);
|
|
143
270
|
const newUntracked = currentUntracked.filter(entry => !baselineUntracked.has(entry));
|
|
144
271
|
|
|
145
272
|
if (newUntracked.length === 0) return diff;
|
|
146
273
|
|
|
147
274
|
const untrackedDiffs = await Promise.all(
|
|
148
275
|
newUntracked.map(entry =>
|
|
149
|
-
$`git diff --binary --no-index /dev/null ${entry}`.cwd(
|
|
276
|
+
$`git diff --binary --no-index /dev/null ${entry}`.cwd(repoDir).quiet().nothrow().text(),
|
|
150
277
|
),
|
|
151
278
|
);
|
|
152
279
|
return `${diff}${diff && !diff.endsWith("\n") ? "\n" : ""}${untrackedDiffs.join("\n")}`;
|
|
@@ -155,6 +282,76 @@ export async function captureDeltaPatch(worktreeDir: string, baseline: WorktreeB
|
|
|
155
282
|
}
|
|
156
283
|
}
|
|
157
284
|
|
|
285
|
+
export interface NestedRepoPatch {
|
|
286
|
+
relativePath: string;
|
|
287
|
+
patch: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export interface DeltaPatchResult {
|
|
291
|
+
rootPatch: string;
|
|
292
|
+
nestedPatches: NestedRepoPatch[];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export async function captureDeltaPatch(isolationDir: string, baseline: WorktreeBaseline): Promise<DeltaPatchResult> {
|
|
296
|
+
const rootPatch = await captureRepoDeltaPatch(isolationDir, baseline.root);
|
|
297
|
+
const nestedPatches: NestedRepoPatch[] = [];
|
|
298
|
+
|
|
299
|
+
for (const { relativePath, baseline: nb } of baseline.nested) {
|
|
300
|
+
const nestedDir = path.join(isolationDir, relativePath);
|
|
301
|
+
try {
|
|
302
|
+
await fs.access(path.join(nestedDir, ".git"));
|
|
303
|
+
} catch {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const patch = await captureRepoDeltaPatch(nestedDir, nb);
|
|
307
|
+
if (patch.trim()) nestedPatches.push({ relativePath, patch });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return { rootPatch, nestedPatches };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Apply nested repo patches directly to their working directories after parent merge.
|
|
315
|
+
* @param commitMessage Optional async function to generate a commit message from the combined diff.
|
|
316
|
+
* If omitted or returns null, falls back to a generic message.
|
|
317
|
+
*/
|
|
318
|
+
export async function applyNestedPatches(
|
|
319
|
+
repoRoot: string,
|
|
320
|
+
patches: NestedRepoPatch[],
|
|
321
|
+
commitMessage?: (diff: string) => Promise<string | null>,
|
|
322
|
+
): Promise<void> {
|
|
323
|
+
// Group patches by target repo to apply all at once and commit
|
|
324
|
+
const byRepo = new Map<string, NestedRepoPatch[]>();
|
|
325
|
+
for (const p of patches) {
|
|
326
|
+
if (!p.patch.trim()) continue;
|
|
327
|
+
const group = byRepo.get(p.relativePath) ?? [];
|
|
328
|
+
group.push(p);
|
|
329
|
+
byRepo.set(p.relativePath, group);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
for (const [relativePath, repoPatches] of byRepo) {
|
|
333
|
+
const nestedDir = path.join(repoRoot, relativePath);
|
|
334
|
+
try {
|
|
335
|
+
await fs.access(path.join(nestedDir, ".git"));
|
|
336
|
+
} catch {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const combinedDiff = repoPatches.map(p => p.patch).join("\n");
|
|
341
|
+
for (const { patch } of repoPatches) {
|
|
342
|
+
await applyPatch(nestedDir, patch);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Commit so nested repo history reflects the task changes
|
|
346
|
+
const hasChanges = (await $`git status --porcelain`.cwd(nestedDir).quiet().nothrow().text()).trim();
|
|
347
|
+
if (hasChanges) {
|
|
348
|
+
const msg = (await commitMessage?.(combinedDiff)) ?? "changes from isolated task(s)";
|
|
349
|
+
await $`git add -A`.cwd(nestedDir).quiet();
|
|
350
|
+
await $`git commit -m ${msg}`.cwd(nestedDir).quiet();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
158
355
|
export async function cleanupWorktree(dir: string): Promise<void> {
|
|
159
356
|
try {
|
|
160
357
|
const commonDirRaw = await $`git rev-parse --git-common-dir`.cwd(dir).quiet().nothrow().text();
|
|
@@ -168,3 +365,168 @@ export async function cleanupWorktree(dir: string): Promise<void> {
|
|
|
168
365
|
await fs.rm(dir, { recursive: true, force: true });
|
|
169
366
|
}
|
|
170
367
|
}
|
|
368
|
+
|
|
369
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
370
|
+
// Fuse-overlay isolation
|
|
371
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
372
|
+
|
|
373
|
+
export async function ensureFuseOverlay(baseCwd: string, id: string): Promise<string> {
|
|
374
|
+
const repoRoot = await getRepoRoot(baseCwd);
|
|
375
|
+
const encodedProject = getEncodedProjectName(repoRoot);
|
|
376
|
+
const baseDir = getWorktreeDir(encodedProject, id);
|
|
377
|
+
const upperDir = path.join(baseDir, "upper");
|
|
378
|
+
const workDir = path.join(baseDir, "work");
|
|
379
|
+
const mergedDir = path.join(baseDir, "merged");
|
|
380
|
+
|
|
381
|
+
// Clean up any stale mount at this path
|
|
382
|
+
const fusermount = Bun.which("fusermount3") ?? Bun.which("fusermount");
|
|
383
|
+
if (fusermount) {
|
|
384
|
+
await $`${fusermount} -u ${mergedDir}`.quiet().nothrow();
|
|
385
|
+
}
|
|
386
|
+
await fs.rm(baseDir, { recursive: true, force: true });
|
|
387
|
+
|
|
388
|
+
await fs.mkdir(upperDir, { recursive: true });
|
|
389
|
+
await fs.mkdir(workDir, { recursive: true });
|
|
390
|
+
await fs.mkdir(mergedDir, { recursive: true });
|
|
391
|
+
|
|
392
|
+
const binary = Bun.which("fuse-overlayfs");
|
|
393
|
+
if (!binary) {
|
|
394
|
+
await fs.rm(baseDir, { recursive: true, force: true });
|
|
395
|
+
throw new Error(
|
|
396
|
+
"fuse-overlayfs not found. Install it (e.g. `apt install fuse-overlayfs` or `pacman -S fuse-overlayfs`) to use fuse-overlay isolation.",
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const result = await $`${binary} -o lowerdir=${repoRoot},upperdir=${upperDir},workdir=${workDir} ${mergedDir}`
|
|
401
|
+
.quiet()
|
|
402
|
+
.nothrow();
|
|
403
|
+
if (result.exitCode !== 0) {
|
|
404
|
+
const stderr = result.stderr.toString().trim();
|
|
405
|
+
await fs.rm(baseDir, { recursive: true, force: true });
|
|
406
|
+
throw new Error(`fuse-overlayfs mount failed (exit ${result.exitCode}): ${stderr}`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return mergedDir;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export async function cleanupFuseOverlay(mergedDir: string): Promise<void> {
|
|
413
|
+
try {
|
|
414
|
+
const fusermount = Bun.which("fusermount3") ?? Bun.which("fusermount");
|
|
415
|
+
if (fusermount) {
|
|
416
|
+
await $`${fusermount} -u ${mergedDir}`.quiet().nothrow();
|
|
417
|
+
}
|
|
418
|
+
} finally {
|
|
419
|
+
// baseDir is the parent of the merged directory
|
|
420
|
+
const baseDir = path.dirname(mergedDir);
|
|
421
|
+
await fs.rm(baseDir, { recursive: true, force: true });
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
426
|
+
// Branch-mode isolation
|
|
427
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
428
|
+
|
|
429
|
+
export interface CommitToBranchResult {
|
|
430
|
+
branchName?: string;
|
|
431
|
+
nestedPatches: NestedRepoPatch[];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Commit task-only changes to a new branch.
|
|
436
|
+
* Only root repo changes go on the branch. Nested repo patches are returned
|
|
437
|
+
* separately since the parent git can't track files inside gitlinks.
|
|
438
|
+
*/
|
|
439
|
+
export async function commitToBranch(
|
|
440
|
+
isolationDir: string,
|
|
441
|
+
baseline: WorktreeBaseline,
|
|
442
|
+
taskId: string,
|
|
443
|
+
description: string | undefined,
|
|
444
|
+
commitMessage?: (diff: string) => Promise<string | null>,
|
|
445
|
+
): Promise<CommitToBranchResult | null> {
|
|
446
|
+
const { rootPatch, nestedPatches } = await captureDeltaPatch(isolationDir, baseline);
|
|
447
|
+
if (!rootPatch.trim() && nestedPatches.length === 0) return null;
|
|
448
|
+
|
|
449
|
+
const repoRoot = baseline.root.repoRoot;
|
|
450
|
+
const branchName = `omp/task/${taskId}`;
|
|
451
|
+
const fallbackMessage = description || taskId;
|
|
452
|
+
|
|
453
|
+
// Only create a branch if the root repo has changes
|
|
454
|
+
if (rootPatch.trim()) {
|
|
455
|
+
await $`git branch ${branchName} HEAD`.cwd(repoRoot).quiet();
|
|
456
|
+
const tmpDir = path.join(os.tmpdir(), `omp-branch-${Snowflake.next()}`);
|
|
457
|
+
try {
|
|
458
|
+
await $`git worktree add ${tmpDir} ${branchName}`.cwd(repoRoot).quiet();
|
|
459
|
+
const patchPath = path.join(os.tmpdir(), `omp-branch-patch-${Snowflake.next()}.patch`);
|
|
460
|
+
try {
|
|
461
|
+
await Bun.write(patchPath, rootPatch);
|
|
462
|
+
const applyResult = await $`git apply --binary ${patchPath}`.cwd(tmpDir).quiet().nothrow();
|
|
463
|
+
if (applyResult.exitCode !== 0) {
|
|
464
|
+
const stderr = applyResult.stderr.toString().slice(0, 2000);
|
|
465
|
+
logger.error("commitToBranch: git apply failed", {
|
|
466
|
+
taskId,
|
|
467
|
+
exitCode: applyResult.exitCode,
|
|
468
|
+
stderr,
|
|
469
|
+
patchSize: rootPatch.length,
|
|
470
|
+
patchHead: rootPatch.slice(0, 500),
|
|
471
|
+
});
|
|
472
|
+
throw new Error(`git apply failed for task ${taskId}: ${stderr}`);
|
|
473
|
+
}
|
|
474
|
+
} finally {
|
|
475
|
+
await fs.rm(patchPath, { force: true });
|
|
476
|
+
}
|
|
477
|
+
await $`git add -A`.cwd(tmpDir).quiet();
|
|
478
|
+
const msg = (commitMessage && (await commitMessage(rootPatch))) || fallbackMessage;
|
|
479
|
+
await $`git commit -m ${msg}`.cwd(tmpDir).quiet();
|
|
480
|
+
} finally {
|
|
481
|
+
await $`git worktree remove -f ${tmpDir}`.cwd(repoRoot).quiet().nothrow();
|
|
482
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return { branchName: rootPatch.trim() ? branchName : undefined, nestedPatches };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export interface MergeBranchResult {
|
|
490
|
+
merged: string[];
|
|
491
|
+
failed: string[];
|
|
492
|
+
conflict?: string;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Cherry-pick task branch commits sequentially onto HEAD.
|
|
497
|
+
* Each branch has a single commit that gets replayed cleanly.
|
|
498
|
+
* Stops on first conflict and reports which branches succeeded.
|
|
499
|
+
*/
|
|
500
|
+
export async function mergeTaskBranches(
|
|
501
|
+
repoRoot: string,
|
|
502
|
+
branches: Array<{ branchName: string; taskId: string; description?: string }>,
|
|
503
|
+
): Promise<MergeBranchResult> {
|
|
504
|
+
const merged: string[] = [];
|
|
505
|
+
const failed: string[] = [];
|
|
506
|
+
|
|
507
|
+
for (const { branchName } of branches) {
|
|
508
|
+
const result = await $`git cherry-pick ${branchName}`.cwd(repoRoot).quiet().nothrow();
|
|
509
|
+
|
|
510
|
+
if (result.exitCode !== 0) {
|
|
511
|
+
await $`git cherry-pick --abort`.cwd(repoRoot).quiet().nothrow();
|
|
512
|
+
const stderr = result.stderr.toString().trim();
|
|
513
|
+
failed.push(branchName);
|
|
514
|
+
return {
|
|
515
|
+
merged,
|
|
516
|
+
failed: [...failed, ...branches.slice(merged.length + failed.length).map(b => b.branchName)],
|
|
517
|
+
conflict: `${branchName}: ${stderr}`,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
merged.push(branchName);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return { merged, failed };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** Clean up temporary task branches. */
|
|
528
|
+
export async function cleanupTaskBranches(repoRoot: string, branches: string[]): Promise<void> {
|
|
529
|
+
for (const branch of branches) {
|
|
530
|
+
await $`git branch -D ${branch}`.cwd(repoRoot).quiet().nothrow();
|
|
531
|
+
}
|
|
532
|
+
}
|
package/src/tools/ask.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "@oh-my-pi/pi-tui";
|
|
12
12
|
import type { Terminal as XtermTerminalType } from "@xterm/headless";
|
|
13
13
|
import xterm from "@xterm/headless";
|
|
14
|
+
import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
|
|
14
15
|
import type { Theme } from "../modes/theme/theme";
|
|
15
16
|
import { OutputSink, type OutputSummary } from "../session/streaming-output";
|
|
16
17
|
import { formatStatusIcon, replaceTabs } from "./render-utils";
|
|
@@ -275,50 +276,6 @@ class BashInteractiveOverlayComponent implements Component {
|
|
|
275
276
|
}
|
|
276
277
|
}
|
|
277
278
|
|
|
278
|
-
export const NO_PAGER_ENV = {
|
|
279
|
-
// Disable pagers so commands don't block on interactive views.
|
|
280
|
-
PAGER: "cat",
|
|
281
|
-
GIT_PAGER: "cat",
|
|
282
|
-
MANPAGER: "cat",
|
|
283
|
-
SYSTEMD_PAGER: "cat",
|
|
284
|
-
BAT_PAGER: "cat",
|
|
285
|
-
DELTA_PAGER: "cat",
|
|
286
|
-
GH_PAGER: "cat",
|
|
287
|
-
GLAB_PAGER: "cat",
|
|
288
|
-
PSQL_PAGER: "cat",
|
|
289
|
-
MYSQL_PAGER: "cat",
|
|
290
|
-
AWS_PAGER: "",
|
|
291
|
-
HOMEBREW_PAGER: "cat",
|
|
292
|
-
LESS: "FRX",
|
|
293
|
-
// Disable editor and terminal credential prompts.
|
|
294
|
-
GIT_EDITOR: "true",
|
|
295
|
-
VISUAL: "true",
|
|
296
|
-
EDITOR: "true",
|
|
297
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
298
|
-
SSH_ASKPASS: "/usr/bin/false",
|
|
299
|
-
CI: "1",
|
|
300
|
-
// Package manager defaults for unattended execution.
|
|
301
|
-
npm_config_yes: "true",
|
|
302
|
-
npm_config_update_notifier: "false",
|
|
303
|
-
npm_config_fund: "false",
|
|
304
|
-
npm_config_audit: "false",
|
|
305
|
-
npm_config_progress: "false",
|
|
306
|
-
PNPM_DISABLE_SELF_UPDATE_CHECK: "true",
|
|
307
|
-
PNPM_UPDATE_NOTIFIER: "false",
|
|
308
|
-
YARN_ENABLE_TELEMETRY: "0",
|
|
309
|
-
YARN_ENABLE_PROGRESS_BARS: "0",
|
|
310
|
-
// Cross-language/tooling non-interactive defaults.
|
|
311
|
-
CARGO_TERM_PROGRESS_WHEN: "never",
|
|
312
|
-
DEBIAN_FRONTEND: "noninteractive",
|
|
313
|
-
PIP_NO_INPUT: "1",
|
|
314
|
-
PIP_DISABLE_PIP_VERSION_CHECK: "1",
|
|
315
|
-
TF_INPUT: "0",
|
|
316
|
-
TF_IN_AUTOMATION: "1",
|
|
317
|
-
GH_PROMPT_DISABLED: "1",
|
|
318
|
-
COMPOSER_NO_INTERACTION: "1",
|
|
319
|
-
CLOUDSDK_CORE_DISABLE_PROMPTS: "1",
|
|
320
|
-
};
|
|
321
|
-
|
|
322
279
|
export async function runInteractiveBashPty(
|
|
323
280
|
ui: NonNullable<AgentToolContext["ui"]>,
|
|
324
281
|
options: {
|
|
@@ -388,8 +345,8 @@ export async function runInteractiveBashPty(
|
|
|
388
345
|
cwd: options.cwd,
|
|
389
346
|
timeoutMs: options.timeoutMs,
|
|
390
347
|
env: {
|
|
348
|
+
...NON_INTERACTIVE_ENV,
|
|
391
349
|
...options.env,
|
|
392
|
-
...NO_PAGER_ENV,
|
|
393
350
|
},
|
|
394
351
|
signal: options.signal,
|
|
395
352
|
cols,
|
package/src/tools/bash.ts
CHANGED
|
@@ -3,11 +3,11 @@ import * as path from "node:path";
|
|
|
3
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
4
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
6
|
-
import { $env, isEnoent } from "@oh-my-pi/pi-utils";
|
|
7
|
-
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
6
|
+
import { $env, getProjectDir, isEnoent } from "@oh-my-pi/pi-utils";
|
|
8
7
|
import { Type } from "@sinclair/typebox";
|
|
9
8
|
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
10
9
|
import { type BashResult, executeBash } from "../exec/bash-executor";
|
|
10
|
+
import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
|
|
11
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
12
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
13
13
|
import type { Theme } from "../modes/theme/theme";
|
|
@@ -16,7 +16,7 @@ import { DEFAULT_MAX_BYTES, TailBuffer } from "../session/streaming-output";
|
|
|
16
16
|
import { renderStatusLine } from "../tui";
|
|
17
17
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
18
18
|
import type { ToolSession } from ".";
|
|
19
|
-
import { type BashInteractiveResult,
|
|
19
|
+
import { type BashInteractiveResult, runInteractiveBashPty } from "./bash-interactive";
|
|
20
20
|
import { checkBashInterception } from "./bash-interceptor";
|
|
21
21
|
import { applyHeadTail } from "./bash-normalize";
|
|
22
22
|
import { expandInternalUrls, type InternalUrlExpansionOptions } from "./bash-skill-urls";
|
|
@@ -222,7 +222,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
222
222
|
sessionKey: `${this.session.getSessionId?.() ?? ""}:async:${jobId}`,
|
|
223
223
|
timeout: timeoutMs,
|
|
224
224
|
signal: runSignal,
|
|
225
|
-
env:
|
|
225
|
+
env: NON_INTERACTIVE_ENV,
|
|
226
226
|
artifactPath,
|
|
227
227
|
artifactId,
|
|
228
228
|
onChunk: chunk => {
|
|
@@ -273,7 +273,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
273
273
|
sessionKey: this.session.getSessionId?.() ?? undefined,
|
|
274
274
|
timeout: timeoutMs,
|
|
275
275
|
signal,
|
|
276
|
-
env:
|
|
276
|
+
env: NON_INTERACTIVE_ENV,
|
|
277
277
|
artifactPath,
|
|
278
278
|
artifactId,
|
|
279
279
|
onChunk: chunk => {
|
package/src/tools/browser.ts
CHANGED
|
@@ -3,8 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { Readability } from "@mozilla/readability";
|
|
4
4
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
5
5
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
6
|
-
import { logger, Snowflake, untilAborted } from "@oh-my-pi/pi-utils";
|
|
7
|
-
import { getPuppeteerDir } from "@oh-my-pi/pi-utils/dirs";
|
|
6
|
+
import { getPuppeteerDir, logger, Snowflake, untilAborted } from "@oh-my-pi/pi-utils";
|
|
8
7
|
import { type Static, Type } from "@sinclair/typebox";
|
|
9
8
|
import { type HTMLElement, parseHTML } from "linkedom";
|
|
10
9
|
import type {
|