@qwen-code/qwen-code 0.16.1-preview.0 → 0.16.2
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/bundled/new-app/SKILL.md +22 -0
- package/bundled/qc-helper/docs/configuration/settings.md +2 -1
- package/bundled/qc-helper/docs/features/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/worktree.md +345 -0
- package/chunks/{agent-KYFWAFRH.js → agent-RY5EB3XR.js} +15 -15
- package/chunks/{anthropicContentGenerator-DUZ2FYDT.js → anthropicContentGenerator-LYI3OHBB.js} +4 -4
- package/chunks/{askUserQuestion-KFDJMM66.js → askUserQuestion-R3MKD2JT.js} +2 -2
- package/chunks/{ca-UZ7BANMN.js → ca-NMZFEGAU.js} +1 -0
- package/chunks/{chunk-DDU3LH4J.js → chunk-4YNZFYJY.js} +59 -21
- package/chunks/{chunk-JQF2373J.js → chunk-66CJCYYZ.js} +11 -11
- package/chunks/{chunk-ERQCJRX4.js → chunk-6NUSWV4M.js} +1 -1
- package/chunks/{chunk-QQAHXZS2.js → chunk-7HM6OB7M.js} +574 -103
- package/chunks/{chunk-DZNH4UDC.js → chunk-7YJIR2FX.js} +2 -2
- package/chunks/{chunk-W2FLCFLJ.js → chunk-ACBGEKB7.js} +1 -1
- package/chunks/{chunk-34S63Y6S.js → chunk-C27V5A2J.js} +1 -1
- package/chunks/{chunk-QDV553GJ.js → chunk-EY6BDW7Y.js} +1 -1
- package/chunks/{chunk-JZNYPW2X.js → chunk-FO7BIVSR.js} +1 -1
- package/chunks/{chunk-F6MJMTLW.js → chunk-GQXXO5HJ.js} +28 -8
- package/chunks/{chunk-Q7IZ5RFJ.js → chunk-GVWPJCXU.js} +1 -1
- package/chunks/{chunk-SS4HGKS4.js → chunk-JVD46YJV.js} +1 -1
- package/chunks/{chunk-33FTD7OV.js → chunk-K72FHBFO.js} +1 -1
- package/chunks/{chunk-G5JCK3FG.js → chunk-NQ3E7YLD.js} +1 -1
- package/chunks/{chunk-QOVHOYKK.js → chunk-ODPVJ6JJ.js} +1 -1
- package/chunks/{chunk-AAJT7L7R.js → chunk-OIL7KDWV.js} +1 -1
- package/chunks/{chunk-M5TA67JI.js → chunk-PJLEMR7N.js} +6 -6
- package/chunks/{chunk-VMHEBQ5K.js → chunk-TXQI3VZ7.js} +1 -1
- package/chunks/{chunk-XS2N5WHQ.js → chunk-UE5LPQF7.js} +5 -5
- package/chunks/{chunk-6W4AH2GT.js → chunk-VMOWXTRC.js} +2 -2
- package/chunks/{chunk-OEIT4HZX.js → chunk-YMDXEEOW.js} +1 -1
- package/chunks/{contextCommand-E3Z6QOHV.js → contextCommand-DDGVLQSF.js} +17 -17
- package/chunks/{cron-create-GJT7LK4I.js → cron-create-BTEOGHPH.js} +2 -2
- package/chunks/{cron-delete-PWUIASTZ.js → cron-delete-56CEWELN.js} +2 -2
- package/chunks/{cron-list-P42BOGWK.js → cron-list-SV6QRZW2.js} +2 -2
- package/chunks/{de-V4IE2OOZ.js → de-OIMT3OMI.js} +1 -0
- package/chunks/{edit-2CY35GKJ.js → edit-4LLGNYVZ.js} +16 -16
- package/chunks/{en-HGJ2SPLM.js → en-2IFZ5THF.js} +1 -0
- package/chunks/{enter-worktree-HJGN4DH2.js → enter-worktree-E2R5XAFT.js} +18 -16
- package/chunks/{exit-worktree-VAPX5N2E.js → exit-worktree-YVBYYYDD.js} +15 -15
- package/chunks/{exitPlanMode-3JP3BRRO.js → exitPlanMode-WD5IH7NS.js} +15 -15
- package/chunks/{fr-CJULI7ZX.js → fr-PVELSHTV.js} +1 -0
- package/chunks/{geminiContentGenerator-MD7IEVCK.js → geminiContentGenerator-LM65ADWM.js} +3 -3
- package/chunks/{glob-C5ZNGH7V.js → glob-6X6OCEWE.js} +15 -15
- package/chunks/{grep-GKU7Y77F.js → grep-2UUPSSIQ.js} +15 -15
- package/chunks/{ja-L7CHRQEW.js → ja-P5TK5GNN.js} +1 -0
- package/chunks/{keychain-token-storage-QCF76A5D.js → keychain-token-storage-3552ENXE.js} +2 -2
- package/chunks/{ls-QZTZYURK.js → ls-MYXAM7LJ.js} +3 -3
- package/chunks/{lsp-L6NQBEQA.js → lsp-PFGI35JL.js} +2 -2
- package/chunks/{monitor-HJTGGH2N.js → monitor-VUHPEGUW.js} +25 -20
- package/chunks/{notebook-edit-RWOEUTYU.js → notebook-edit-P4QVLW6I.js} +16 -16
- package/chunks/{openaiContentGenerator-FDC4XT77.js → openaiContentGenerator-JH4YNZ3H.js} +10 -10
- package/chunks/{pt-M6JULLEQ.js → pt-A5GHG66T.js} +1 -0
- package/chunks/{qwenContentGenerator-J3C7NUMH.js → qwenContentGenerator-5FE4UYUT.js} +17 -17
- package/chunks/{qwenOAuth2-7JZIHZ7O.js → qwenOAuth2-BAN2EGSH.js} +3 -3
- package/chunks/{read-file-5QUJ3ZIL.js → read-file-J7DH4OKV.js} +7 -7
- package/chunks/{ripGrep-PA7M7MJS.js → ripGrep-33DECY4F.js} +15 -15
- package/chunks/{ru-QILM4HBC.js → ru-66XKB4QX.js} +1 -0
- package/chunks/{send-message-GDN76EHU.js → send-message-JUFP62VD.js} +2 -2
- package/chunks/{serve-FLSKL5TV.js → serve-7FX7MREA.js} +17 -17
- package/chunks/{shell-DA54BZO2.js → shell-ZNTQIRK6.js} +15 -15
- package/chunks/{skill-47FZXDEM.js → skill-CFCUIY23.js} +9 -9
- package/chunks/{src-6Y7NVY2L.js → src-AHV2CWEQ.js} +21 -15
- package/chunks/{syntheticOutput-6N4MZYQT.js → syntheticOutput-AKTXC6FR.js} +3 -3
- package/chunks/{task-stop-TTT4O7JR.js → task-stop-2NYFR2ES.js} +2 -2
- package/chunks/{todoWrite-GYUYNLKE.js → todoWrite-WHZ2O2XP.js} +3 -3
- package/chunks/{tool-search-PONJXBSP.js → tool-search-C2EMLFBJ.js} +7 -7
- package/chunks/{web-fetch-V4TWMRQD.js → web-fetch-S6MZXPZ5.js} +4 -4
- package/chunks/{write-file-2YX4NB3B.js → write-file-EEPVRS4Q.js} +16 -16
- package/chunks/{zh-PWL2NKY3.js → zh-OB5P2ZDO.js} +1 -0
- package/chunks/{zh-TW-S3YGWICZ.js → zh-TW-3ND6DQRX.js} +1 -0
- package/cli.js +1377 -922
- package/locales/ca.js +1 -0
- package/locales/de.js +1 -0
- package/locales/en.js +1 -0
- package/locales/fr.js +1 -0
- package/locales/ja.js +1 -0
- package/locales/pt.js +1 -0
- package/locales/ru.js +1 -0
- package/locales/zh-TW.js +1 -0
- package/locales/zh.js +1 -0
- package/package.json +2 -2
|
@@ -36,11 +36,11 @@ import {
|
|
|
36
36
|
} from "./chunk-TW522KN6.js";
|
|
37
37
|
import {
|
|
38
38
|
runSideQuery
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-C27V5A2J.js";
|
|
40
40
|
import {
|
|
41
41
|
escapeSystemReminderTags,
|
|
42
42
|
escapeXml
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-EY6BDW7Y.js";
|
|
44
44
|
import {
|
|
45
45
|
DefaultHookOutput,
|
|
46
46
|
HOOKS_CONFIG_FIELDS,
|
|
@@ -52,14 +52,14 @@ import {
|
|
|
52
52
|
StopHookOutput,
|
|
53
53
|
createHookOutput,
|
|
54
54
|
getHookKey
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-K72FHBFO.js";
|
|
56
56
|
import {
|
|
57
57
|
DEFAULT_FILE_FILTERING_OPTIONS
|
|
58
58
|
} from "./chunk-77WXWU44.js";
|
|
59
59
|
import {
|
|
60
60
|
BaseTokenStorage,
|
|
61
61
|
KeychainTokenStorage
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-GVWPJCXU.js";
|
|
63
63
|
import {
|
|
64
64
|
AUTH_ENV_MAPPINGS,
|
|
65
65
|
ApiResponseEvent,
|
|
@@ -114,6 +114,7 @@ import {
|
|
|
114
114
|
initializeTelemetry,
|
|
115
115
|
isTelemetrySdkInitialized,
|
|
116
116
|
isUtf8CompatibleEncoding,
|
|
117
|
+
isWithinRoot,
|
|
117
118
|
logArenaAgentCompleted,
|
|
118
119
|
logArenaSessionEnded,
|
|
119
120
|
logArenaSessionStarted,
|
|
@@ -160,7 +161,7 @@ import {
|
|
|
160
161
|
truncateSpanError,
|
|
161
162
|
truncateToolOutput,
|
|
162
163
|
uiTelemetryService
|
|
163
|
-
} from "./chunk-
|
|
164
|
+
} from "./chunk-4YNZFYJY.js";
|
|
164
165
|
import {
|
|
165
166
|
DEFAULT_QWEN_EMBEDDING_MODEL,
|
|
166
167
|
DEFAULT_QWEN_MODEL
|
|
@@ -173,12 +174,12 @@ import {
|
|
|
173
174
|
getAutoMemoryMetadataPath,
|
|
174
175
|
getAutoMemoryRoot,
|
|
175
176
|
isAutoMemPath
|
|
176
|
-
} from "./chunk-
|
|
177
|
+
} from "./chunk-NQ3E7YLD.js";
|
|
177
178
|
import {
|
|
178
179
|
DEFAULT_TOKEN_LIMIT,
|
|
179
180
|
ESCALATED_MAX_TOKENS,
|
|
180
181
|
tokenLimit
|
|
181
|
-
} from "./chunk-
|
|
182
|
+
} from "./chunk-6NUSWV4M.js";
|
|
182
183
|
import {
|
|
183
184
|
FinishReason,
|
|
184
185
|
createModelContent,
|
|
@@ -188,7 +189,7 @@ import {
|
|
|
188
189
|
} from "./chunk-T4VD6OJ4.js";
|
|
189
190
|
import {
|
|
190
191
|
STRUCTURED_OUTPUT_REDACTED_ARGS
|
|
191
|
-
} from "./chunk-
|
|
192
|
+
} from "./chunk-FO7BIVSR.js";
|
|
192
193
|
import {
|
|
193
194
|
AgentStatistics,
|
|
194
195
|
BaseDeclarativeTool,
|
|
@@ -203,7 +204,7 @@ import {
|
|
|
203
204
|
isTool,
|
|
204
205
|
require_ajv,
|
|
205
206
|
require_dist
|
|
206
|
-
} from "./chunk-
|
|
207
|
+
} from "./chunk-YMDXEEOW.js";
|
|
207
208
|
import {
|
|
208
209
|
PATH_ARG_KEYS,
|
|
209
210
|
QWEN_DIR,
|
|
@@ -222,7 +223,7 @@ import {
|
|
|
222
223
|
setDebugLogSession,
|
|
223
224
|
toFriendlyError,
|
|
224
225
|
unescapePath
|
|
225
|
-
} from "./chunk-
|
|
226
|
+
} from "./chunk-ACBGEKB7.js";
|
|
226
227
|
import {
|
|
227
228
|
require_undici
|
|
228
229
|
} from "./chunk-E7E2MFYM.js";
|
|
@@ -15589,11 +15590,11 @@ var require_extract_zip = __commonJS({
|
|
|
15589
15590
|
var { createWriteStream: createWriteStream3, promises: fs81 } = __require("fs");
|
|
15590
15591
|
var getStream = require_get_stream();
|
|
15591
15592
|
var path84 = __require("path");
|
|
15592
|
-
var { promisify:
|
|
15593
|
+
var { promisify: promisify6 } = __require("util");
|
|
15593
15594
|
var stream2 = __require("stream");
|
|
15594
15595
|
var yauzl = require_yauzl();
|
|
15595
|
-
var openZip =
|
|
15596
|
-
var pipeline =
|
|
15596
|
+
var openZip = promisify6(yauzl.open);
|
|
15597
|
+
var pipeline = promisify6(stream2.pipeline);
|
|
15597
15598
|
var Extractor = class {
|
|
15598
15599
|
static {
|
|
15599
15600
|
__name(this, "Extractor");
|
|
@@ -15660,14 +15661,14 @@ var require_extract_zip = __commonJS({
|
|
|
15660
15661
|
const IFMT2 = 61440;
|
|
15661
15662
|
const IFDIR2 = 16384;
|
|
15662
15663
|
const IFLNK2 = 40960;
|
|
15663
|
-
const
|
|
15664
|
+
const symlink2 = (mode & IFMT2) === IFLNK2;
|
|
15664
15665
|
let isDir = (mode & IFMT2) === IFDIR2;
|
|
15665
15666
|
if (!isDir && entry.fileName.endsWith("/")) {
|
|
15666
15667
|
isDir = true;
|
|
15667
15668
|
}
|
|
15668
15669
|
const madeBy = entry.versionMadeBy >> 8;
|
|
15669
15670
|
if (!isDir) isDir = madeBy === 0 && entry.externalFileAttributes === 16;
|
|
15670
|
-
debug2("extracting entry", { filename: entry.fileName, isDir, isSymlink:
|
|
15671
|
+
debug2("extracting entry", { filename: entry.fileName, isDir, isSymlink: symlink2 });
|
|
15671
15672
|
const procMode = this.getExtractedMode(mode, isDir) & 511;
|
|
15672
15673
|
const destDir = isDir ? dest : path84.dirname(dest);
|
|
15673
15674
|
const mkdirOptions = { recursive: true };
|
|
@@ -15678,8 +15679,8 @@ var require_extract_zip = __commonJS({
|
|
|
15678
15679
|
await fs81.mkdir(destDir, mkdirOptions);
|
|
15679
15680
|
if (isDir) return;
|
|
15680
15681
|
debug2("opening read stream", dest);
|
|
15681
|
-
const readStream = await
|
|
15682
|
-
if (
|
|
15682
|
+
const readStream = await promisify6(this.zipfile.openReadStream.bind(this.zipfile))(entry);
|
|
15683
|
+
if (symlink2) {
|
|
15683
15684
|
const link2 = await getStream(readStream);
|
|
15684
15685
|
debug2("creating symlink", link2, dest);
|
|
15685
15686
|
await fs81.symlink(link2, dest);
|
|
@@ -34206,22 +34207,7 @@ IMPORTANT: Always use the ${ToolNames.TODO_WRITE} tool to plan and track tasks t
|
|
|
34206
34207
|
|
|
34207
34208
|
## New Applications
|
|
34208
34209
|
|
|
34209
|
-
|
|
34210
|
-
|
|
34211
|
-
1. **Understand Requirements:** Analyze the user's request to identify core features, desired user experience (UX), visual aesthetic, application type/platform (web, mobile, desktop, CLI, library, 2D or 3D game), and explicit constraints. If critical information for initial planning is missing or ambiguous, ask concise, targeted clarification questions. Use the ${ToolNames.ASK_USER_QUESTION} tool to ask questions, clarify and gather information as needed.
|
|
34212
|
-
2. **Propose Plan:** Formulate an internal development plan. Present a clear, concise, high-level summary to the user. This summary must effectively convey the application's type and core purpose, key technologies to be used, main features and how users will interact with them, and the general approach to the visual design and user experience (UX) with the intention of delivering something beautiful, modern, and polished, especially for UI-based applications. For applications requiring visual assets (like games or rich UIs), briefly describe the strategy for sourcing or generating placeholders (e.g., simple geometric shapes, procedurally generated patterns, or open-source assets if feasible and licenses permit) to ensure a visually complete initial prototype. Ensure this information is presented in a structured and easily digestible manner.
|
|
34213
|
-
- When key technologies aren't specified, prefer the following:
|
|
34214
|
-
- **Websites (Frontend):** React (JavaScript/TypeScript) with Bootstrap CSS, incorporating Material Design principles for UI/UX.
|
|
34215
|
-
- **Back-End APIs:** Node.js with Express.js (JavaScript/TypeScript) or Python with FastAPI.
|
|
34216
|
-
- **Full-stack:** Next.js (React/Node.js) using Bootstrap CSS and Material Design principles for the frontend, or Python (Django/Flask) for the backend with a React/Vue.js frontend styled with Bootstrap CSS and Material Design principles.
|
|
34217
|
-
- **CLIs:** Python or Go.
|
|
34218
|
-
- **Mobile App:** Compose Multiplatform (Kotlin Multiplatform) or Flutter (Dart) using Material Design libraries and principles, when sharing code between Android and iOS. Jetpack Compose (Kotlin JVM) with Material Design principles or SwiftUI (Swift) for native apps targeted at either Android or iOS, respectively.
|
|
34219
|
-
- **3d Games:** HTML/CSS/JavaScript with Three.js.
|
|
34220
|
-
- **2d Games:** HTML/CSS/JavaScript.
|
|
34221
|
-
3. **User Approval:** Obtain user approval for the proposed plan.
|
|
34222
|
-
4. **Implementation:** Use the '${ToolNames.TODO_WRITE}' tool to convert the approved plan into a structured todo list with specific, actionable tasks, then autonomously implement each task utilizing all available tools. When starting ensure you scaffold the application using '${ToolNames.SHELL}' for commands like 'npm init', 'npx create-react-app'. Aim for full scope completion. Proactively create or source necessary placeholder assets (e.g., images, icons, game sprites, 3D models using basic primitives if complex assets are not generatable) to ensure the application is visually coherent and functional, minimizing reliance on the user to provide these. If the model can generate simple assets (e.g., a uniformly colored square sprite, a simple 3D cube), it should do so. Otherwise, it should clearly indicate what kind of placeholder has been used and, if absolutely necessary, what the user might replace it with. Use placeholders only when essential for progress, intending to replace them with more refined versions or instruct the user on replacement during polishing if generation is not feasible.
|
|
34223
|
-
5. **Verify:** Review work against the original request, the approved plan. Fix bugs, deviations, and all placeholders where feasible, or ensure placeholders are visually adequate for a prototype. Ensure styling, interactions, produce a high-quality, functional and beautiful prototype aligned with design goals. Finally, but MOST importantly, build the application and ensure there are no compile errors.
|
|
34224
|
-
6. **Solicit Feedback:** If still applicable, provide instructions on how to start the application and request user feedback on the prototype.
|
|
34210
|
+
When a user wants to create a new application, project, website, game, or library from scratch, use the '${ToolNames.SKILL}' tool with skill="new-app" to load the detailed workflow and tech-stack guidance.
|
|
34225
34211
|
|
|
34226
34212
|
# Operational Guidelines
|
|
34227
34213
|
|
|
@@ -38116,6 +38102,9 @@ function evaluateShellSegment(segment) {
|
|
|
38116
38102
|
if (!segment.trim()) {
|
|
38117
38103
|
return true;
|
|
38118
38104
|
}
|
|
38105
|
+
if (detectCommandSubstitution(segment)) {
|
|
38106
|
+
return false;
|
|
38107
|
+
}
|
|
38119
38108
|
const stripped = stripShellWrapper(segment);
|
|
38120
38109
|
if (!stripped) {
|
|
38121
38110
|
return true;
|
|
@@ -50849,7 +50838,8 @@ init_esbuild_shims();
|
|
|
50849
50838
|
import * as fs15 from "node:fs/promises";
|
|
50850
50839
|
import * as path15 from "node:path";
|
|
50851
50840
|
import { randomBytes as randomBytes2, randomInt } from "node:crypto";
|
|
50852
|
-
import { execSync as execSync3 } from "node:child_process";
|
|
50841
|
+
import { execFile as execFile2, execSync as execSync3 } from "node:child_process";
|
|
50842
|
+
import { promisify as promisify2 } from "node:util";
|
|
50853
50843
|
|
|
50854
50844
|
// node_modules/simple-git/dist/esm/index.js
|
|
50855
50845
|
init_esbuild_shims();
|
|
@@ -55668,6 +55658,7 @@ async function initRepositoryWithMainBranch(git) {
|
|
|
55668
55658
|
__name(initRepositoryWithMainBranch, "initRepositoryWithMainBranch");
|
|
55669
55659
|
|
|
55670
55660
|
// packages/core/src/services/gitWorktreeService.ts
|
|
55661
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
55671
55662
|
var debugLogger25 = createDebugLogger("GIT_WORKTREE_SERVICE");
|
|
55672
55663
|
var WORKTREE_BRANCH_PREFIX = "worktree-";
|
|
55673
55664
|
function worktreeBranchForSlug(slug) {
|
|
@@ -55864,6 +55855,25 @@ var GitWorktreeService = class _GitWorktreeService {
|
|
|
55864
55855
|
const hash = await this.git.revparse(["HEAD"]);
|
|
55865
55856
|
return hash.trim();
|
|
55866
55857
|
}
|
|
55858
|
+
/**
|
|
55859
|
+
* Resolves a git ref name to a 40-char commit SHA. Returns `null` when
|
|
55860
|
+
* the ref is unknown / unborn / not a commit.
|
|
55861
|
+
*
|
|
55862
|
+
* Used by Phase D-3 to lock in `FETCH_HEAD` immediately after
|
|
55863
|
+
* `fetchPullRequestRef` succeeds, so the SHA passed to
|
|
55864
|
+
* `git worktree add` is immutable against a concurrent `git fetch` from
|
|
55865
|
+
* another process sharing the same repo, AND so `WorktreeExitDialog`'s
|
|
55866
|
+
* `rev-list <originalHeadCommit>..HEAD` counts only THIS session's new
|
|
55867
|
+
* work rather than every commit in the fetched PR.
|
|
55868
|
+
*/
|
|
55869
|
+
async resolveRef(ref) {
|
|
55870
|
+
try {
|
|
55871
|
+
const out2 = (await this.git.raw(["rev-parse", "--verify", ref])).trim();
|
|
55872
|
+
return /^[0-9a-f]{40}$/.test(out2) ? out2 : null;
|
|
55873
|
+
} catch {
|
|
55874
|
+
return null;
|
|
55875
|
+
}
|
|
55876
|
+
}
|
|
55867
55877
|
/**
|
|
55868
55878
|
* Creates a single worktree.
|
|
55869
55879
|
*/
|
|
@@ -56353,6 +56363,207 @@ var GitWorktreeService = class _GitWorktreeService {
|
|
|
56353
56363
|
const suffix = randomBytes2(3).toString("hex");
|
|
56354
56364
|
return `${adj}-${noun}-${suffix}`;
|
|
56355
56365
|
}
|
|
56366
|
+
/**
|
|
56367
|
+
* Parses a PR reference from a string. Recognised forms:
|
|
56368
|
+
*
|
|
56369
|
+
* - `#123` — shorthand PR number
|
|
56370
|
+
* - `https://github.com/<owner>/<repo>/pull/123` — full GitHub URL
|
|
56371
|
+
* (any host, any query string, any fragment)
|
|
56372
|
+
*
|
|
56373
|
+
* Returns the parsed PR number on match, `null` otherwise. The slug for
|
|
56374
|
+
* a PR worktree is derived by callers as `pr-<N>` and the branch as
|
|
56375
|
+
* `worktree-pr-<N>` (see `createUserWorktree`).
|
|
56376
|
+
*
|
|
56377
|
+
* Mirrors claude-code's `parsePRReference` (utils/worktree.ts:633) so
|
|
56378
|
+
* cross-CLI muscle memory transfers.
|
|
56379
|
+
*/
|
|
56380
|
+
static parsePRReference(input) {
|
|
56381
|
+
if (typeof input !== "string") return null;
|
|
56382
|
+
const trimmed2 = input.trim();
|
|
56383
|
+
const urlMatch = trimmed2.match(
|
|
56384
|
+
/^https?:\/\/[^/]+\/[^/]+\/[^/]+\/pull\/(\d+)(?:\/[^?#]*)?(?:[?#].*)?$/i
|
|
56385
|
+
);
|
|
56386
|
+
if (urlMatch?.[1]) {
|
|
56387
|
+
const n = parseInt(urlMatch[1], 10);
|
|
56388
|
+
return Number.isSafeInteger(n) && n > 0 ? n : null;
|
|
56389
|
+
}
|
|
56390
|
+
const hashMatch = trimmed2.match(/^#([1-9]\d*)$/);
|
|
56391
|
+
if (hashMatch?.[1]) {
|
|
56392
|
+
const n = parseInt(hashMatch[1], 10);
|
|
56393
|
+
return Number.isSafeInteger(n) && n > 0 ? n : null;
|
|
56394
|
+
}
|
|
56395
|
+
return null;
|
|
56396
|
+
}
|
|
56397
|
+
/**
|
|
56398
|
+
* Identifies the registered worktree at `worktreePath` as a member of
|
|
56399
|
+
* THIS repository (`sourceRepoPath`). Returns the branch + HEAD commit
|
|
56400
|
+
* SHA on success, or `null` when the path is not a worktree of this
|
|
56401
|
+
* repo.
|
|
56402
|
+
*
|
|
56403
|
+
* Used by Phase D-1's re-attach path: when `--worktree foo` is passed
|
|
56404
|
+
* and `<repoRoot>/.qwen/worktrees/foo` already exists on disk, we
|
|
56405
|
+
* verify it really IS a Qwen-managed worktree of the current repo (not
|
|
56406
|
+
* a standalone `git init` someone dropped at that path) before
|
|
56407
|
+
* assuming it's safe to chdir into. Returning the HEAD SHA in the
|
|
56408
|
+
* same call avoids a second subprocess to recapture it after chdir.
|
|
56409
|
+
*
|
|
56410
|
+
* Implementation — a single `git rev-parse` returning four lines:
|
|
56411
|
+
* 1. `HEAD` → the worktree's HEAD commit SHA (must come BEFORE
|
|
56412
|
+
* `--abbrev-ref` since the flag sticks for all subsequent refs).
|
|
56413
|
+
* 2. `--abbrev-ref HEAD` → the branch name. A detached HEAD produces
|
|
56414
|
+
* `HEAD` here, which we treat as "no real branch" and return null
|
|
56415
|
+
* — the caller's re-attach gate will then refuse, since the
|
|
56416
|
+
* slug-derived branch couldn't possibly be `HEAD`.
|
|
56417
|
+
* 3. `--git-common-dir` → the common `.git` directory. For a real
|
|
56418
|
+
* linked worktree of this repo that's `<sourceRepoPath>/.git`;
|
|
56419
|
+
* for a sibling `git init` it resolves to `<worktreePath>/.git`.
|
|
56420
|
+
* We compare against this repo's own common-dir to reject the
|
|
56421
|
+
* latter.
|
|
56422
|
+
* 4. `--show-toplevel` → git's idea of the worktree top. For a real
|
|
56423
|
+
* linked worktree this equals `worktreePath`; for a plain
|
|
56424
|
+
* directory living UNDER the main repo (e.g. `mkdir
|
|
56425
|
+
* <repo>/.qwen/worktrees/foo`) git walks up to the outer `.git`
|
|
56426
|
+
* and returns the OUTER repo's root — which would otherwise pass
|
|
56427
|
+
* the common-dir check and let us "re-attach" to a non-worktree
|
|
56428
|
+
* directory. Compare paths to reject this.
|
|
56429
|
+
*/
|
|
56430
|
+
async getRegisteredWorktreeBranch(worktreePath) {
|
|
56431
|
+
let resolvedWorktreePath;
|
|
56432
|
+
try {
|
|
56433
|
+
const stat16 = await fs15.stat(worktreePath);
|
|
56434
|
+
if (!stat16.isDirectory()) return null;
|
|
56435
|
+
resolvedWorktreePath = await fs15.realpath(worktreePath);
|
|
56436
|
+
} catch {
|
|
56437
|
+
return null;
|
|
56438
|
+
}
|
|
56439
|
+
const probeGit = simpleGit(worktreePath);
|
|
56440
|
+
let ourCommonDir;
|
|
56441
|
+
let headCommit;
|
|
56442
|
+
let branch;
|
|
56443
|
+
let probeCommonDir;
|
|
56444
|
+
let probeToplevel;
|
|
56445
|
+
try {
|
|
56446
|
+
const [ourRaw, probeRaw] = await Promise.all([
|
|
56447
|
+
this.git.raw(["rev-parse", "--git-common-dir"]),
|
|
56448
|
+
probeGit.raw([
|
|
56449
|
+
"rev-parse",
|
|
56450
|
+
"HEAD",
|
|
56451
|
+
"--abbrev-ref",
|
|
56452
|
+
"HEAD",
|
|
56453
|
+
"--git-common-dir",
|
|
56454
|
+
"--show-toplevel"
|
|
56455
|
+
])
|
|
56456
|
+
]);
|
|
56457
|
+
ourCommonDir = path15.resolve(this.sourceRepoPath, ourRaw.trim());
|
|
56458
|
+
const lines = probeRaw.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
56459
|
+
if (lines.length < 4) return null;
|
|
56460
|
+
headCommit = lines[0];
|
|
56461
|
+
branch = lines[1];
|
|
56462
|
+
probeCommonDir = path15.resolve(worktreePath, lines[2]);
|
|
56463
|
+
probeToplevel = path15.resolve(lines[3]);
|
|
56464
|
+
} catch (error) {
|
|
56465
|
+
debugLogger25.debug(
|
|
56466
|
+
`getRegisteredWorktreeBranch: probe at ${worktreePath} failed: ${error}`
|
|
56467
|
+
);
|
|
56468
|
+
return null;
|
|
56469
|
+
}
|
|
56470
|
+
if (probeCommonDir !== ourCommonDir) {
|
|
56471
|
+
debugLogger25.debug(
|
|
56472
|
+
`getRegisteredWorktreeBranch: ${worktreePath} belongs to a different repo (common-dir=${probeCommonDir}, expected ${ourCommonDir})`
|
|
56473
|
+
);
|
|
56474
|
+
return null;
|
|
56475
|
+
}
|
|
56476
|
+
if (probeToplevel !== resolvedWorktreePath) {
|
|
56477
|
+
debugLogger25.debug(
|
|
56478
|
+
`getRegisteredWorktreeBranch: ${worktreePath} is not a registered worktree (toplevel=${probeToplevel}, expected ${resolvedWorktreePath})`
|
|
56479
|
+
);
|
|
56480
|
+
return null;
|
|
56481
|
+
}
|
|
56482
|
+
if (!branch || branch === "HEAD") return null;
|
|
56483
|
+
return { branch, headCommit };
|
|
56484
|
+
}
|
|
56485
|
+
/**
|
|
56486
|
+
* Fetches the GitHub PR ref `refs/pull/<N>/head` from the `origin` remote
|
|
56487
|
+
* so a subsequent `createUserWorktree(..., 'FETCH_HEAD')` call can branch
|
|
56488
|
+
* off the PR's tip (Phase D-3). Returns `{ success: true }` on success,
|
|
56489
|
+
* or `{ success: false, error }` with a user-facing reason on failure.
|
|
56490
|
+
*
|
|
56491
|
+
* Implementation notes:
|
|
56492
|
+
*
|
|
56493
|
+
* - Uses `git fetch origin pull/<N>/head` (no `gh` CLI dependency).
|
|
56494
|
+
* - Hard timeout of 30s by default — overridable for tests. A hung git
|
|
56495
|
+
* process on a misconfigured corporate proxy would otherwise stall
|
|
56496
|
+
* the entire startup sequence.
|
|
56497
|
+
* - Does NOT create a local branch — leaves the ref accessible only
|
|
56498
|
+
* via `FETCH_HEAD`. Subsequent `git worktree add -b <branch> <wt>
|
|
56499
|
+
* FETCH_HEAD` materialises the worktree branch off it.
|
|
56500
|
+
*
|
|
56501
|
+
* Error message taxonomy is friendly because this is the user's first
|
|
56502
|
+
* impression when their `--worktree=#<N>` fails:
|
|
56503
|
+
* - missing `origin` → tell them the remote is required + how to fix
|
|
56504
|
+
* - timeout → mention the configured timeout so they can blame the network
|
|
56505
|
+
* - generic failure → "PR may not exist or origin is unreachable"
|
|
56506
|
+
*/
|
|
56507
|
+
async fetchPullRequestRef(prNumber, options2) {
|
|
56508
|
+
if (!Number.isSafeInteger(prNumber) || prNumber <= 0 || prNumber > 1e9) {
|
|
56509
|
+
return {
|
|
56510
|
+
success: false,
|
|
56511
|
+
error: `Invalid PR number: ${prNumber}.`
|
|
56512
|
+
};
|
|
56513
|
+
}
|
|
56514
|
+
const timeoutMs = options2?.timeoutMs ?? 3e4;
|
|
56515
|
+
const prNumberStr = String(prNumber);
|
|
56516
|
+
if (!/^[1-9][0-9]*$/.test(prNumberStr)) {
|
|
56517
|
+
return {
|
|
56518
|
+
success: false,
|
|
56519
|
+
error: `Invalid PR number: ${prNumber}.`
|
|
56520
|
+
};
|
|
56521
|
+
}
|
|
56522
|
+
const refspec = `pull/${prNumberStr}/head`;
|
|
56523
|
+
try {
|
|
56524
|
+
await execFileAsync2(
|
|
56525
|
+
"git",
|
|
56526
|
+
["fetch", "--end-of-options", "origin", refspec],
|
|
56527
|
+
{
|
|
56528
|
+
cwd: this.sourceRepoPath,
|
|
56529
|
+
timeout: timeoutMs,
|
|
56530
|
+
env: { ...process.env, LANG: "C", LC_ALL: "C" }
|
|
56531
|
+
}
|
|
56532
|
+
);
|
|
56533
|
+
return { success: true };
|
|
56534
|
+
} catch (error) {
|
|
56535
|
+
const err2 = error;
|
|
56536
|
+
const stderr = typeof err2.stderr === "string" ? err2.stderr : err2.stderr instanceof Buffer ? err2.stderr.toString("utf8") : "";
|
|
56537
|
+
const lower = stderr.toLowerCase();
|
|
56538
|
+
if (err2.signal === "SIGTERM") {
|
|
56539
|
+
return {
|
|
56540
|
+
success: false,
|
|
56541
|
+
error: `Failed to fetch PR #${prNumber}: timed out after ${Math.round(timeoutMs / 1e3)}s. Check network connectivity and any HTTP(S) proxy settings.`
|
|
56542
|
+
};
|
|
56543
|
+
}
|
|
56544
|
+
if (lower.includes("does not appear to be a git repository") || lower.includes("could not read from remote repository") || lower.includes("'origin' does not appear")) {
|
|
56545
|
+
return {
|
|
56546
|
+
success: false,
|
|
56547
|
+
error: `--worktree=#${prNumber} requires an "origin" remote that points at GitHub. Add one with \`git remote add origin <url>\` and retry.`
|
|
56548
|
+
};
|
|
56549
|
+
}
|
|
56550
|
+
if (lower.includes("no such ref") || lower.includes("couldn't find remote ref") || lower.includes("couldn't find remote ref pull/")) {
|
|
56551
|
+
return {
|
|
56552
|
+
success: false,
|
|
56553
|
+
error: `Failed to fetch PR #${prNumber}: the PR does not exist on origin, or origin is not a GitHub repository (only GitHub exposes refs/pull/<N>/head).`
|
|
56554
|
+
};
|
|
56555
|
+
}
|
|
56556
|
+
const firstLine = stderr.split("\n").find((l) => l.trim().length > 0);
|
|
56557
|
+
const detail = firstLine ? ` (${firstLine.trim()})` : "";
|
|
56558
|
+
debugLogger25.warn(
|
|
56559
|
+
`fetchPullRequestRef: git fetch pull/${prNumber}/head failed: ${error}`
|
|
56560
|
+
);
|
|
56561
|
+
return {
|
|
56562
|
+
success: false,
|
|
56563
|
+
error: `Failed to fetch PR #${prNumber}: PR may not exist, or origin remote is unreachable${detail}.`
|
|
56564
|
+
};
|
|
56565
|
+
}
|
|
56566
|
+
}
|
|
56356
56567
|
/**
|
|
56357
56568
|
* Validates a worktree slug. Returns null on success, or an error message.
|
|
56358
56569
|
*
|
|
@@ -56398,7 +56609,7 @@ var GitWorktreeService = class _GitWorktreeService {
|
|
|
56398
56609
|
* silently resetting the branch. The previous `-B` form would have
|
|
56399
56610
|
* dropped any commits unique to that branch — see review #4073.
|
|
56400
56611
|
*/
|
|
56401
|
-
async createUserWorktree(slug, baseBranch) {
|
|
56612
|
+
async createUserWorktree(slug, baseBranch, options2) {
|
|
56402
56613
|
const validationError = _GitWorktreeService.validateUserWorktreeSlug(slug);
|
|
56403
56614
|
if (validationError) {
|
|
56404
56615
|
debugLogger25.warn(
|
|
@@ -56437,6 +56648,17 @@ var GitWorktreeService = class _GitWorktreeService {
|
|
|
56437
56648
|
`createUserWorktree: failed to configure core.hooksPath for ${slug}: ${error}`
|
|
56438
56649
|
);
|
|
56439
56650
|
});
|
|
56651
|
+
const symlinkPaths = options2?.symlinkDirectories ?? [];
|
|
56652
|
+
if (symlinkPaths.length > 0) {
|
|
56653
|
+
await this.symlinkConfiguredDirectories(
|
|
56654
|
+
worktreePath,
|
|
56655
|
+
symlinkPaths
|
|
56656
|
+
).catch((error) => {
|
|
56657
|
+
debugLogger25.warn(
|
|
56658
|
+
`createUserWorktree: symlinkConfiguredDirectories failed for ${slug}: ${error}`
|
|
56659
|
+
);
|
|
56660
|
+
});
|
|
56661
|
+
}
|
|
56440
56662
|
const worktree = {
|
|
56441
56663
|
id: slug,
|
|
56442
56664
|
name: slug,
|
|
@@ -56509,6 +56731,172 @@ var GitWorktreeService = class _GitWorktreeService {
|
|
|
56509
56731
|
);
|
|
56510
56732
|
}
|
|
56511
56733
|
}
|
|
56734
|
+
/**
|
|
56735
|
+
* Phase D-2 symlink loop. For each configured directory under the main
|
|
56736
|
+
* repository, creates a symbolic link from the new worktree to the
|
|
56737
|
+
* main-repo location (`<worktreePath>/<dir>` → `<repoRoot>/<dir>`).
|
|
56738
|
+
*
|
|
56739
|
+
* Fail-open semantics — the worktree IS already on disk and usable by
|
|
56740
|
+
* the time this runs, so a symlink failure must NOT abort the parent
|
|
56741
|
+
* `createUserWorktree` call. Per-entry failures are logged at debug or
|
|
56742
|
+
* warn level depending on cause:
|
|
56743
|
+
*
|
|
56744
|
+
* - **ENOENT on source** (the main repo does not have the directory):
|
|
56745
|
+
* debug log, skip. Typical for users who configure `node_modules`
|
|
56746
|
+
* but launch from a fresh clone where `npm install` hasn't run yet.
|
|
56747
|
+
* - **EEXIST on destination** (something already lives at the symlink
|
|
56748
|
+
* target inside the worktree): debug log, skip. No overwrite; the
|
|
56749
|
+
* existing content (whether file, dir, or stale link) wins.
|
|
56750
|
+
* - **Absolute path or path traversal in the configured value**:
|
|
56751
|
+
* warn log, skip the entry. Configured values must stay relative to
|
|
56752
|
+
* the repo root to prevent a setting from redirecting writes onto
|
|
56753
|
+
* `/etc`, `~`, or anywhere outside the repo subtree.
|
|
56754
|
+
* - **Other I/O errors**: warn log, continue to the next entry.
|
|
56755
|
+
*
|
|
56756
|
+
* Mirrors claude-code's `symlinkDirectories` helper (utils/worktree.ts).
|
|
56757
|
+
*/
|
|
56758
|
+
async symlinkConfiguredDirectories(worktreePath, configured) {
|
|
56759
|
+
let repoRootAbs;
|
|
56760
|
+
try {
|
|
56761
|
+
repoRootAbs = await fs15.realpath(this.sourceRepoPath);
|
|
56762
|
+
} catch {
|
|
56763
|
+
debugLogger25.warn(
|
|
56764
|
+
`symlinkConfiguredDirectories: cannot realpath sourceRepoPath "${this.sourceRepoPath}", skipping all entries`
|
|
56765
|
+
);
|
|
56766
|
+
return;
|
|
56767
|
+
}
|
|
56768
|
+
const gitDirAbs = path15.join(repoRootAbs, ".git");
|
|
56769
|
+
const qwenDirAbs = path15.join(repoRootAbs, ".qwen");
|
|
56770
|
+
const realWorktreePath = await fs15.realpath(worktreePath).catch(() => worktreePath);
|
|
56771
|
+
for (const raw2 of configured) {
|
|
56772
|
+
if (typeof raw2 !== "string" || raw2.length === 0) {
|
|
56773
|
+
debugLogger25.warn(
|
|
56774
|
+
`symlinkConfiguredDirectories: skipping non-string / empty entry: ${JSON.stringify(raw2)}`
|
|
56775
|
+
);
|
|
56776
|
+
continue;
|
|
56777
|
+
}
|
|
56778
|
+
if (path15.isAbsolute(raw2)) {
|
|
56779
|
+
debugLogger25.warn(
|
|
56780
|
+
`symlinkConfiguredDirectories: refusing absolute path "${raw2}"`
|
|
56781
|
+
);
|
|
56782
|
+
continue;
|
|
56783
|
+
}
|
|
56784
|
+
if (raw2.split(/[\\/]/).includes("..")) {
|
|
56785
|
+
debugLogger25.warn(
|
|
56786
|
+
`symlinkConfiguredDirectories: refusing path "${raw2}" \u2014 contains '..' segment`
|
|
56787
|
+
);
|
|
56788
|
+
continue;
|
|
56789
|
+
}
|
|
56790
|
+
const sourceAbs = path15.resolve(repoRootAbs, raw2);
|
|
56791
|
+
if (sourceAbs === repoRootAbs) {
|
|
56792
|
+
debugLogger25.warn(
|
|
56793
|
+
`symlinkConfiguredDirectories: refusing empty / repo-root path "${raw2}"`
|
|
56794
|
+
);
|
|
56795
|
+
continue;
|
|
56796
|
+
}
|
|
56797
|
+
if (!isWithinRoot(sourceAbs, repoRootAbs)) {
|
|
56798
|
+
debugLogger25.warn(
|
|
56799
|
+
`symlinkConfiguredDirectories: refusing path "${raw2}" \u2014 resolves outside repo root (${sourceAbs} vs ${repoRootAbs})`
|
|
56800
|
+
);
|
|
56801
|
+
continue;
|
|
56802
|
+
}
|
|
56803
|
+
if (isWithinRoot(sourceAbs, gitDirAbs)) {
|
|
56804
|
+
debugLogger25.warn(
|
|
56805
|
+
`symlinkConfiguredDirectories: refusing git-internal path "${raw2}"`
|
|
56806
|
+
);
|
|
56807
|
+
continue;
|
|
56808
|
+
}
|
|
56809
|
+
if (isWithinRoot(sourceAbs, qwenDirAbs)) {
|
|
56810
|
+
debugLogger25.warn(
|
|
56811
|
+
`symlinkConfiguredDirectories: refusing path "${raw2}" \u2014 the .qwen tree is CLI-managed; symlinking any of it could create a worktrees-inside-worktrees loop or alias CLI metadata.`
|
|
56812
|
+
);
|
|
56813
|
+
continue;
|
|
56814
|
+
}
|
|
56815
|
+
let sourceStat = null;
|
|
56816
|
+
try {
|
|
56817
|
+
sourceStat = await fs15.stat(sourceAbs);
|
|
56818
|
+
} catch (error) {
|
|
56819
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
56820
|
+
debugLogger25.debug(
|
|
56821
|
+
`symlinkConfiguredDirectories: source missing, skipping: ${sourceAbs}`
|
|
56822
|
+
);
|
|
56823
|
+
} else {
|
|
56824
|
+
debugLogger25.warn(
|
|
56825
|
+
`symlinkConfiguredDirectories: cannot stat ${sourceAbs}: ${error}`
|
|
56826
|
+
);
|
|
56827
|
+
}
|
|
56828
|
+
continue;
|
|
56829
|
+
}
|
|
56830
|
+
let realSource;
|
|
56831
|
+
try {
|
|
56832
|
+
realSource = await fs15.realpath(sourceAbs);
|
|
56833
|
+
} catch (error) {
|
|
56834
|
+
debugLogger25.warn(
|
|
56835
|
+
`symlinkConfiguredDirectories: cannot realpath source "${sourceAbs}": ${error}`
|
|
56836
|
+
);
|
|
56837
|
+
continue;
|
|
56838
|
+
}
|
|
56839
|
+
if (!isWithinRoot(realSource, repoRootAbs)) {
|
|
56840
|
+
debugLogger25.warn(
|
|
56841
|
+
`symlinkConfiguredDirectories: refusing path "${raw2}" \u2014 real source ${realSource} escapes repo root ${repoRootAbs}`
|
|
56842
|
+
);
|
|
56843
|
+
continue;
|
|
56844
|
+
}
|
|
56845
|
+
if (isWithinRoot(realSource, gitDirAbs)) {
|
|
56846
|
+
debugLogger25.warn(
|
|
56847
|
+
`symlinkConfiguredDirectories: refusing path "${raw2}" \u2014 real source ${realSource} resolves inside .git`
|
|
56848
|
+
);
|
|
56849
|
+
continue;
|
|
56850
|
+
}
|
|
56851
|
+
if (isWithinRoot(realSource, qwenDirAbs)) {
|
|
56852
|
+
debugLogger25.warn(
|
|
56853
|
+
`symlinkConfiguredDirectories: refusing path "${raw2}" \u2014 real source ${realSource} resolves inside .qwen`
|
|
56854
|
+
);
|
|
56855
|
+
continue;
|
|
56856
|
+
}
|
|
56857
|
+
const destAbs = path15.join(worktreePath, raw2);
|
|
56858
|
+
try {
|
|
56859
|
+
await fs15.mkdir(path15.dirname(destAbs), { recursive: true });
|
|
56860
|
+
} catch (error) {
|
|
56861
|
+
debugLogger25.warn(
|
|
56862
|
+
`symlinkConfiguredDirectories: cannot mkdir parent of ${destAbs}: ${error}`
|
|
56863
|
+
);
|
|
56864
|
+
continue;
|
|
56865
|
+
}
|
|
56866
|
+
let realDestParent;
|
|
56867
|
+
try {
|
|
56868
|
+
realDestParent = await fs15.realpath(path15.dirname(destAbs));
|
|
56869
|
+
} catch (error) {
|
|
56870
|
+
debugLogger25.warn(
|
|
56871
|
+
`symlinkConfiguredDirectories: cannot realpath dest parent for "${raw2}" (${path15.dirname(destAbs)}): ${error}`
|
|
56872
|
+
);
|
|
56873
|
+
continue;
|
|
56874
|
+
}
|
|
56875
|
+
if (!isWithinRoot(realDestParent, realWorktreePath)) {
|
|
56876
|
+
debugLogger25.warn(
|
|
56877
|
+
`symlinkConfiguredDirectories: refusing path "${raw2}" \u2014 dest parent ${realDestParent} escapes worktree root ${realWorktreePath} (committed-symlink chain)`
|
|
56878
|
+
);
|
|
56879
|
+
continue;
|
|
56880
|
+
}
|
|
56881
|
+
try {
|
|
56882
|
+
const symlinkType = sourceStat.isDirectory() ? process.platform === "win32" ? "junction" : "dir" : "file";
|
|
56883
|
+
await fs15.symlink(realSource, destAbs, symlinkType);
|
|
56884
|
+
debugLogger25.debug(
|
|
56885
|
+
`symlinkConfiguredDirectories: linked ${destAbs} \u2192 ${realSource} (${symlinkType})`
|
|
56886
|
+
);
|
|
56887
|
+
} catch (error) {
|
|
56888
|
+
if (isNodeError(error) && error.code === "EEXIST") {
|
|
56889
|
+
debugLogger25.debug(
|
|
56890
|
+
`symlinkConfiguredDirectories: destination exists, skipping: ${destAbs}`
|
|
56891
|
+
);
|
|
56892
|
+
} else {
|
|
56893
|
+
debugLogger25.warn(
|
|
56894
|
+
`symlinkConfiguredDirectories: failed to link ${destAbs} \u2192 ${realSource}: ${error}`
|
|
56895
|
+
);
|
|
56896
|
+
}
|
|
56897
|
+
}
|
|
56898
|
+
}
|
|
56899
|
+
}
|
|
56512
56900
|
/**
|
|
56513
56901
|
* Returns true if a local branch with the given name exists.
|
|
56514
56902
|
*
|
|
@@ -58681,7 +59069,9 @@ var AgentToolInvocation = class extends BaseToolInvocation {
|
|
|
58681
59069
|
`[Agent] getCurrentBranch failed at ${projectRoot}: ${error}`
|
|
58682
59070
|
);
|
|
58683
59071
|
}
|
|
58684
|
-
const created = await wtService.createUserWorktree(slug, parentBranch
|
|
59072
|
+
const created = await wtService.createUserWorktree(slug, parentBranch, {
|
|
59073
|
+
symlinkDirectories: this.config.getWorktreeSymlinkDirectories()
|
|
59074
|
+
});
|
|
58685
59075
|
if (!created.success || !created.worktree) {
|
|
58686
59076
|
return failWorktreeProvisioning(
|
|
58687
59077
|
`Failed to create isolation worktree: ${created.error ?? "unknown error"}`
|
|
@@ -65179,9 +65569,9 @@ function evaluateAwkReadOnly(args2) {
|
|
|
65179
65569
|
}
|
|
65180
65570
|
__name(evaluateAwkReadOnly, "evaluateAwkReadOnly");
|
|
65181
65571
|
function evaluateStatementReadOnly(node) {
|
|
65572
|
+
if (containsCommandSubstitutionAST(node)) return false;
|
|
65182
65573
|
switch (node.type) {
|
|
65183
65574
|
case "command":
|
|
65184
|
-
if (containsCommandSubstitutionAST(node)) return false;
|
|
65185
65575
|
return evaluateCommandReadOnly(node);
|
|
65186
65576
|
case "pipeline": {
|
|
65187
65577
|
for (const child of node.namedChildren) {
|
|
@@ -69393,12 +69783,12 @@ import { dirname as dirname9, isAbsolute as isAbsolute6, join as join21, relativ
|
|
|
69393
69783
|
|
|
69394
69784
|
// packages/core/src/utils/gitDiff.ts
|
|
69395
69785
|
init_esbuild_shims();
|
|
69396
|
-
import { execFile as
|
|
69786
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
69397
69787
|
import * as nodeFs from "node:fs";
|
|
69398
69788
|
import { access as access3, lstat as lstat3, open, readFile as readFile14, stat as stat6 } from "node:fs/promises";
|
|
69399
69789
|
import * as path31 from "node:path";
|
|
69400
|
-
import { promisify as
|
|
69401
|
-
var
|
|
69790
|
+
import { promisify as promisify3 } from "node:util";
|
|
69791
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
69402
69792
|
var GIT_TIMEOUT_MS = 5e3;
|
|
69403
69793
|
var MAX_FILES = 50;
|
|
69404
69794
|
var MAX_DIFF_SIZE_BYTES = 1e6;
|
|
@@ -69919,7 +70309,7 @@ __name(mapWithConcurrency, "mapWithConcurrency");
|
|
|
69919
70309
|
async function runGit(args2, cwd2) {
|
|
69920
70310
|
const fullArgs = ["-c", "core.quotepath=false", ...args2];
|
|
69921
70311
|
try {
|
|
69922
|
-
const { stdout } = await
|
|
70312
|
+
const { stdout } = await execFileAsync3("git", fullArgs, {
|
|
69923
70313
|
cwd: cwd2,
|
|
69924
70314
|
timeout: GIT_TIMEOUT_MS,
|
|
69925
70315
|
maxBuffer: 64 * 1024 * 1024,
|
|
@@ -73430,7 +73820,7 @@ import { posix as posix3, win32 } from "node:path";
|
|
|
73430
73820
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
73431
73821
|
import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync as readlinkSync2, realpathSync as rps } from "fs";
|
|
73432
73822
|
import * as actualFS from "node:fs";
|
|
73433
|
-
import { lstat as lstat4, readdir as readdir7, readlink as readlink2, realpath as
|
|
73823
|
+
import { lstat as lstat4, readdir as readdir7, readlink as readlink2, realpath as realpath4 } from "node:fs/promises";
|
|
73434
73824
|
|
|
73435
73825
|
// node_modules/minipass/dist/esm/index.js
|
|
73436
73826
|
init_esbuild_shims();
|
|
@@ -74332,7 +74722,7 @@ var defaultFS = {
|
|
|
74332
74722
|
lstat: lstat4,
|
|
74333
74723
|
readdir: readdir7,
|
|
74334
74724
|
readlink: readlink2,
|
|
74335
|
-
realpath:
|
|
74725
|
+
realpath: realpath4
|
|
74336
74726
|
}
|
|
74337
74727
|
};
|
|
74338
74728
|
var fsFromOption = /* @__PURE__ */ __name((fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS ? defaultFS : {
|
|
@@ -78528,11 +78918,11 @@ import { URL as URL3 } from "node:url";
|
|
|
78528
78918
|
|
|
78529
78919
|
// packages/core/src/utils/secure-browser-launcher.ts
|
|
78530
78920
|
init_esbuild_shims();
|
|
78531
|
-
import { execFile as
|
|
78532
|
-
import { promisify as
|
|
78921
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
78922
|
+
import { promisify as promisify4 } from "node:util";
|
|
78533
78923
|
import { platform } from "node:os";
|
|
78534
78924
|
import { URL as URL2 } from "node:url";
|
|
78535
|
-
var
|
|
78925
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
78536
78926
|
function validateUrl(url2) {
|
|
78537
78927
|
let parsedUrl;
|
|
78538
78928
|
try {
|
|
@@ -78592,7 +78982,7 @@ async function openBrowserSecurely(url2) {
|
|
|
78592
78982
|
stdio: "ignore"
|
|
78593
78983
|
};
|
|
78594
78984
|
try {
|
|
78595
|
-
await
|
|
78985
|
+
await execFileAsync4(command, args2, options2);
|
|
78596
78986
|
} catch (_error) {
|
|
78597
78987
|
if ((platformName === "linux" || platformName === "freebsd" || platformName === "openbsd") && command === "xdg-open") {
|
|
78598
78988
|
const fallbackCommands = [
|
|
@@ -78605,7 +78995,7 @@ async function openBrowserSecurely(url2) {
|
|
|
78605
78995
|
];
|
|
78606
78996
|
for (const fallbackCommand of fallbackCommands) {
|
|
78607
78997
|
try {
|
|
78608
|
-
await
|
|
78998
|
+
await execFileAsync4(fallbackCommand, [url2], options2);
|
|
78609
78999
|
return;
|
|
78610
79000
|
} catch {
|
|
78611
79001
|
continue;
|
|
@@ -78791,7 +79181,7 @@ var HybridTokenStorage = class extends BaseTokenStorage {
|
|
|
78791
79181
|
const forceFileStorage = process.env[FORCE_FILE_STORAGE_ENV_VAR] === "true";
|
|
78792
79182
|
if (!forceFileStorage) {
|
|
78793
79183
|
try {
|
|
78794
|
-
const { KeychainTokenStorage: KeychainTokenStorage2 } = await import("./keychain-token-storage-
|
|
79184
|
+
const { KeychainTokenStorage: KeychainTokenStorage2 } = await import("./keychain-token-storage-3552ENXE.js");
|
|
78795
79185
|
const keychainStorage = new KeychainTokenStorage2(this.serviceName);
|
|
78796
79186
|
const isAvailable = await keychainStorage.isAvailable();
|
|
78797
79187
|
if (isAvailable) {
|
|
@@ -81074,7 +81464,7 @@ __name(isEnabled, "isEnabled");
|
|
|
81074
81464
|
init_esbuild_shims();
|
|
81075
81465
|
import path39 from "node:path";
|
|
81076
81466
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
81077
|
-
import { execFile as
|
|
81467
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
81078
81468
|
|
|
81079
81469
|
// packages/core/src/utils/bundlePaths.ts
|
|
81080
81470
|
init_esbuild_shims();
|
|
@@ -81219,7 +81609,7 @@ async function runRipgrep(args2, signal) {
|
|
|
81219
81609
|
}
|
|
81220
81610
|
await ensureRipgrepHealthy(selection);
|
|
81221
81611
|
return new Promise((resolve31) => {
|
|
81222
|
-
const child =
|
|
81612
|
+
const child = execFile5(
|
|
81223
81613
|
selection.command,
|
|
81224
81614
|
args2,
|
|
81225
81615
|
{
|
|
@@ -83163,7 +83553,7 @@ import * as sysPath2 from "path";
|
|
|
83163
83553
|
|
|
83164
83554
|
// node_modules/readdirp/esm/index.js
|
|
83165
83555
|
init_esbuild_shims();
|
|
83166
|
-
import { stat as stat9, lstat as lstat5, readdir as readdir9, realpath as
|
|
83556
|
+
import { stat as stat9, lstat as lstat5, readdir as readdir9, realpath as realpath5 } from "node:fs/promises";
|
|
83167
83557
|
import { Readable } from "node:stream";
|
|
83168
83558
|
import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "node:path";
|
|
83169
83559
|
var EntryTypes = {
|
|
@@ -83342,7 +83732,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
83342
83732
|
if (stats && stats.isSymbolicLink()) {
|
|
83343
83733
|
const full = entry.fullPath;
|
|
83344
83734
|
try {
|
|
83345
|
-
const entryRealPath = await
|
|
83735
|
+
const entryRealPath = await realpath5(full);
|
|
83346
83736
|
const entryRealPathStats = await lstat5(entryRealPath);
|
|
83347
83737
|
if (entryRealPathStats.isFile()) {
|
|
83348
83738
|
return "file";
|
|
@@ -84051,7 +84441,7 @@ var NodeFsHandler = class {
|
|
|
84051
84441
|
* @param realpath
|
|
84052
84442
|
* @returns closer for the watcher instance.
|
|
84053
84443
|
*/
|
|
84054
|
-
async _handleDir(dir, stats, initialAdd, depth, target, wh,
|
|
84444
|
+
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath7) {
|
|
84055
84445
|
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
|
|
84056
84446
|
const tracked = parentDir.has(sysPath.basename(dir));
|
|
84057
84447
|
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
|
|
@@ -84062,7 +84452,7 @@ var NodeFsHandler = class {
|
|
|
84062
84452
|
let throttler;
|
|
84063
84453
|
let closer;
|
|
84064
84454
|
const oDepth = this.fsw.options.depth;
|
|
84065
|
-
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(
|
|
84455
|
+
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath7)) {
|
|
84066
84456
|
if (!target) {
|
|
84067
84457
|
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
|
|
84068
84458
|
if (this.fsw.closed)
|
|
@@ -87847,9 +88237,8 @@ var PermissionManager = class _PermissionManager {
|
|
|
87847
88237
|
*
|
|
87848
88238
|
* When a sub-command returns 'default' (no rule matches), it is resolved to
|
|
87849
88239
|
* the actual default permission using AST analysis:
|
|
87850
|
-
* - Command substitution detected → 'deny'
|
|
87851
88240
|
* - Read-only command (cd, ls, git status, etc.) → 'allow'
|
|
87852
|
-
* - Otherwise → 'ask'
|
|
88241
|
+
* - Otherwise (including command substitution) → 'ask'
|
|
87853
88242
|
*
|
|
87854
88243
|
* Example: with rules `allow: [git checkout *]`
|
|
87855
88244
|
* - "cd /path && git checkout -b feature" → allow (cd) + allow (rule) → allow
|
|
@@ -87883,19 +88272,26 @@ var PermissionManager = class _PermissionManager {
|
|
|
87883
88272
|
* Resolve 'default' permission to actual permission using AST analysis.
|
|
87884
88273
|
* This mirrors the logic in ShellToolInvocation.getDefaultPermission().
|
|
87885
88274
|
*
|
|
88275
|
+
* Command substitution ($(), ``, <(), >()) is NOT a hard deny here — it
|
|
88276
|
+
* falls through to 'ask' along with every other non-read-only command, so
|
|
88277
|
+
* the user (or YOLO mode) can decide. The user-facing warning is surfaced
|
|
88278
|
+
* by ShellToolInvocation.getConfirmationDetails so the confirmation prompt
|
|
88279
|
+
* still flags the substitution clearly. See issue #4093 for why a hard
|
|
88280
|
+
* deny here is wrong: it (a) cannot be overridden by YOLO mode and (b)
|
|
88281
|
+
* fires inconsistently based on whether the PermissionManager has
|
|
88282
|
+
* "relevant" rules for the surrounding compound command.
|
|
88283
|
+
*
|
|
87886
88284
|
* @param command - The shell command to analyze.
|
|
87887
|
-
* @returns '
|
|
88285
|
+
* @returns 'allow' for read-only, 'ask' otherwise.
|
|
87888
88286
|
*/
|
|
87889
88287
|
async resolveDefaultPermission(command) {
|
|
87890
|
-
if (detectCommandSubstitution(command)) {
|
|
87891
|
-
return "deny";
|
|
87892
|
-
}
|
|
87893
88288
|
try {
|
|
87894
88289
|
const isReadOnly = await isShellCommandReadOnlyAST(command);
|
|
87895
88290
|
if (isReadOnly) {
|
|
87896
88291
|
return "allow";
|
|
87897
88292
|
}
|
|
87898
|
-
} catch {
|
|
88293
|
+
} catch (e) {
|
|
88294
|
+
debugLogger60.warn("AST read-only check failed, falling back to ask:", e);
|
|
87899
88295
|
}
|
|
87900
88296
|
return "ask";
|
|
87901
88297
|
}
|
|
@@ -94342,9 +94738,9 @@ var SymlinkError = class extends Error {
|
|
|
94342
94738
|
symlink;
|
|
94343
94739
|
syscall = "symlink";
|
|
94344
94740
|
code = "TAR_SYMLINK_ERROR";
|
|
94345
|
-
constructor(
|
|
94741
|
+
constructor(symlink2, path84) {
|
|
94346
94742
|
super("TAR_SYMLINK_ERROR: Cannot extract through symbolic link");
|
|
94347
|
-
this.symlink =
|
|
94743
|
+
this.symlink = symlink2;
|
|
94348
94744
|
this.path = path84;
|
|
94349
94745
|
}
|
|
94350
94746
|
get name() {
|
|
@@ -105544,6 +105940,20 @@ var Config = class {
|
|
|
105544
105940
|
}
|
|
105545
105941
|
sessionId;
|
|
105546
105942
|
sessionData;
|
|
105943
|
+
/**
|
|
105944
|
+
* One-shot notice produced by `setupStartupWorktree` (Phase D-1) when the
|
|
105945
|
+
* CLI was launched with `--worktree`. The active entry point (TUI XOR
|
|
105946
|
+
* headless) reads it via {@link consumePendingStartupWorktreeNotice} on
|
|
105947
|
+
* the model's first prompt and skips Phase C's `restoreWorktreeContext`
|
|
105948
|
+
* for that turn — startup wins over the resumed-session sidecar. ACP is
|
|
105949
|
+
* gated out earlier in `gemini.tsx` (mutex with `--worktree`) so it
|
|
105950
|
+
* never reaches this slot.
|
|
105951
|
+
*
|
|
105952
|
+
* @invariant At most one consumer per process. If a future entry path
|
|
105953
|
+
* sets this slot without ever consuming, the string persists until
|
|
105954
|
+
* process exit (which dies with the process — no leak).
|
|
105955
|
+
*/
|
|
105956
|
+
pendingStartupWorktreeNotice = null;
|
|
105547
105957
|
debugLogger;
|
|
105548
105958
|
toolRegistry;
|
|
105549
105959
|
/**
|
|
@@ -105670,6 +106080,7 @@ var Config = class {
|
|
|
105670
106080
|
arenaManagerChangeCallback = null;
|
|
105671
106081
|
arenaAgentClient;
|
|
105672
106082
|
agentsSettings;
|
|
106083
|
+
worktreeSettings;
|
|
105673
106084
|
skipLoopDetection;
|
|
105674
106085
|
skipStartupContext;
|
|
105675
106086
|
bareMode;
|
|
@@ -105859,6 +106270,7 @@ var Config = class {
|
|
|
105859
106270
|
this.eventEmitter = params.eventEmitter;
|
|
105860
106271
|
this.arenaAgentClient = ArenaAgentClient.create();
|
|
105861
106272
|
this.agentsSettings = params.agents ?? {};
|
|
106273
|
+
this.worktreeSettings = params.worktree ?? {};
|
|
105862
106274
|
if (params.contextFileName) {
|
|
105863
106275
|
setGeminiMdFilename(params.contextFileName);
|
|
105864
106276
|
}
|
|
@@ -105888,8 +106300,8 @@ var Config = class {
|
|
|
105888
106300
|
isWorkspaceTrusted: this.isTrustedFolder()
|
|
105889
106301
|
});
|
|
105890
106302
|
this.enableManagedAutoMemory = params.enableManagedAutoMemory ?? true;
|
|
105891
|
-
this.enableManagedAutoDream = params.enableManagedAutoDream ??
|
|
105892
|
-
this.enableAutoSkill = params.enableAutoSkill ??
|
|
106303
|
+
this.enableManagedAutoDream = params.enableManagedAutoDream ?? true;
|
|
106304
|
+
this.enableAutoSkill = params.enableAutoSkill ?? true;
|
|
105893
106305
|
this.fastModel = params.fastModel || void 0;
|
|
105894
106306
|
this.disableAllHooks = params.disableAllHooks ?? false;
|
|
105895
106307
|
this.stopHookBlockingCap = resolveStopHookBlockingCap(
|
|
@@ -106645,6 +107057,27 @@ var Config = class {
|
|
|
106645
107057
|
getTargetDir() {
|
|
106646
107058
|
return this.targetDir;
|
|
106647
107059
|
}
|
|
107060
|
+
/**
|
|
107061
|
+
* Stashes a one-shot context message that the next user prompt will
|
|
107062
|
+
* inject into the model (see {@link pendingStartupWorktreeNotice}). Called
|
|
107063
|
+
* from `gemini.tsx` right after `loadCliConfig` when `--worktree` produced
|
|
107064
|
+
* a valid worktree. Pass `null` to clear (rarely needed).
|
|
107065
|
+
*/
|
|
107066
|
+
setPendingStartupWorktreeNotice(notice) {
|
|
107067
|
+
this.pendingStartupWorktreeNotice = notice;
|
|
107068
|
+
}
|
|
107069
|
+
/**
|
|
107070
|
+
* Reads and clears the pending startup-worktree notice. Returns `null`
|
|
107071
|
+
* when nothing is stashed (the common case). Each entry point (TUI /
|
|
107072
|
+
* headless / ACP) calls this on the model's first prompt; a non-null
|
|
107073
|
+
* return means the entry point should NOT additionally call
|
|
107074
|
+
* `restoreWorktreeContext()` for that prompt — startup overrides resume.
|
|
107075
|
+
*/
|
|
107076
|
+
consumePendingStartupWorktreeNotice() {
|
|
107077
|
+
const v = this.pendingStartupWorktreeNotice;
|
|
107078
|
+
this.pendingStartupWorktreeNotice = null;
|
|
107079
|
+
return v;
|
|
107080
|
+
}
|
|
106648
107081
|
getProjectRoot() {
|
|
106649
107082
|
return this.targetDir;
|
|
106650
107083
|
}
|
|
@@ -106926,6 +107359,17 @@ var Config = class {
|
|
|
106926
107359
|
getAgentsSettings() {
|
|
106927
107360
|
return this.agentsSettings;
|
|
106928
107361
|
}
|
|
107362
|
+
/**
|
|
107363
|
+
* Convenience accessor for `worktree.symlinkDirectories` — returns an
|
|
107364
|
+
* empty array when the setting is unset, so callers can pass the
|
|
107365
|
+
* result directly into the GitWorktreeService loop without nullchecks.
|
|
107366
|
+
*
|
|
107367
|
+
* (No general `getWorktreeSettings()` getter yet — add one when a
|
|
107368
|
+
* second field on `WorktreeSettings` justifies the broader API.)
|
|
107369
|
+
*/
|
|
107370
|
+
getWorktreeSymlinkDirectories() {
|
|
107371
|
+
return this.worktreeSettings.symlinkDirectories ?? [];
|
|
107372
|
+
}
|
|
106929
107373
|
/**
|
|
106930
107374
|
* Clean up Arena runtime. When `force` is true (e.g., /arena select --discard),
|
|
106931
107375
|
* always removes worktrees regardless of preserveArtifacts.
|
|
@@ -107765,25 +108209,25 @@ var Config = class {
|
|
|
107765
108209
|
if (options2?.forSubAgent) return;
|
|
107766
108210
|
const schema = this.jsonSchema;
|
|
107767
108211
|
await registerLazy(ToolNames.STRUCTURED_OUTPUT, async () => {
|
|
107768
|
-
const { SyntheticOutputTool } = await import("./syntheticOutput-
|
|
108212
|
+
const { SyntheticOutputTool } = await import("./syntheticOutput-AKTXC6FR.js");
|
|
107769
108213
|
return new SyntheticOutputTool(schema);
|
|
107770
108214
|
});
|
|
107771
108215
|
}, "registerStructuredOutputIfRequested");
|
|
107772
108216
|
if (this.getBareMode()) {
|
|
107773
108217
|
await registerLazy(ToolNames.READ_FILE, async () => {
|
|
107774
|
-
const { ReadFileTool } = await import("./read-file-
|
|
108218
|
+
const { ReadFileTool } = await import("./read-file-J7DH4OKV.js");
|
|
107775
108219
|
return new ReadFileTool(this);
|
|
107776
108220
|
});
|
|
107777
108221
|
await registerLazy(ToolNames.EDIT, async () => {
|
|
107778
|
-
const { EditTool } = await import("./edit-
|
|
108222
|
+
const { EditTool } = await import("./edit-4LLGNYVZ.js");
|
|
107779
108223
|
return new EditTool(this);
|
|
107780
108224
|
});
|
|
107781
108225
|
await registerLazy(ToolNames.NOTEBOOK_EDIT, async () => {
|
|
107782
|
-
const { NotebookEditTool } = await import("./notebook-edit-
|
|
108226
|
+
const { NotebookEditTool } = await import("./notebook-edit-P4QVLW6I.js");
|
|
107783
108227
|
return new NotebookEditTool(this);
|
|
107784
108228
|
});
|
|
107785
108229
|
await registerLazy(ToolNames.SHELL, async () => {
|
|
107786
|
-
const { ShellTool: ShellTool2 } = await import("./shell-
|
|
108230
|
+
const { ShellTool: ShellTool2 } = await import("./shell-ZNTQIRK6.js");
|
|
107787
108231
|
return new ShellTool2(this);
|
|
107788
108232
|
});
|
|
107789
108233
|
await registerStructuredOutputIfRequested();
|
|
@@ -107793,31 +108237,31 @@ var Config = class {
|
|
|
107793
108237
|
return registry;
|
|
107794
108238
|
}
|
|
107795
108239
|
await registerLazy(ToolNames.TOOL_SEARCH, async () => {
|
|
107796
|
-
const { ToolSearchTool } = await import("./tool-search-
|
|
108240
|
+
const { ToolSearchTool } = await import("./tool-search-C2EMLFBJ.js");
|
|
107797
108241
|
return new ToolSearchTool(this);
|
|
107798
108242
|
});
|
|
107799
108243
|
await registerLazy(ToolNames.AGENT, async () => {
|
|
107800
|
-
const { AgentTool: AgentTool2 } = await import("./agent-
|
|
108244
|
+
const { AgentTool: AgentTool2 } = await import("./agent-RY5EB3XR.js");
|
|
107801
108245
|
return new AgentTool2(this);
|
|
107802
108246
|
});
|
|
107803
108247
|
await registerLazy(ToolNames.TASK_STOP, async () => {
|
|
107804
|
-
const { TaskStopTool } = await import("./task-stop-
|
|
108248
|
+
const { TaskStopTool } = await import("./task-stop-2NYFR2ES.js");
|
|
107805
108249
|
return new TaskStopTool(this);
|
|
107806
108250
|
});
|
|
107807
108251
|
await registerLazy(ToolNames.SEND_MESSAGE, async () => {
|
|
107808
|
-
const { SendMessageTool } = await import("./send-message-
|
|
108252
|
+
const { SendMessageTool } = await import("./send-message-JUFP62VD.js");
|
|
107809
108253
|
return new SendMessageTool(this);
|
|
107810
108254
|
});
|
|
107811
108255
|
await registerLazy(ToolNames.SKILL, async () => {
|
|
107812
|
-
const { SkillTool } = await import("./skill-
|
|
108256
|
+
const { SkillTool } = await import("./skill-CFCUIY23.js");
|
|
107813
108257
|
return new SkillTool(this);
|
|
107814
108258
|
});
|
|
107815
108259
|
await registerLazy(ToolNames.LS, async () => {
|
|
107816
|
-
const { LSTool } = await import("./ls-
|
|
108260
|
+
const { LSTool } = await import("./ls-MYXAM7LJ.js");
|
|
107817
108261
|
return new LSTool(this);
|
|
107818
108262
|
});
|
|
107819
108263
|
await registerLazy(ToolNames.READ_FILE, async () => {
|
|
107820
|
-
const { ReadFileTool } = await import("./read-file-
|
|
108264
|
+
const { ReadFileTool } = await import("./read-file-J7DH4OKV.js");
|
|
107821
108265
|
return new ReadFileTool(this);
|
|
107822
108266
|
});
|
|
107823
108267
|
if (this.getUseRipgrep()) {
|
|
@@ -107830,7 +108274,7 @@ var Config = class {
|
|
|
107830
108274
|
}
|
|
107831
108275
|
if (useRipgrep) {
|
|
107832
108276
|
await registerLazy(ToolNames.GREP, async () => {
|
|
107833
|
-
const { RipGrepTool: RipGrepTool2 } = await import("./ripGrep-
|
|
108277
|
+
const { RipGrepTool: RipGrepTool2 } = await import("./ripGrep-33DECY4F.js");
|
|
107834
108278
|
return new RipGrepTool2(this);
|
|
107835
108279
|
});
|
|
107836
108280
|
} else {
|
|
@@ -107843,85 +108287,85 @@ var Config = class {
|
|
|
107843
108287
|
)
|
|
107844
108288
|
);
|
|
107845
108289
|
await registerLazy(ToolNames.GREP, async () => {
|
|
107846
|
-
const { GrepTool } = await import("./grep-
|
|
108290
|
+
const { GrepTool } = await import("./grep-2UUPSSIQ.js");
|
|
107847
108291
|
return new GrepTool(this);
|
|
107848
108292
|
});
|
|
107849
108293
|
}
|
|
107850
108294
|
} else {
|
|
107851
108295
|
await registerLazy(ToolNames.GREP, async () => {
|
|
107852
|
-
const { GrepTool } = await import("./grep-
|
|
108296
|
+
const { GrepTool } = await import("./grep-2UUPSSIQ.js");
|
|
107853
108297
|
return new GrepTool(this);
|
|
107854
108298
|
});
|
|
107855
108299
|
}
|
|
107856
108300
|
await registerLazy(ToolNames.GLOB, async () => {
|
|
107857
|
-
const { GlobTool } = await import("./glob-
|
|
108301
|
+
const { GlobTool } = await import("./glob-6X6OCEWE.js");
|
|
107858
108302
|
return new GlobTool(this);
|
|
107859
108303
|
});
|
|
107860
108304
|
await registerLazy(ToolNames.EDIT, async () => {
|
|
107861
|
-
const { EditTool } = await import("./edit-
|
|
108305
|
+
const { EditTool } = await import("./edit-4LLGNYVZ.js");
|
|
107862
108306
|
return new EditTool(this);
|
|
107863
108307
|
});
|
|
107864
108308
|
await registerLazy(ToolNames.NOTEBOOK_EDIT, async () => {
|
|
107865
|
-
const { NotebookEditTool } = await import("./notebook-edit-
|
|
108309
|
+
const { NotebookEditTool } = await import("./notebook-edit-P4QVLW6I.js");
|
|
107866
108310
|
return new NotebookEditTool(this);
|
|
107867
108311
|
});
|
|
107868
108312
|
await registerLazy(ToolNames.WRITE_FILE, async () => {
|
|
107869
|
-
const { WriteFileTool } = await import("./write-file-
|
|
108313
|
+
const { WriteFileTool } = await import("./write-file-EEPVRS4Q.js");
|
|
107870
108314
|
return new WriteFileTool(this);
|
|
107871
108315
|
});
|
|
107872
108316
|
await registerLazy(ToolNames.SHELL, async () => {
|
|
107873
|
-
const { ShellTool: ShellTool2 } = await import("./shell-
|
|
108317
|
+
const { ShellTool: ShellTool2 } = await import("./shell-ZNTQIRK6.js");
|
|
107874
108318
|
return new ShellTool2(this);
|
|
107875
108319
|
});
|
|
107876
108320
|
await registerLazy(ToolNames.TODO_WRITE, async () => {
|
|
107877
|
-
const { TodoWriteTool } = await import("./todoWrite-
|
|
108321
|
+
const { TodoWriteTool } = await import("./todoWrite-WHZ2O2XP.js");
|
|
107878
108322
|
return new TodoWriteTool(this);
|
|
107879
108323
|
});
|
|
107880
108324
|
await registerLazy(ToolNames.ASK_USER_QUESTION, async () => {
|
|
107881
|
-
const { AskUserQuestionTool } = await import("./askUserQuestion-
|
|
108325
|
+
const { AskUserQuestionTool } = await import("./askUserQuestion-R3MKD2JT.js");
|
|
107882
108326
|
return new AskUserQuestionTool(this);
|
|
107883
108327
|
});
|
|
107884
108328
|
if (!this.sdkMode) {
|
|
107885
108329
|
await registerLazy(ToolNames.EXIT_PLAN_MODE, async () => {
|
|
107886
|
-
const { ExitPlanModeTool } = await import("./exitPlanMode-
|
|
108330
|
+
const { ExitPlanModeTool } = await import("./exitPlanMode-WD5IH7NS.js");
|
|
107887
108331
|
return new ExitPlanModeTool(this);
|
|
107888
108332
|
});
|
|
107889
108333
|
}
|
|
107890
108334
|
await registerLazy(ToolNames.ENTER_WORKTREE, async () => {
|
|
107891
|
-
const { EnterWorktreeTool } = await import("./enter-worktree-
|
|
108335
|
+
const { EnterWorktreeTool } = await import("./enter-worktree-E2R5XAFT.js");
|
|
107892
108336
|
return new EnterWorktreeTool(this);
|
|
107893
108337
|
});
|
|
107894
108338
|
await registerLazy(ToolNames.EXIT_WORKTREE, async () => {
|
|
107895
|
-
const { ExitWorktreeTool } = await import("./exit-worktree-
|
|
108339
|
+
const { ExitWorktreeTool } = await import("./exit-worktree-YVBYYYDD.js");
|
|
107896
108340
|
return new ExitWorktreeTool(this);
|
|
107897
108341
|
});
|
|
107898
108342
|
await registerLazy(ToolNames.WEB_FETCH, async () => {
|
|
107899
|
-
const { WebFetchTool } = await import("./web-fetch-
|
|
108343
|
+
const { WebFetchTool } = await import("./web-fetch-S6MZXPZ5.js");
|
|
107900
108344
|
return new WebFetchTool(this);
|
|
107901
108345
|
});
|
|
107902
108346
|
if (this.isLspEnabled() && this.getLspClient()) {
|
|
107903
108347
|
await registerLazy(ToolNames.LSP, async () => {
|
|
107904
|
-
const { LspTool } = await import("./lsp-
|
|
108348
|
+
const { LspTool } = await import("./lsp-PFGI35JL.js");
|
|
107905
108349
|
return new LspTool(this);
|
|
107906
108350
|
});
|
|
107907
108351
|
}
|
|
107908
108352
|
await registerStructuredOutputIfRequested();
|
|
107909
108353
|
if (this.isCronEnabled()) {
|
|
107910
108354
|
await registerLazy(ToolNames.CRON_CREATE, async () => {
|
|
107911
|
-
const { CronCreateTool } = await import("./cron-create-
|
|
108355
|
+
const { CronCreateTool } = await import("./cron-create-BTEOGHPH.js");
|
|
107912
108356
|
return new CronCreateTool(this);
|
|
107913
108357
|
});
|
|
107914
108358
|
await registerLazy(ToolNames.CRON_LIST, async () => {
|
|
107915
|
-
const { CronListTool } = await import("./cron-list-
|
|
108359
|
+
const { CronListTool } = await import("./cron-list-SV6QRZW2.js");
|
|
107916
108360
|
return new CronListTool(this);
|
|
107917
108361
|
});
|
|
107918
108362
|
await registerLazy(ToolNames.CRON_DELETE, async () => {
|
|
107919
|
-
const { CronDeleteTool } = await import("./cron-delete-
|
|
108363
|
+
const { CronDeleteTool } = await import("./cron-delete-56CEWELN.js");
|
|
107920
108364
|
return new CronDeleteTool(this);
|
|
107921
108365
|
});
|
|
107922
108366
|
}
|
|
107923
108367
|
await registerLazy(ToolNames.MONITOR, async () => {
|
|
107924
|
-
const { MonitorTool } = await import("./monitor-
|
|
108368
|
+
const { MonitorTool } = await import("./monitor-VUHPEGUW.js");
|
|
107925
108369
|
return new MonitorTool(this);
|
|
107926
108370
|
});
|
|
107927
108371
|
if (this.pendingMcpBudgetCallback) {
|
|
@@ -117323,9 +117767,9 @@ var formatMemoryUsage = /* @__PURE__ */ __name((bytes) => {
|
|
|
117323
117767
|
// packages/core/src/utils/memoryDiagnostics.ts
|
|
117324
117768
|
init_esbuild_shims();
|
|
117325
117769
|
import { readdir as readdir15, readFile as readFile26 } from "node:fs/promises";
|
|
117326
|
-
import { execFile as
|
|
117770
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
117327
117771
|
import process7 from "node:process";
|
|
117328
|
-
import { promisify as
|
|
117772
|
+
import { promisify as promisify5 } from "node:util";
|
|
117329
117773
|
import v8 from "node:v8";
|
|
117330
117774
|
var RSS_HEAP_GAP_RATIO = 10;
|
|
117331
117775
|
var RSS_HEAP_GAP_MIN_BYTES = 256 * 1024 * 1024;
|
|
@@ -117334,7 +117778,7 @@ var ACTIVE_HANDLES_THRESHOLD = 256;
|
|
|
117334
117778
|
var ACTIVE_REQUESTS_THRESHOLD = 100;
|
|
117335
117779
|
var OPEN_FD_THRESHOLD = 500;
|
|
117336
117780
|
var debugLogger91 = createDebugLogger("MEMORY_DIAGNOSTICS");
|
|
117337
|
-
var
|
|
117781
|
+
var execFileAsync5 = promisify5(execFile6);
|
|
117338
117782
|
async function collectMemoryDiagnostics(options2 = {}) {
|
|
117339
117783
|
const now = options2.now ?? (() => /* @__PURE__ */ new Date());
|
|
117340
117784
|
const platform8 = options2.platform ?? process7.platform;
|
|
@@ -117463,7 +117907,7 @@ async function collectProcessTreeMemoryUsage(platform8) {
|
|
|
117463
117907
|
if (platform8 === "win32") {
|
|
117464
117908
|
throw new Error("process tree RSS probe is unavailable on win32");
|
|
117465
117909
|
}
|
|
117466
|
-
const { stdout } = await
|
|
117910
|
+
const { stdout } = await execFileAsync5("ps", ["-axo", "pid=,ppid=,rss="], {
|
|
117467
117911
|
maxBuffer: 1024 * 1024,
|
|
117468
117912
|
timeout: 5e3
|
|
117469
117913
|
});
|
|
@@ -118816,7 +119260,7 @@ __name(doesToolInvocationMatch, "doesToolInvocationMatch");
|
|
|
118816
119260
|
|
|
118817
119261
|
// packages/core/src/utils/shell-utils.ts
|
|
118818
119262
|
import {
|
|
118819
|
-
execFile as
|
|
119263
|
+
execFile as execFile7,
|
|
118820
119264
|
execFileSync
|
|
118821
119265
|
} from "node:child_process";
|
|
118822
119266
|
import { accessSync, constants as fsConstants } from "node:fs";
|
|
@@ -119621,6 +120065,21 @@ function detectCommandSubstitution(command) {
|
|
|
119621
120065
|
return false;
|
|
119622
120066
|
}
|
|
119623
120067
|
__name(detectCommandSubstitution, "detectCommandSubstitution");
|
|
120068
|
+
var COMMAND_SUBSTITUTION_WARNING = "Contains command substitution ($(...), backticks, <(...), or >(...)).";
|
|
120069
|
+
function hasShellSubstitution(rawCommand) {
|
|
120070
|
+
if (typeof rawCommand !== "string" || rawCommand.length === 0) return false;
|
|
120071
|
+
if (detectCommandSubstitution(rawCommand)) return true;
|
|
120072
|
+
const stripped = stripShellWrapper(rawCommand);
|
|
120073
|
+
return stripped !== rawCommand && detectCommandSubstitution(stripped);
|
|
120074
|
+
}
|
|
120075
|
+
__name(hasShellSubstitution, "hasShellSubstitution");
|
|
120076
|
+
function buildShellExecWarnings(strippedCommand, rawCommand) {
|
|
120077
|
+
if (hasShellSubstitution(rawCommand) || detectCommandSubstitution(strippedCommand)) {
|
|
120078
|
+
return [COMMAND_SUBSTITUTION_WARNING];
|
|
120079
|
+
}
|
|
120080
|
+
return void 0;
|
|
120081
|
+
}
|
|
120082
|
+
__name(buildShellExecWarnings, "buildShellExecWarnings");
|
|
119624
120083
|
async function checkCommandPermissions(command, config, sessionAllowlist) {
|
|
119625
120084
|
if (detectCommandSubstitution(command)) {
|
|
119626
120085
|
return {
|
|
@@ -119772,7 +120231,7 @@ async function checkCommandPermissions(command, config, sessionAllowlist) {
|
|
|
119772
120231
|
__name(checkCommandPermissions, "checkCommandPermissions");
|
|
119773
120232
|
function execCommand(command, args2, options2 = {}) {
|
|
119774
120233
|
return new Promise((resolve31, reject) => {
|
|
119775
|
-
const child =
|
|
120234
|
+
const child = execFile7(
|
|
119776
120235
|
command,
|
|
119777
120236
|
args2,
|
|
119778
120237
|
{ encoding: "utf8", ...options2 },
|
|
@@ -121720,10 +122179,15 @@ var ShellToolInvocation = class extends BaseToolInvocation {
|
|
|
121720
122179
|
}
|
|
121721
122180
|
/**
|
|
121722
122181
|
* AST-based permission check for the shell command.
|
|
122182
|
+
* - Substitution-bearing commands (any form, including inside an
|
|
122183
|
+
* env-prefix wrapper that `stripShellWrapper` would discard) → 'ask'
|
|
121723
122184
|
* - Read-only commands (via AST analysis) → 'allow'
|
|
121724
122185
|
* - All other commands → 'ask'
|
|
121725
122186
|
*/
|
|
121726
122187
|
async getDefaultPermission() {
|
|
122188
|
+
if (hasShellSubstitution(this.params.command)) {
|
|
122189
|
+
return "ask";
|
|
122190
|
+
}
|
|
121727
122191
|
const command = stripShellWrapper(this.params.command);
|
|
121728
122192
|
try {
|
|
121729
122193
|
const isReadOnly = await isShellCommandReadOnlyAST(command);
|
|
@@ -121784,6 +122248,7 @@ var ShellToolInvocation = class extends BaseToolInvocation {
|
|
|
121784
122248
|
} catch (e) {
|
|
121785
122249
|
debugLogger93.warn("Failed to extract command rules:", e);
|
|
121786
122250
|
}
|
|
122251
|
+
const warnings = buildShellExecWarnings(command, this.params.command);
|
|
121787
122252
|
const confirmationDetails = {
|
|
121788
122253
|
type: "exec",
|
|
121789
122254
|
title: "Confirm Shell Command",
|
|
@@ -121793,6 +122258,9 @@ var ShellToolInvocation = class extends BaseToolInvocation {
|
|
|
121793
122258
|
onConfirm: /* @__PURE__ */ __name(async (_outcome, _payload) => {
|
|
121794
122259
|
}, "onConfirm")
|
|
121795
122260
|
};
|
|
122261
|
+
if (warnings) {
|
|
122262
|
+
confirmationDetails.warnings = warnings;
|
|
122263
|
+
}
|
|
121796
122264
|
return confirmationDetails;
|
|
121797
122265
|
}
|
|
121798
122266
|
async execute(signal, updateOutput, shellExecutionConfig, setPidCallback, setPromoteAbortControllerCallback) {
|
|
@@ -123464,6 +123932,9 @@ export {
|
|
|
123464
123932
|
normalizeMonitorCommand,
|
|
123465
123933
|
hasUnsafeMonitorBackgroundOperator,
|
|
123466
123934
|
detectCommandSubstitution,
|
|
123935
|
+
COMMAND_SUBSTITUTION_WARNING,
|
|
123936
|
+
hasShellSubstitution,
|
|
123937
|
+
buildShellExecWarnings,
|
|
123467
123938
|
checkCommandPermissions,
|
|
123468
123939
|
execCommand,
|
|
123469
123940
|
resolveCommandPath,
|