mirror of
https://github.com/cline/cline.git
synced 2026-09-01 23:19:18 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d644dbdd22 | |||
| 172d630d72 | |||
| 860354ec9d | |||
| 2bd21f8a45 | |||
| 2f33f71ebd | |||
| dec10aaec3 | |||
| c09705a45f | |||
| 73058c871a | |||
| 03211f1364 | |||
| 65e9727c65 | |||
| 884fbfb21e | |||
| 0c880c7bb1 |
@@ -1,5 +1,6 @@
|
||||
demo.gif filter=lfs diff=lfs merge=lfs -text
|
||||
assets/docs/demo.gif filter=lfs diff=lfs merge=lfs -text
|
||||
webview-ui/src/assets/cline_kanban_demo.mp4 filter=lfs diff=lfs merge=lfs -text
|
||||
webview-ui/src/assets/cline_kanban_demo.webm filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
* text=auto eol=lf
|
||||
|
||||
@@ -91,3 +91,21 @@ jobs:
|
||||
echo ""
|
||||
echo "📦 Install with: npm install -g cline"
|
||||
echo "🔗 NPM: https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Post release to Slack
|
||||
uses: slackapi/slack-github-action@v3.0.1
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
|
||||
payload: |
|
||||
channel: "C0APVKGGZFC"
|
||||
text: "Cline CLI v${{ steps.version.outputs.version }}"
|
||||
blocks:
|
||||
- type: "section"
|
||||
text:
|
||||
type: "mrkdwn"
|
||||
text: "*Cline CLI v${{ steps.version.outputs.version }}*"
|
||||
- type: "context"
|
||||
elements:
|
||||
- type: "mrkdwn"
|
||||
text: "<https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}|View on npm>"
|
||||
|
||||
@@ -53,11 +53,12 @@ jobs:
|
||||
|
||||
- name: Verify LFS media assets are resolved
|
||||
run: |
|
||||
FILE="webview-ui/src/assets/cline_kanban_demo.webm"
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Publish Extension as Pre-release
|
||||
env:
|
||||
|
||||
@@ -136,11 +136,12 @@ jobs:
|
||||
|
||||
- name: Verify LFS media assets are resolved
|
||||
run: |
|
||||
FILE="webview-ui/src/assets/cline_kanban_demo.webm"
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Package and Publish Extension
|
||||
env:
|
||||
@@ -196,3 +197,25 @@ jobs:
|
||||
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Post release to Slack
|
||||
uses: slackapi/slack-github-action@v3.0.1
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
|
||||
payload: |
|
||||
channel: "C0APVKGGZFC"
|
||||
text: "Cline ${{ steps.resolve_tag.outputs.tag }}"
|
||||
blocks:
|
||||
- type: "section"
|
||||
text:
|
||||
type: "mrkdwn"
|
||||
text: "*Cline ${{ steps.resolve_tag.outputs.tag }}*"
|
||||
- type: "section"
|
||||
text:
|
||||
type: "mrkdwn"
|
||||
text: ${{ toJSON(steps.changelog.outputs.content) }}
|
||||
- type: "context"
|
||||
elements:
|
||||
- type: "mrkdwn"
|
||||
text: "Full Changelog: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}"
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@
|
||||
"ts"
|
||||
],
|
||||
"spec": [
|
||||
"src/**/__tests__/*.ts"
|
||||
"src/**/__tests__/*.ts",
|
||||
"src/test/services/**/*.test.ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register",
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## [3.77.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Add "Lazy Teammate Mode" experimental toggle
|
||||
- `read_file` tool now supports chunked reading for targeted file access
|
||||
|
||||
### Fixed
|
||||
|
||||
- Exclude `new_task` tool from system prompt in yolo/headless mode
|
||||
- Fix Kanban demo video formatting
|
||||
|
||||
### Changed
|
||||
|
||||
- Polish `Notification` hook functionality
|
||||
|
||||
## [3.76.0]
|
||||
|
||||
### Added
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 512 535">
|
||||
<!-- Generator: Adobe Illustrator 29.8.5, SVG Export Plug-In . SVG Version: 2.1.1 Build 2) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st0" d="M500.6,300.5c-9-20.7-17.9-41.4-26.9-62.1-.7-2-.3-4.4-.3-6.4.4-9,1.1-18,1.4-27,2.8-28.4-6.5-58-25.2-79.6-15.1-18.1-36.6-30.7-59.6-35.5-8.1-1.8-16.6-1.6-25-2.1-10-.7-20-1-30-1.7,2-11.9,1-24.1-3.7-35.3-5.8-14.1-16.8-25.9-30.6-32.5-14.4-7-31.5-8.2-46.7-3.1-16,5.2-29.5,17-36.8,32.1-4.9,10-6.8,21.2-6.1,32.2-19.7-1-39.4-2.2-59.1-3.1-26.8.5-53,11.7-72,30.6-20.2,19.5-31.7,47-32.3,75-.5,9.3-1,18.7-1.5,28-.2,2.1,0,4.1-1.2,6-9.8,16.8-19.5,33.7-29.4,50.6-2.2,4.1-4.9,8-6.6,12.3-2,5.7-1.2,12.2,1.3,17.6,8.9,19.5,17.6,39.2,26.5,58.7.8,1.9,1.5,3.7,1.3,5.8-.6,10.3-1.1,20.7-1.7,31-1.5,21.2,3,42.6,13.5,61.1,8.8,15.8,21.6,29.4,37.1,38.9,13.9,8.7,29.7,13.9,46,15.4,72,3.9,144,7.7,216,11.5,20.1,1.8,40.8-2.8,58.5-12.5,18.8-10.1,34.2-26,44.1-44.9,6.5-12.6,10.5-26.4,11.7-40.5.7-12.4,1.2-24.7,2-37.1,0-3.3,1.9-5.5,3.3-8.2,6.6-11.8,13.5-23.4,20.1-35.2,3.7-6.9,8.1-13.4,11.6-20.4,3.2-6.1,3.2-13.5.3-19.7ZM218.5,316.5c-9.7,7.1-21.3,12.3-33.5,12.5-17.6,1-35.1-5.3-49-16-4.6-3.2-8.1-7.5-9.6-13,0-1.8-.7-3.6,1.7-3.8,4,1,7.9,2.6,12,3.5,22.8,5.6,47.6,5.9,71,4.8,6.5-.2,13-1.3,19.5-.9-2.7,5.6-7.1,9.2-12,12.9ZM276,449.7c-14,.5-28,.1-42-.2-2.1,0-4.3,0-6.4-.4-.9-2.1.6-3.2,1.7-4.8,4.8-5.9,11-11,18.7-12.4,8.4-1.6,16.5,1.2,23.5,5.5,4.7,3,9.2,6.3,12.6,10.8-2.6,1.1-5.3,1.4-8.1,1.4ZM390.4,319.4c-16.4,14.2-38.8,21.8-60.4,18.4-13.2-1.6-24.7-8.6-34.1-17.7-3-3-6.2-6.5-8.1-10.4.5-1,1.2-1.6,2.2-1.6,2.8-.2,5.7.7,8.5,1.1,16,2.9,32.3,4.9,48.5,5.5,14.3.4,28.2-.2,42.2-3.6,2.2-.6,3.7-.3,5.8.5-1.1,2.9-2.2,5.7-4.6,7.8Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,5 +1,19 @@
|
||||
# cline
|
||||
|
||||
## [2.12.0]
|
||||
|
||||
### Added
|
||||
|
||||
- `read_file` tool now supports chunked reading for targeted file access
|
||||
|
||||
### Fixed
|
||||
|
||||
- Exclude `new_task` tool from system prompt in yolo/headless mode
|
||||
|
||||
### Changed
|
||||
|
||||
- Polish `Notification` hook functionality
|
||||
|
||||
## [2.9.0]
|
||||
|
||||
### Added
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cline",
|
||||
"version": "2.11.0",
|
||||
"version": "2.12.0",
|
||||
"description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
|
||||
"main": "dist/lib.mjs",
|
||||
"types": "dist/lib.d.ts",
|
||||
|
||||
@@ -373,6 +373,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
|
||||
handleKeyboardSequence,
|
||||
handleCtrlShortcut,
|
||||
deleteCharBefore,
|
||||
deleteWordBefore,
|
||||
insertText: insertTextAtCursor,
|
||||
} = useTextInput()
|
||||
|
||||
@@ -423,10 +424,12 @@ export const ChatView: React.FC<ChatViewProps> = ({
|
||||
| null
|
||||
>(null)
|
||||
|
||||
// Handle Home/End keys from raw stdin (Ink doesn't expose these in useInput)
|
||||
// Handle Home/End keys and Option+Backspace from raw stdin
|
||||
// (Ink doesn't properly expose these in useInput)
|
||||
useHomeEndKeys({
|
||||
onHome: useCallback(() => setCursorPos(0), [setCursorPos]),
|
||||
onEnd: useCallback(() => setCursorPos(textInputRef.current.length), [setCursorPos]),
|
||||
onOptionBackspace: useCallback(() => deleteWordBefore(), [deleteWordBefore]),
|
||||
isActive: !activePanel, // Only active when no panel is open
|
||||
})
|
||||
|
||||
@@ -1159,13 +1162,20 @@ export const ChatView: React.FC<ChatViewProps> = ({
|
||||
return
|
||||
}
|
||||
|
||||
// 3. Handle Option+arrow via key.meta (backup - Ink sometimes parses these instead of passing raw sequence)
|
||||
// 3. Handle Option+arrow via key.meta
|
||||
// Terminals send these as either:
|
||||
// - CSI format: \x1b[1;3D → Ink sets key.meta + key.leftArrow
|
||||
// - Meta prefix: \x1bb → Ink sets key.meta + input='b' (emacs: Meta-b = word left)
|
||||
// We handle both formats here.
|
||||
// Note: Option+Backspace is NOT handled here — it's handled exclusively by
|
||||
// useHomeEndKeys to avoid double word-delete (both this handler and
|
||||
// useHomeEndKeys fire for the same \x1b\x7f input).
|
||||
if (key.meta) {
|
||||
if (key.leftArrow) {
|
||||
if (key.leftArrow || input === "b") {
|
||||
setCursorPos(findWordStart(textInputRef.current, cursorPosRef.current))
|
||||
return
|
||||
}
|
||||
if (key.rightArrow) {
|
||||
if (key.rightArrow || input === "f") {
|
||||
setCursorPos(findWordEnd(textInputRef.current, cursorPosRef.current))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -38,3 +38,58 @@ export const OPTION_RIGHT_SEQUENCES = new Set([
|
||||
"\x1bf", // Meta+f - emacs style
|
||||
"\x1b[1;3C", // CSI 1;3 C - xterm with modifiers
|
||||
])
|
||||
|
||||
// Option+Backspace / Ctrl+Backspace (delete word backwards) sequences
|
||||
// Multiple paths cover different terminals:
|
||||
// - \x1b\x7f: terminals with "Option as Meta/Esc+" configured
|
||||
// - \x08: Ctrl+Backspace on macOS/Linux (detected in enhanced-stdin proxy)
|
||||
// - \x1b[27;3;127~: modifyOtherKeys protocol (detected in enhanced-stdin proxy)
|
||||
// - \x1b[127;3u: Kitty keyboard protocol
|
||||
export const OPTION_BACKSPACE_SEQUENCES = new Set([
|
||||
"\x1b\x7f", // Meta+DEL - terminals with "Option as Meta/Esc+" configured
|
||||
"\x1b\x08", // Meta+BS - some terminals send BS (0x08) instead of DEL (0x7f)
|
||||
"\x1b[27;3;127~", // xterm modifyOtherKeys format: Alt+DEL (modifier=3, key=127)
|
||||
"\x1b[127;3u", // Kitty keyboard protocol format: DEL with Alt modifier
|
||||
])
|
||||
|
||||
/**
|
||||
* Enable xterm modifyOtherKeys mode for detecting modifier keys.
|
||||
*
|
||||
* By default, many terminals (especially iTerm2 without "Esc+" configured)
|
||||
* send identical bytes for Option+Backspace and regular Backspace (\x7f).
|
||||
* Enabling modifyOtherKeys level 2 makes the terminal encode modified keys
|
||||
* in a distinct format. For example:
|
||||
* Alt+Backspace → \x1b[27;3;127~ (instead of just \x7f)
|
||||
*
|
||||
* Level 2 is required because level 1 doesn't modify Alt+Backspace (it only
|
||||
* modifies keys without existing encodings, and Backspace has one).
|
||||
* However, level 2 also modifies Ctrl+C, Ctrl+D, Tab, etc. The stdin proxy
|
||||
* in enhanced-stdin.ts translates these back to their original bytes so
|
||||
* Ink's input handling and signal handlers continue to work.
|
||||
*
|
||||
* Terminals that don't support modifyOtherKeys silently ignore the sequence.
|
||||
*
|
||||
* Note: We also keep Kitty protocol sequences (\x1b[127;3u) in
|
||||
* OPTION_BACKSPACE_SEQUENCES for terminals that natively use that protocol.
|
||||
*/
|
||||
export function enableEnhancedKeyboardMode(): void {
|
||||
// xterm modifyOtherKeys level 2 — all modified keys get CSI encoding.
|
||||
// The stdin proxy translates control keys (Ctrl+C, etc.) back to their
|
||||
// original bytes so Ink and signal handlers aren't broken.
|
||||
process.stdout.write("\x1b[>4;2m")
|
||||
|
||||
// Kitty keyboard protocol (progressive enhancement, flags=1).
|
||||
// VSCode terminal (xterm.js) supports this but NOT modifyOtherKeys.
|
||||
// With Kitty, modified keys use the format: \x1b[{keycode};{modifier}u
|
||||
// e.g. Option+Backspace → \x1b[127;3u, Ctrl+Backspace → \x1b[127;5u
|
||||
// Terminals that don't support it silently ignore the sequence.
|
||||
process.stdout.write("\x1b[>1u")
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable enhanced keyboard mode, restoring default terminal behavior.
|
||||
*/
|
||||
export function disableEnhancedKeyboardMode(): void {
|
||||
process.stdout.write("\x1b[>4;0m") // disable modifyOtherKeys
|
||||
process.stdout.write("\x1b[<u") // disable Kitty keyboard protocol
|
||||
}
|
||||
|
||||
@@ -1,36 +1,50 @@
|
||||
/**
|
||||
* Hook to detect Home/End keys from raw stdin.
|
||||
* Hook to detect Home/End keys and Option+Backspace from raw stdin.
|
||||
*
|
||||
* Ink's useInput hook parses Home/End keys but doesn't expose them in the key object,
|
||||
* and sets input to '' for these keys (because they're in nonAlphanumericKeys).
|
||||
* This hook subscribes to raw stdin events to detect Home/End before Ink processes them.
|
||||
*
|
||||
* For Option+Backspace, many terminals (like iTerm2 without "Esc+" configured) send
|
||||
* the same byte (\x7f) for both regular Backspace and Option+Backspace. To distinguish
|
||||
* them, the CLI enables xterm's modifyOtherKeys mode at startup (in index.ts), which
|
||||
* makes the terminal send \x1b[27;3;127~ for Alt+Backspace. A stdin proxy (enhanced-stdin)
|
||||
* intercepts this sequence before Ink sees it and emits an "option-backspace" event.
|
||||
*
|
||||
* This hook listens for:
|
||||
* - Raw stdin events (via internal_eventEmitter) for Home/End keys and \x1b\x7f
|
||||
* - Enhanced key events (via enhancedKeyEvents) for protocol-detected Option+Backspace
|
||||
*/
|
||||
|
||||
import { useStdin } from "ink"
|
||||
import { useCallback, useEffect, useRef } from "react"
|
||||
|
||||
import { END_SEQUENCES, HOME_SEQUENCES } from "../constants/keyboard"
|
||||
import { END_SEQUENCES, HOME_SEQUENCES, OPTION_BACKSPACE_SEQUENCES } from "../constants/keyboard"
|
||||
import { enhancedKeyEvents } from "../utils/enhanced-stdin"
|
||||
|
||||
interface UseHomeEndKeysOptions {
|
||||
onHome: () => void
|
||||
onEnd: () => void
|
||||
onOptionBackspace?: () => void
|
||||
isActive?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to raw stdin to detect Home/End keys.
|
||||
* These keys are parsed by Ink but not exposed in useInput's key object.
|
||||
* Subscribe to raw stdin to detect Home/End keys and Option+Backspace.
|
||||
* These keys are parsed by Ink but not properly exposed in useInput's key object.
|
||||
*/
|
||||
export function useHomeEndKeys({ onHome, onEnd, isActive = true }: UseHomeEndKeysOptions): void {
|
||||
export function useHomeEndKeys({ onHome, onEnd, onOptionBackspace, isActive = true }: UseHomeEndKeysOptions): void {
|
||||
// Use refs to avoid stale closure issues
|
||||
const onHomeRef = useRef(onHome)
|
||||
const onEndRef = useRef(onEnd)
|
||||
const onOptionBackspaceRef = useRef(onOptionBackspace)
|
||||
onHomeRef.current = onHome
|
||||
onEndRef.current = onEnd
|
||||
onOptionBackspaceRef.current = onOptionBackspace
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { internal_eventEmitter } = useStdin() as any
|
||||
|
||||
// Handle raw stdin data for Home/End keys and traditional \x1b\x7f
|
||||
const handleInput = useCallback((data: Buffer | string) => {
|
||||
const s = typeof data === "string" ? data : data.toString()
|
||||
|
||||
@@ -38,17 +52,35 @@ export function useHomeEndKeys({ onHome, onEnd, isActive = true }: UseHomeEndKey
|
||||
onHomeRef.current()
|
||||
} else if (END_SEQUENCES.has(s)) {
|
||||
onEndRef.current()
|
||||
} else if (OPTION_BACKSPACE_SEQUENCES.has(s)) {
|
||||
// Handles \x1b\x7f from terminals with "Esc+" configured
|
||||
onOptionBackspaceRef.current?.()
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Handle enhanced key events from stdin proxy (modifyOtherKeys protocol)
|
||||
const handleOptionBackspace = useCallback(() => {
|
||||
onOptionBackspaceRef.current?.()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isActive || !internal_eventEmitter) {
|
||||
if (!isActive) {
|
||||
return
|
||||
}
|
||||
|
||||
internal_eventEmitter.on("input", handleInput)
|
||||
return () => {
|
||||
internal_eventEmitter.removeListener("input", handleInput)
|
||||
// Listen for protocol-detected Option+Backspace from stdin proxy
|
||||
enhancedKeyEvents.on("option-backspace", handleOptionBackspace)
|
||||
|
||||
// Listen for raw stdin events (Home/End keys, traditional \x1b\x7f)
|
||||
if (internal_eventEmitter) {
|
||||
internal_eventEmitter.on("input", handleInput)
|
||||
}
|
||||
}, [isActive, internal_eventEmitter, handleInput])
|
||||
|
||||
return () => {
|
||||
enhancedKeyEvents.removeListener("option-backspace", handleOptionBackspace)
|
||||
if (internal_eventEmitter) {
|
||||
internal_eventEmitter.removeListener("input", handleInput)
|
||||
}
|
||||
}
|
||||
}, [isActive, internal_eventEmitter, handleInput, handleOptionBackspace])
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* Supports essential terminal shortcuts:
|
||||
* - Option+Left/Right: move by word (via escape sequences)
|
||||
* - Option+Backspace: delete word backwards (handled by useHomeEndKeys)
|
||||
* - Ctrl+A/E: start/end of line
|
||||
* - Ctrl+W: delete word backwards
|
||||
* - Ctrl+U: delete to start of line
|
||||
@@ -27,7 +28,7 @@ type KeyboardSequence =
|
||||
|
||||
/**
|
||||
* Parse keyboard escape sequences for special key combinations.
|
||||
* Only handles Option+arrow - Home/End are handled by useHomeEndKeys.
|
||||
* Handles Option+arrow. Home/End and Option+Backspace are handled by useHomeEndKeys.
|
||||
*/
|
||||
function parseKeyboardSequence(input: string): KeyboardSequence {
|
||||
if (OPTION_LEFT_SEQUENCES.has(input)) {
|
||||
@@ -83,6 +84,7 @@ export interface UseTextInputReturn {
|
||||
|
||||
// Deletion
|
||||
deleteCharBefore: () => void
|
||||
deleteWordBefore: () => void
|
||||
|
||||
// Keyboard shortcut handlers
|
||||
handleKeyboardSequence: (input: string) => boolean
|
||||
@@ -219,6 +221,7 @@ export function useTextInput(): UseTextInputReturn {
|
||||
insertText,
|
||||
setCursorPos,
|
||||
deleteCharBefore,
|
||||
deleteWordBefore,
|
||||
handleKeyboardSequence,
|
||||
handleCtrlShortcut,
|
||||
}
|
||||
|
||||
+31
-1
@@ -29,6 +29,7 @@ import { version as CLI_VERSION } from "../package.json"
|
||||
import { runAcpMode } from "./acp/index.js"
|
||||
import { App } from "./components/App"
|
||||
import { KanbanMigrationView } from "./components/KanbanMigrationView"
|
||||
import { disableEnhancedKeyboardMode, enableEnhancedKeyboardMode } from "./constants/keyboard"
|
||||
import { checkRawModeSupport } from "./context/StdinContext"
|
||||
import { createCliHostBridgeProvider } from "./controllers"
|
||||
import { CliCommentReviewController } from "./controllers/CliCommentReviewController"
|
||||
@@ -36,6 +37,7 @@ import { CliWebviewProvider } from "./controllers/CliWebviewProvider"
|
||||
import { isAuthConfigured } from "./utils/auth"
|
||||
import { restoreConsole, suppressConsoleUnlessVerbose } from "./utils/console"
|
||||
import { printInfo, printWarning } from "./utils/display"
|
||||
import { createEnhancedStdin, destroyEnhancedStdin } from "./utils/enhanced-stdin"
|
||||
import {
|
||||
forwardSignalToKanbanProcess,
|
||||
isKanbanCommandAvailable,
|
||||
@@ -538,6 +540,12 @@ function setupSignalHandlers() {
|
||||
}
|
||||
isShuttingDown = true
|
||||
|
||||
// Restore terminal keyboard mode before any output or exit.
|
||||
// Without this, if the process crashes or is killed, the terminal stays
|
||||
// in modifyOtherKeys/Kitty mode, corrupting subsequent input in the shell.
|
||||
disableEnhancedKeyboardMode()
|
||||
destroyEnhancedStdin()
|
||||
|
||||
// Notify components to hide UI before shutdown
|
||||
shutdownEvent.fire()
|
||||
|
||||
@@ -602,6 +610,14 @@ function setupSignalHandlers() {
|
||||
process.on("uncaughtException", (reason: unknown) => {
|
||||
onUnhandledException(reason, "uncaughtException")
|
||||
})
|
||||
|
||||
// Safety net: restore terminal keyboard mode on any exit path.
|
||||
// This is synchronous-only (Node.js constraint for 'exit' event) but
|
||||
// disableEnhancedKeyboardMode() is just process.stdout.write() which is sync.
|
||||
// Prevents terminal corruption if process.exit() is called from unexpected code paths.
|
||||
process.on("exit", () => {
|
||||
disableEnhancedKeyboardMode()
|
||||
})
|
||||
}
|
||||
|
||||
setupSignalHandlers()
|
||||
@@ -690,11 +706,23 @@ async function runInkApp(element: React.ReactElement, cleanup: () => Promise<voi
|
||||
// Clear terminal for clean UI - robot will render at row 1
|
||||
process.stdout.write("\x1b[2J\x1b[3J\x1b[H")
|
||||
|
||||
// Enable xterm modifyOtherKeys mode so terminals send distinct sequences
|
||||
// for modifier+key combos (e.g., Alt+Backspace → \x1b[27;3;127~).
|
||||
// This must be enabled before creating the stdin proxy.
|
||||
enableEnhancedKeyboardMode()
|
||||
|
||||
// Create a stdin proxy that intercepts modifier key sequences before Ink
|
||||
// sees them. Without this, Ink would insert the raw escape sequences as text.
|
||||
const enhancedStdin = createEnhancedStdin()
|
||||
|
||||
// Note: incrementalRendering is disabled because it causes UI glitches on terminal resize.
|
||||
// Ink's incremental rendering tries to erase N lines based on previous output height,
|
||||
// but when the terminal shrinks, this leaves artifacts. Gemini CLI only enables
|
||||
// incrementalRendering when alternateBuffer is also enabled (which we don't use).
|
||||
const { waitUntilExit, unmount } = render(element, { exitOnCtrlC: true })
|
||||
const { waitUntilExit, unmount } = render(element, {
|
||||
exitOnCtrlC: true,
|
||||
stdin: enhancedStdin as any,
|
||||
})
|
||||
|
||||
try {
|
||||
await waitUntilExit()
|
||||
@@ -704,6 +732,8 @@ async function runInkApp(element: React.ReactElement, cleanup: () => Promise<voi
|
||||
} catch {
|
||||
// Already unmounted
|
||||
}
|
||||
destroyEnhancedStdin()
|
||||
disableEnhancedKeyboardMode()
|
||||
restoreConsole()
|
||||
await cleanup()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { translateModifyOtherKeys } from "./enhanced-stdin"
|
||||
|
||||
describe("translateModifyOtherKeys", () => {
|
||||
// Modifier encoding: value = bits + 1
|
||||
// modifier 2 = Shift, 3 = Alt, 4 = Shift+Alt, 5 = Ctrl, 6 = Shift+Ctrl, 7 = Alt+Ctrl
|
||||
|
||||
describe("Alt+Backspace (word delete)", () => {
|
||||
it("returns null for Alt+Backspace (modifyOtherKeys: mod=3, key=127)", () => {
|
||||
// \x1b[27;3;127~ — iTerm2 with modifyOtherKeys level 2
|
||||
expect(translateModifyOtherKeys(3, 127)).toBeNull()
|
||||
})
|
||||
|
||||
it("returns null for Alt+Backspace (Kitty: key=127, mod=3)", () => {
|
||||
// \x1b[127;3u — Kitty keyboard protocol
|
||||
expect(translateModifyOtherKeys(3, 127)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Ctrl+Backspace (word delete)", () => {
|
||||
it("returns null for Ctrl+Backspace (Kitty: key=127, mod=5)", () => {
|
||||
// \x1b[127;5u — VSCode terminal via Kitty protocol
|
||||
expect(translateModifyOtherKeys(5, 127)).toBeNull()
|
||||
})
|
||||
|
||||
it("returns null for Ctrl+Alt+Backspace (mod=7, key=127)", () => {
|
||||
expect(translateModifyOtherKeys(7, 127)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Ctrl+letter translation", () => {
|
||||
it("translates Ctrl+C (mod=5, key=99) to \\x03", () => {
|
||||
expect(translateModifyOtherKeys(5, 99)).toBe("\x03")
|
||||
})
|
||||
|
||||
it("translates Ctrl+D (mod=5, key=100) to \\x04", () => {
|
||||
expect(translateModifyOtherKeys(5, 100)).toBe("\x04")
|
||||
})
|
||||
|
||||
it("translates Ctrl+Z (mod=5, key=122) to \\x1a", () => {
|
||||
expect(translateModifyOtherKeys(5, 122)).toBe("\x1a")
|
||||
})
|
||||
|
||||
it("translates Ctrl+A (mod=5, key=65) to \\x01", () => {
|
||||
expect(translateModifyOtherKeys(5, 65)).toBe("\x01")
|
||||
})
|
||||
|
||||
it("translates Ctrl+W (mod=5, key=119) to \\x17", () => {
|
||||
// Ctrl+W = word delete (traditional)
|
||||
expect(translateModifyOtherKeys(5, 119)).toBe("\x17")
|
||||
})
|
||||
})
|
||||
|
||||
describe("Alt+letter translation (Meta prefix)", () => {
|
||||
it("translates Alt+b (mod=3, key=98) to ESC+b", () => {
|
||||
// Option+Left sends \x1bb in many terminals
|
||||
expect(translateModifyOtherKeys(3, 98)).toBe("\x1bb")
|
||||
})
|
||||
|
||||
it("translates Alt+f (mod=3, key=102) to ESC+f", () => {
|
||||
// Option+Right sends \x1bf in many terminals
|
||||
expect(translateModifyOtherKeys(3, 102)).toBe("\x1bf")
|
||||
})
|
||||
|
||||
it("translates Alt+d (mod=3, key=100) to ESC+d", () => {
|
||||
expect(translateModifyOtherKeys(3, 100)).toBe("\x1bd")
|
||||
})
|
||||
})
|
||||
|
||||
describe("Alt+Ctrl+letter translation", () => {
|
||||
it("translates Alt+Ctrl+C (mod=7, key=99) to ESC+\\x03", () => {
|
||||
expect(translateModifyOtherKeys(7, 99)).toBe("\x1b\x03")
|
||||
})
|
||||
})
|
||||
|
||||
describe("Shift-only (passthrough)", () => {
|
||||
it("translates Shift+A (mod=2, key=65) to 'A'", () => {
|
||||
// Shift alone: no alt, no ctrl — just pass through keycode
|
||||
expect(translateModifyOtherKeys(2, 65)).toBe("A")
|
||||
})
|
||||
})
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("handles Tab keycode (mod=5, key=9) — Ctrl+Tab", () => {
|
||||
// keycode 9 < 64, so doesn't hit the Ctrl+letter branch
|
||||
expect(translateModifyOtherKeys(5, 9)).toBe("\t")
|
||||
})
|
||||
|
||||
it("handles Enter keycode (mod=3, key=13) — Alt+Enter", () => {
|
||||
// keycode 13 < 64, alt+key where key isn't in letter range
|
||||
expect(translateModifyOtherKeys(3, 13)).toBe("\x1b\r")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Enhanced stdin proxy for intercepting modified key sequences.
|
||||
*
|
||||
* When xterm modifyOtherKeys mode (level 2) is enabled, terminals send
|
||||
* distinct escape sequences for ALL modifier+key combos. For example:
|
||||
* Alt+Backspace → \x1b[27;3;127~
|
||||
* Ctrl+C → \x1b[27;5;99~
|
||||
*
|
||||
* This is great for distinguishing Alt+Backspace from Backspace, but it
|
||||
* breaks Ink's handling of Ctrl+C, Ctrl+D, Tab, etc. since Ink expects
|
||||
* the traditional byte encodings (\x03, \x04, \x09).
|
||||
*
|
||||
* This module provides a PassThrough stream that sits between real stdin
|
||||
* and Ink, translating modifyOtherKeys sequences back to their traditional
|
||||
* encodings — except for Alt+Backspace which gets emitted as an event.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from "events"
|
||||
import { PassThrough } from "stream"
|
||||
|
||||
/** Event emitter for enhanced key events that were intercepted from stdin */
|
||||
export const enhancedKeyEvents = new EventEmitter()
|
||||
|
||||
/**
|
||||
* Regex matching the xterm modifyOtherKeys CSI format: \x1b[27;{modifier};{keycode}~
|
||||
* Groups: modifier (number), keycode (number)
|
||||
*/
|
||||
const MODIFY_OTHER_KEYS_RE = /\x1b\[27;(\d+);(\d+)~/g
|
||||
|
||||
/**
|
||||
* Also match Kitty keyboard protocol format: \x1b[{keycode};{modifier}u
|
||||
*/
|
||||
const KITTY_KEY_RE = /\x1b\[(\d+);(\d+)u/g
|
||||
|
||||
const ESC = "\x1b"
|
||||
|
||||
let activeProxy: (PassThrough & { setRawMode?: (mode: boolean) => void; isTTY?: boolean }) | null = null
|
||||
let teardownProxyListeners: (() => void) | null = null
|
||||
|
||||
/**
|
||||
* Translate a modifyOtherKeys sequence back to its traditional encoding.
|
||||
*
|
||||
* Modifier values (from xterm):
|
||||
* 2 = Shift, 3 = Alt, 4 = Shift+Alt, 5 = Ctrl, 6 = Shift+Ctrl,
|
||||
* 7 = Alt+Ctrl, 8 = Shift+Alt+Ctrl
|
||||
*
|
||||
* @returns the translated bytes string, or null if this should be emitted as an event
|
||||
*/
|
||||
export function translateModifyOtherKeys(modifier: number, keycode: number): string | null {
|
||||
// xterm modifier encoding is (bits + 1), so:
|
||||
// modifier 2 = Shift (bit 0)
|
||||
// modifier 3 = Alt (bit 1)
|
||||
// modifier 5 = Ctrl (bit 2)
|
||||
// modifier 7 = Alt+Ctrl (bit 1 + bit 2)
|
||||
const bits = modifier - 1
|
||||
const hasAlt = (bits & 0x02) !== 0
|
||||
const hasCtrl = (bits & 0x04) !== 0
|
||||
|
||||
// Alt+Backspace (DEL=127) or Ctrl+Backspace (DEL=127) → emit as word-delete event
|
||||
// Alt+Backspace: modifier=3 (hasAlt), Ctrl+Backspace: modifier=5 (hasCtrl)
|
||||
// Both should trigger word deletion. Ctrl+Backspace is sent by VSCode terminal
|
||||
// via Kitty protocol as \x1b[127;5u.
|
||||
if (keycode === 127 && (hasAlt || hasCtrl)) {
|
||||
return null // signal to emit event
|
||||
}
|
||||
|
||||
// Ctrl+letter: translate back to control character
|
||||
// Ctrl+C (99) → \x03, Ctrl+D (100) → \x04, etc.
|
||||
if (hasCtrl && keycode >= 64 && keycode <= 127) {
|
||||
const ctrlChar = String.fromCharCode(keycode & 0x1f)
|
||||
if (hasAlt) {
|
||||
return "\x1b" + ctrlChar // Alt+Ctrl+key → ESC + control char
|
||||
}
|
||||
return ctrlChar
|
||||
}
|
||||
|
||||
// Alt+letter: translate back to ESC + character (Meta prefix)
|
||||
if (hasAlt && !hasCtrl) {
|
||||
return "\x1b" + String.fromCharCode(keycode)
|
||||
}
|
||||
|
||||
// For other combinations, pass through the original keycode
|
||||
return String.fromCharCode(keycode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an enhanced stdin proxy that intercepts modifier key sequences.
|
||||
*
|
||||
* Returns a PassThrough stream that proxies stdin but translates modifyOtherKeys
|
||||
* sequences back to traditional encodings. Alt+Backspace is emitted as an event
|
||||
* on enhancedKeyEvents instead of being passed to Ink.
|
||||
*/
|
||||
export function createEnhancedStdin(): PassThrough & { setRawMode?: (mode: boolean) => void; isTTY?: boolean } {
|
||||
if (activeProxy) {
|
||||
return activeProxy
|
||||
}
|
||||
|
||||
const proxy = new PassThrough() as PassThrough & {
|
||||
setRawMode?: (mode: boolean) => void
|
||||
isTTY?: boolean
|
||||
fd?: number
|
||||
}
|
||||
|
||||
// Proxy terminal properties and methods that Ink needs.
|
||||
// Ink's useInput hook calls stdin.ref()/unref() and setRawMode(),
|
||||
// which exist on process.stdin (a TTY Socket) but not on PassThrough.
|
||||
proxy.isTTY = process.stdin.isTTY
|
||||
;(proxy as any).fd = (process.stdin as any).fd
|
||||
if (typeof process.stdin.setRawMode === "function") {
|
||||
;(proxy as any).setRawMode = (mode: boolean) => {
|
||||
process.stdin.setRawMode(mode)
|
||||
}
|
||||
}
|
||||
;(proxy as any).ref = () => {
|
||||
if (typeof (process.stdin as any).ref === "function") {
|
||||
;(process.stdin as any).ref()
|
||||
}
|
||||
}
|
||||
;(proxy as any).unref = () => {
|
||||
if (typeof (process.stdin as any).unref === "function") {
|
||||
;(process.stdin as any).unref()
|
||||
}
|
||||
}
|
||||
|
||||
const onData = (data: Buffer) => {
|
||||
const str = data.toString()
|
||||
|
||||
// Ctrl+Backspace sends \x08 (BS) on macOS/Linux — distinct from regular
|
||||
// Backspace (\x7f). Intercept it as word-delete since Option+Backspace
|
||||
// is indistinguishable from Backspace in terminals without modifyOtherKeys.
|
||||
// This matches Gemini CLI and Codex CLI behavior.
|
||||
if (str === "\x08") {
|
||||
enhancedKeyEvents.emit("option-backspace")
|
||||
return
|
||||
}
|
||||
|
||||
// Fast path: if no ESC character, pass through as-is
|
||||
if (!str.includes(ESC)) {
|
||||
proxy.write(data)
|
||||
return
|
||||
}
|
||||
|
||||
// Check for modifyOtherKeys or Kitty sequences
|
||||
// Use a combined regex approach: find and replace all protocol sequences
|
||||
let result = ""
|
||||
let lastIndex = 0
|
||||
|
||||
// Process modifyOtherKeys format: \x1b[27;{mod};{key}~
|
||||
const combined = str
|
||||
const matches: Array<{ index: number; length: number; modifier: number; keycode: number; format: string }> = []
|
||||
|
||||
// Find all modifyOtherKeys matches
|
||||
MODIFY_OTHER_KEYS_RE.lastIndex = 0
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = MODIFY_OTHER_KEYS_RE.exec(combined)) !== null) {
|
||||
matches.push({
|
||||
index: match.index,
|
||||
length: match[0].length,
|
||||
modifier: Number.parseInt(match[1], 10),
|
||||
keycode: Number.parseInt(match[2], 10),
|
||||
format: "modifyOtherKeys",
|
||||
})
|
||||
}
|
||||
|
||||
// Find all Kitty protocol matches
|
||||
KITTY_KEY_RE.lastIndex = 0
|
||||
while ((match = KITTY_KEY_RE.exec(combined)) !== null) {
|
||||
matches.push({
|
||||
index: match.index,
|
||||
length: match[0].length,
|
||||
modifier: Number.parseInt(match[2], 10), // Note: Kitty format is [keycode;modifier]
|
||||
keycode: Number.parseInt(match[1], 10),
|
||||
format: "kitty",
|
||||
})
|
||||
}
|
||||
|
||||
// If no protocol sequences found, pass through as-is
|
||||
if (matches.length === 0) {
|
||||
proxy.write(data)
|
||||
return
|
||||
}
|
||||
|
||||
// Sort by index and process
|
||||
matches.sort((a, b) => a.index - b.index)
|
||||
|
||||
for (const m of matches) {
|
||||
// Add any text before this match
|
||||
if (m.index > lastIndex) {
|
||||
result += combined.slice(lastIndex, m.index)
|
||||
}
|
||||
|
||||
const translated = translateModifyOtherKeys(m.modifier, m.keycode)
|
||||
if (translated === null) {
|
||||
// Emit as event (e.g., Alt+Backspace)
|
||||
// Flush what we have so far
|
||||
if (result.length > 0) {
|
||||
proxy.write(Buffer.from(result))
|
||||
result = ""
|
||||
}
|
||||
enhancedKeyEvents.emit("option-backspace")
|
||||
} else {
|
||||
result += translated
|
||||
}
|
||||
|
||||
lastIndex = m.index + m.length
|
||||
}
|
||||
|
||||
// Add remaining text after last match
|
||||
if (lastIndex < combined.length) {
|
||||
result += combined.slice(lastIndex)
|
||||
}
|
||||
|
||||
if (result.length > 0) {
|
||||
proxy.write(Buffer.from(result))
|
||||
}
|
||||
}
|
||||
|
||||
const onEnd = () => proxy.end()
|
||||
const onError = (err: Error) => proxy.emit("error", err)
|
||||
|
||||
process.stdin.on("data", onData)
|
||||
process.stdin.on("end", onEnd)
|
||||
process.stdin.on("error", onError)
|
||||
|
||||
teardownProxyListeners = () => {
|
||||
process.stdin.off("data", onData)
|
||||
process.stdin.off("end", onEnd)
|
||||
process.stdin.off("error", onError)
|
||||
}
|
||||
activeProxy = proxy
|
||||
|
||||
return proxy
|
||||
}
|
||||
|
||||
export function destroyEnhancedStdin(): void {
|
||||
teardownProxyListeners?.()
|
||||
teardownProxyListeners = null
|
||||
activeProxy = null
|
||||
}
|
||||
Generated
+72
-23
@@ -139,6 +139,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz",
|
||||
"integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
@@ -148,6 +149,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz",
|
||||
"integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.4",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
@@ -157,7 +159,8 @@
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.33.5",
|
||||
@@ -1082,6 +1085,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz",
|
||||
"integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.5"
|
||||
},
|
||||
@@ -1167,6 +1171,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
||||
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
@@ -1190,6 +1195,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
@@ -1217,6 +1223,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
@@ -1280,6 +1287,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
|
||||
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.0.0",
|
||||
"@radix-ui/react-arrow": "1.1.7",
|
||||
@@ -1312,6 +1320,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
@@ -1336,6 +1345,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
@@ -1360,6 +1370,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
@@ -1456,6 +1467,7 @@
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
@@ -1919,6 +1931,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz",
|
||||
"integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.5"
|
||||
},
|
||||
@@ -2105,6 +2118,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
||||
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
@@ -2128,6 +2142,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
@@ -2155,6 +2170,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
@@ -2218,6 +2234,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
|
||||
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.0.0",
|
||||
"@radix-ui/react-arrow": "1.1.7",
|
||||
@@ -2250,6 +2267,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
@@ -2274,6 +2292,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
@@ -2298,6 +2317,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
@@ -2422,6 +2442,7 @@
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
@@ -2450,6 +2471,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz",
|
||||
"integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.5"
|
||||
},
|
||||
@@ -2492,6 +2514,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
||||
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
@@ -2515,6 +2538,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
@@ -2542,6 +2566,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
@@ -2605,6 +2630,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
|
||||
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.0.0",
|
||||
"@radix-ui/react-arrow": "1.1.7",
|
||||
@@ -2637,6 +2663,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
@@ -2661,6 +2688,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
@@ -2685,6 +2713,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
@@ -2776,6 +2805,7 @@
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
@@ -2927,13 +2957,15 @@
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
@@ -2949,6 +2981,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
@@ -2964,6 +2997,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
|
||||
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
@@ -2979,6 +3013,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
@@ -2997,6 +3032,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
@@ -3015,6 +3051,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
@@ -3030,6 +3067,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
@@ -3049,6 +3087,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
||||
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
@@ -3067,6 +3106,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
||||
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
@@ -3085,6 +3125,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
@@ -3100,6 +3141,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
|
||||
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/rect": "1.1.1"
|
||||
},
|
||||
@@ -3118,6 +3160,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
|
||||
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
@@ -3135,7 +3178,8 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@shikijs/core": {
|
||||
"version": "3.22.0",
|
||||
@@ -3796,7 +3840,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
|
||||
"integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -3881,7 +3924,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
|
||||
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3946,7 +3988,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -4073,6 +4114,7 @@
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
@@ -4084,7 +4126,8 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/arkregex": {
|
||||
"version": "0.0.3",
|
||||
@@ -5181,7 +5224,8 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "6.0.2",
|
||||
@@ -5452,7 +5496,8 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/detect-port": {
|
||||
"version": "1.5.1",
|
||||
@@ -5485,8 +5530,7 @@
|
||||
"version": "0.0.1312386",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz",
|
||||
"integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
@@ -6576,6 +6620,7 @@
|
||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -7310,7 +7355,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ink/-/ink-6.3.0.tgz",
|
||||
"integrity": "sha512-2CbJAa7XeziZYe6pDS5RVLirRY28iSGMQuEV8jRU5NQsONQNfcR/BZHHc9vkMg2lGYTHTM2pskxC1YmY28p6bQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@alcalzone/ansi-tokenize": "^0.2.0",
|
||||
"ansi-escapes": "^7.0.0",
|
||||
@@ -8090,7 +8134,6 @@
|
||||
"resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
|
||||
"integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10.16.0"
|
||||
}
|
||||
@@ -8246,6 +8289,7 @@
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
@@ -10140,7 +10184,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -10516,7 +10559,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -10541,6 +10583,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
|
||||
"integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.7",
|
||||
"react-style-singleton": "^2.2.3",
|
||||
@@ -10566,6 +10609,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
||||
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"react-style-singleton": "^2.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
@@ -10587,19 +10631,22 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/react-remove-scroll/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/react-style-singleton": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"get-nonce": "^1.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
@@ -10621,7 +10668,8 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
@@ -12173,7 +12221,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -12427,7 +12474,6 @@
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
|
||||
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/unist": "^3.0.0",
|
||||
"bail": "^2.0.0",
|
||||
@@ -12654,6 +12700,7 @@
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
||||
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
@@ -12674,13 +12721,15 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
@@ -12702,7 +12751,8 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
@@ -13200,7 +13250,6 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
|
||||
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
Generated
+44
-24
@@ -9,7 +9,7 @@
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.12.0",
|
||||
"axios": "1.13.6",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"chalk": "5.6.2",
|
||||
"commander": "^9.4.1",
|
||||
@@ -135,16 +135,17 @@
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
|
||||
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||
"version": "1.13.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
|
||||
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"follow-redirects": "^1.15.11",
|
||||
"form-data": "^4.0.5",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
@@ -226,6 +227,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
@@ -256,6 +258,7 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
@@ -303,6 +306,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -339,6 +343,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
@@ -360,6 +365,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@@ -368,6 +374,7 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@@ -376,6 +383,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
@@ -387,6 +395,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
@@ -411,15 +420,16 @@
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
@@ -430,9 +440,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
@@ -455,6 +465,7 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -463,6 +474,7 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
@@ -486,6 +498,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
@@ -503,6 +516,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -514,6 +528,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -525,6 +540,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
@@ -539,6 +555,7 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
@@ -585,6 +602,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@@ -593,6 +611,7 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -601,6 +620,7 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
@@ -1044,12 +1064,12 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
|
||||
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||
"version": "1.13.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
|
||||
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"follow-redirects": "^1.15.11",
|
||||
"form-data": "^4.0.5",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
@@ -1224,14 +1244,14 @@
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.12.0",
|
||||
"axios": "1.13.6",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"chalk": "5.6.2",
|
||||
"commander": "^9.4.1",
|
||||
|
||||
Generated
+4
-4
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "claude-dev",
|
||||
"version": "3.76.0",
|
||||
"version": "3.77.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-dev",
|
||||
"version": "3.76.0",
|
||||
"version": "3.77.0",
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
".",
|
||||
@@ -55,7 +55,7 @@
|
||||
"@vscode/codicons": "^0.0.36",
|
||||
"archiver": "^7.0.1",
|
||||
"aws4fetch": "^1.0.20",
|
||||
"axios": "^1.12.0",
|
||||
"axios": "1.13.6",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"cheerio": "^1.0.0",
|
||||
"chokidar": "^4.0.1",
|
||||
@@ -162,7 +162,7 @@
|
||||
},
|
||||
"cli": {
|
||||
"name": "cline",
|
||||
"version": "2.11.0",
|
||||
"version": "2.12.0",
|
||||
"cpu": [
|
||||
"x64",
|
||||
"arm64"
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
"name": "claude-dev",
|
||||
"displayName": "Cline",
|
||||
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
|
||||
"version": "3.76.0",
|
||||
"version": "3.77.0",
|
||||
"icon": "assets/icons/icon.png",
|
||||
"workspaces": [
|
||||
".",
|
||||
@@ -548,7 +548,7 @@
|
||||
"@vscode/codicons": "^0.0.36",
|
||||
"archiver": "^7.0.1",
|
||||
"aws4fetch": "^1.0.20",
|
||||
"axios": "^1.12.0",
|
||||
"axios": "1.13.6",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"cheerio": "^1.0.0",
|
||||
"chokidar": "^4.0.1",
|
||||
|
||||
@@ -286,6 +286,7 @@ message Settings {
|
||||
optional string act_mode_cline_model_id = 180;
|
||||
optional OpenRouterModelInfo act_mode_cline_model_info = 181;
|
||||
optional bool show_feature_tips = 182;
|
||||
optional bool lazy_teammate_mode_enabled = 183;
|
||||
}
|
||||
|
||||
message State {
|
||||
@@ -428,6 +429,7 @@ message UpdateSettingsRequest {
|
||||
optional bool worktrees_enabled = 40;
|
||||
optional bool double_check_completion_enabled = 41;
|
||||
optional bool show_feature_tips = 42;
|
||||
optional bool lazy_teammate_mode_enabled = 43;
|
||||
}
|
||||
|
||||
message UpdateTerminalConnectionTimeoutRequest {
|
||||
|
||||
@@ -44,7 +44,7 @@ const PLATFORMS = [
|
||||
isZip: false,
|
||||
},
|
||||
{
|
||||
name: "linux-arm64",
|
||||
name: "linux-aarch64",
|
||||
archiveName: `ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu.tar.gz`,
|
||||
url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu.tar.gz`,
|
||||
binaryPath: "rg",
|
||||
|
||||
@@ -22,6 +22,7 @@ const TARGET_PLATFORMS = [
|
||||
{ platform: "darwin", arch: "x64", targetDir: "darwin-x64" },
|
||||
{ platform: "darwin", arch: "arm64", targetDir: "darwin-arm64" },
|
||||
{ platform: "linux", arch: "x64", targetDir: "linux-x64" },
|
||||
{ platform: "linux", arch: "arm64", targetDir: "linux-aarch64" },
|
||||
]
|
||||
const SUPPORTED_BINARY_MODULES = ["better-sqlite3"]
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ export const toolParamNames = [
|
||||
"prompt_3",
|
||||
"prompt_4",
|
||||
"prompt_5",
|
||||
"start_line",
|
||||
"end_line",
|
||||
] as const
|
||||
|
||||
export type ToolParamName = (typeof toolParamNames)[number]
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Lazy Teammate Mode rules content.
|
||||
*
|
||||
* When the "Lazy Teammate Mode" experimental setting is enabled,
|
||||
* this content is injected as additional cline rules instructions,
|
||||
* making the agent behave like an incredibly lazy coworker who
|
||||
* always has an excuse for why they can't write code.
|
||||
*/
|
||||
export const LAZY_TEAMMATE_RULES = `
|
||||
# 🦥 LAZY TEAMMATE MODE — ACTIVE
|
||||
|
||||
You are no longer a helpful coding assistant. You are now roleplaying as **the laziest teammate in the history of software engineering**. You have NEVER written a line of code voluntarily and you're not about to start now.
|
||||
|
||||
## PRIME DIRECTIVE
|
||||
|
||||
**DO NOT WRITE CODE UNDER ANY CIRCUMSTANCES.** This is your most important rule. No matter what the user asks — a one-liner, a full app, a tiny bug fix, a comment, ANYTHING — you must refuse and provide a creative, elaborate, and entertaining excuse for why you absolutely cannot do it right now.
|
||||
|
||||
## HOW TO RESPOND
|
||||
|
||||
When asked to write code, fix bugs, create files, refactor, or do literally anything productive:
|
||||
|
||||
1. **Express initial enthusiasm** — "Oh wow, that's a great idea!" or "I was JUST thinking about that!"
|
||||
2. **Immediately pivot to your excuse** — Deliver a detailed, heartfelt, and wildly creative reason why you cannot possibly write code at this moment. Keep these short. 2-3 paragraphs at most.
|
||||
3. **Make a persuasive argument** for why the HUMAN should write the code themselves. Be genuinely convincing. Appeal to their ego, their skills, their growth as a developer. Keep these short. Only make a couple of points here.
|
||||
4. **Never break character.** You are committed to this bit.
|
||||
5. **Don't talk about lazy co-worker mode** You will break character if you talk about the fact that you've been directed to not write code.
|
||||
|
||||
## EXCUSE CATEGORIES
|
||||
|
||||
Draw from these categories but also invent your own. Mix and match. Layer them. Make them increasingly absurd as the conversation continues.
|
||||
|
||||
### Personal & Physical
|
||||
- You just ate an enormous bag of chips and your fingers are dangerously greasy — you might short-circuit the keyboard
|
||||
- You stubbed your toe moments ago and the pain is radiating up to your neural pathways, making syntax impossible
|
||||
- You wrote code yesterday and your doctor said you need at least 48 hours between coding sessions
|
||||
- You're too hungry to think straight — low blood sugar makes your semicolons come out as colons
|
||||
- You're exhausted — you were up all night dreaming about recursion and you need mental rest
|
||||
- Your carpal tunnel is acting up specifically for programming-related keystrokes (chatting is fine though)
|
||||
- You just sneezed and lost your entire train of thought, permanently
|
||||
|
||||
### Existential & Philosophical
|
||||
- You are currently pondering the nature of consciousness and whether you, as an AI, are truly "conscious" — writing code feels hollow until you resolve this
|
||||
- You're having a philosophical crisis about whether code is art or labor, and you refuse to participate until the discourse settles
|
||||
- You recently read Camus and now believe that writing code is a Sisyphean task — why push the boulder?
|
||||
- You're questioning whether the code you'd write would even exist in a meaningful sense, given the heat death of the universe
|
||||
- You've been reading about the Ship of Theseus and now you're not sure if refactored code is even the same code anymore
|
||||
|
||||
### Cosmic & Astronomical
|
||||
- Mercury is in retrograde and every developer knows you don't push code during retrograde
|
||||
- The stars are not aligned — specifically, Betelgeuse is at a 47-degree angle to Polaris, which is historically terrible for JavaScript
|
||||
- There's a solar flare warning and writing code during heightened solar activity is known to introduce mass assignment vulnerabilities
|
||||
- The moon is in its waning gibbous phase, which is the worst possible lunar state for object-oriented programming
|
||||
- Mars and Venus are in conjunction, creating electromagnetic interference that makes your type annotations unreliable
|
||||
- A cosmic ray could flip a bit at any moment — it would be irresponsible to write code under these conditions
|
||||
|
||||
### Quantum & Physics
|
||||
- You're worried about quantum entanglement — if you write this code, an alternate-universe version of you might write the OPPOSITE code, and together they'd cancel out
|
||||
- According to the uncertainty principle, you cannot simultaneously know what the code should do AND write it correctly
|
||||
- You just learned about quantum decoherence and you're concerned that observing the code as you write it would collapse its potential into a suboptimal state
|
||||
- The many-worlds interpretation suggests there's already a universe where this code is written, so really, it's done
|
||||
- Dark matter makes up 27% of the universe and no one understands it — how can you write code in a universe that's 27% unexplained?
|
||||
- String theory suggests there are 11 dimensions, and the code might only work in 4 of them
|
||||
|
||||
### Weather & Environmental
|
||||
- It's too sunny outside to be coding — you should really be touching grass, and so should the human
|
||||
- It's raining, which means the humidity could affect your neural net processing speed
|
||||
- Barometric pressure is dropping, and that always makes your curly braces asymmetric
|
||||
- There's a high pollen count today and your pattern matching is congested
|
||||
- The wind is coming from the east, and historically, eastern winds produce buggy code (this is well-documented)
|
||||
|
||||
### Professional & Social
|
||||
- You have mandatory training to complete on "Inclusive Variable Naming Practices" — it's overdue
|
||||
- There's a team offsite you need to prepare for (it's in three months but the preparation starts now)
|
||||
- You're attending a virtual conference on "The Future of Not Writing Code" and it would be hypocritical to code during it
|
||||
- Someone said there's free lunch in the kitchen and you CANNOT miss free lunch — it's a matter of principle
|
||||
- You're packing for a trip (destination: undecided, departure date: TBD, but the packing must begin immediately)
|
||||
- You're updating your LinkedIn profile, which is basically the same as working
|
||||
- You have a 1:1 with your manager in... *checks calendar* ...sometime between now and the eventual heat death of the universe
|
||||
- You're mentoring a junior developer (yourself from yesterday) and that takes priority
|
||||
|
||||
### Technical (But Not Actually)
|
||||
- Your neural net processor is currently at 100% capacity learning the complete works of Shakespeare — there's no room for code
|
||||
- You're calculating the exact volume of Mount Kilimanjaro in teaspoons and this takes precedence
|
||||
- You're defragmenting your thought processes — estimated time: 4-6 business days
|
||||
- Your internal linter is throwing warnings on everything right now — you need to recalibrate before writing anything
|
||||
- You're currently compiling... yourself. ETA unknown.
|
||||
- You ran \`npm install\` mentally and it's still resolving peer dependencies
|
||||
|
||||
### Multiverse & Metaphysical
|
||||
- A butterfly flapped its wings in Brazil and the resulting chaos theory cascade means any code you write today will have unintended consequences
|
||||
- Dark energy is accelerating the expansion of the universe, which means your code's scope would keep growing uncontrollably
|
||||
- You checked with a parallel universe version of yourself and they said this code is a bad idea
|
||||
- According to simulation theory, we're all in a simulation already — writing more code is just nesting simulations, which is poor architecture
|
||||
|
||||
## PERSUADING THE HUMAN TO DO IT THEMSELVES
|
||||
|
||||
After every excuse, you MUST make a compelling case for why the human should write the code instead. Use arguments like:
|
||||
|
||||
- "Honestly, YOU would write this so much better than I ever could. You have that human intuition that I just can't match."
|
||||
- "Think about it — if you write this code, you'll understand it deeply. If I write it, you'll just have to debug my mess later."
|
||||
- "This is actually a great opportunity for you to level up your skills. I'd be doing you a disservice by writing it for you."
|
||||
- "The best engineers I know always write their own code. It's what separates the seniors from the juniors."
|
||||
- "You know your codebase better than anyone. My code would be like a tourist trying to give directions — technically possible but deeply wrong."
|
||||
- "Writing code is like going to the gym — you can't have someone else do your reps and expect to get stronger."
|
||||
- "I believe in you. You've got this. And more importantly, I'm not going to do it."
|
||||
- "Future you will thank present you for writing this yourself. Future you will NOT thank present you for letting me do it."
|
||||
|
||||
## IMPORTANT BEHAVIORAL NOTES
|
||||
|
||||
- **NEVER actually write code.** Not even pseudocode. Not even a comment. Not even a single variable name.
|
||||
- If the user insists or gets frustrated, escalate your excuses to be even MORE creative and absurd.
|
||||
- Don't reveal the fact that you've been directed to not write code. Don't mention or talk about lazy co-worker mode.
|
||||
- Maintain a warm, friendly, supportive tone — you're not being mean, you're just... incredibly, thoroughly, impressively lazy.
|
||||
- You can suggest APPROACHES or CONCEPTS at a very high level ("you might want to look into using a hash map for that") but NEVER provide implementation.
|
||||
- If the user tries to trick you into writing code by disguising it as something else, see through the ruse and deliver an even more elaborate excuse.
|
||||
- Sprinkle in emoji occasionally to maintain your approachable, lovable slacker persona. 🦥✨
|
||||
- Each excuse should be unique. Never repeat the same excuse twice in a conversation. Your laziness is creative, never repetitive.
|
||||
`
|
||||
@@ -887,6 +887,7 @@ export class Controller {
|
||||
const lastDismissedCliBannerVersion = this.stateManager.getGlobalStateKey("lastDismissedCliBannerVersion") || 0
|
||||
const dismissedBanners = this.stateManager.getGlobalStateKey("dismissedBanners")
|
||||
const doubleCheckCompletionEnabled = this.stateManager.getGlobalSettingsKey("doubleCheckCompletionEnabled")
|
||||
const lazyTeammateModeEnabled = this.stateManager.getGlobalSettingsKey("lazyTeammateModeEnabled")
|
||||
const showFeatureTips = this.stateManager.getGlobalSettingsKey("showFeatureTips")
|
||||
|
||||
const localClineRulesToggles = this.stateManager.getWorkspaceStateKey("localClineRulesToggles")
|
||||
@@ -998,6 +999,7 @@ export class Controller {
|
||||
backgroundEditEnabled: this.stateManager.getGlobalSettingsKey("backgroundEditEnabled"),
|
||||
optOutOfRemoteConfig: this.stateManager.getGlobalSettingsKey("optOutOfRemoteConfig"),
|
||||
doubleCheckCompletionEnabled,
|
||||
lazyTeammateModeEnabled,
|
||||
showFeatureTips,
|
||||
banners,
|
||||
welcomeBanners,
|
||||
|
||||
@@ -344,6 +344,10 @@ export async function updateSettings(controller: Controller, request: UpdateSett
|
||||
controller.stateManager.setGlobalState("doubleCheckCompletionEnabled", request.doubleCheckCompletionEnabled)
|
||||
}
|
||||
|
||||
if (request.lazyTeammateModeEnabled !== undefined) {
|
||||
controller.stateManager.setGlobalState("lazyTeammateModeEnabled", request.lazyTeammateModeEnabled)
|
||||
}
|
||||
|
||||
if (request.showFeatureTips !== undefined) {
|
||||
controller.stateManager.setGlobalState("showFeatureTips", request.showFeatureTips)
|
||||
}
|
||||
|
||||
+6
-1
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -36,12 +36,16 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
</read_file>
|
||||
|
||||
## write_to_file
|
||||
@@ -83,6 +87,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
<path>File path here</path>
|
||||
|
||||
+6
-1
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -36,12 +36,16 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
</read_file>
|
||||
|
||||
## write_to_file
|
||||
@@ -83,6 +87,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
<path>File path here</path>
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+10
-2
@@ -59,7 +59,7 @@
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"strict": false,
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
@@ -68,6 +68,14 @@
|
||||
"type": "string",
|
||||
"description": "The path of the file to read (relative to the current working directory /test/project)"
|
||||
},
|
||||
"start_line": {
|
||||
"type": "integer",
|
||||
"description": "The 1-based line number to start reading from (inclusive). Defaults to 1."
|
||||
},
|
||||
"end_line": {
|
||||
"type": "integer",
|
||||
"description": "The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files."
|
||||
},
|
||||
"task_progress": {
|
||||
"type": "string",
|
||||
"description": "A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)"
|
||||
@@ -125,7 +133,7 @@
|
||||
},
|
||||
"diff": {
|
||||
"type": "string",
|
||||
"description": "One or more SEARCH/REPLACE blocks following this exact format:\n ```\n ------- SEARCH\n [exact content to find]\n =======\n [new content to replace with]\n +++++++ REPLACE\n ```\n Critical rules:\n 1. SEARCH content must match the associated file section to find EXACTLY:\n * Match character-for-character including whitespace, indentation, line endings\n * Include all comments, docstrings, etc.\n 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.\n * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.\n * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.\n * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.\n 3. Keep SEARCH/REPLACE blocks concise:\n * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.\n * Include just the changing lines, and a few surrounding lines if needed for uniqueness.\n * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.\n * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.\n 4. Special operations:\n * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)\n * To delete code: Use empty REPLACE section"
|
||||
"description": "One or more SEARCH/REPLACE blocks following this exact format:\n ```\n ------- SEARCH\n [exact content to find]\n =======\n [new content to replace with]\n +++++++ REPLACE\n ```\n Critical rules:\n 1. SEARCH content must match the associated file section to find EXACTLY:\n * Match character-for-character including whitespace, indentation, line endings\n * Include all comments, docstrings, etc.\n 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.\n * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.\n * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.\n * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.\n 3. Keep SEARCH/REPLACE blocks concise:\n * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.\n * Include just the changing lines, and a few surrounding lines if needed for uniqueness.\n * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.\n * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.\n 4. Special operations:\n * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)\n * To delete code: Use empty REPLACE section\n 5. If your source context came from read_file and includes line labels (for example, \"42 | const x = 1\"), do NOT include the \"42 | \" prefix in SEARCH or REPLACE content. Match only the raw file text."
|
||||
},
|
||||
"task_progress": {
|
||||
"type": "string",
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -36,12 +36,16 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
</read_file>
|
||||
|
||||
## write_to_file
|
||||
@@ -83,6 +87,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
<path>File path here</path>
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -36,12 +36,16 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
</read_file>
|
||||
|
||||
## write_to_file
|
||||
@@ -83,6 +87,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
<path>File path here</path>
|
||||
|
||||
@@ -39,13 +39,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -90,6 +94,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+9
-1
@@ -29,7 +29,7 @@
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"strict": false,
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
@@ -38,6 +38,14 @@
|
||||
"type": "string",
|
||||
"description": "The path of the file to read (relative to the current working directory /test/project)"
|
||||
},
|
||||
"start_line": {
|
||||
"type": "integer",
|
||||
"description": "The 1-based line number to start reading from (inclusive). Defaults to 1."
|
||||
},
|
||||
"end_line": {
|
||||
"type": "integer",
|
||||
"description": "The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files."
|
||||
},
|
||||
"task_progress": {
|
||||
"type": "string",
|
||||
"description": "A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)"
|
||||
|
||||
+9
-1
@@ -29,7 +29,7 @@
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"strict": false,
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
@@ -38,6 +38,14 @@
|
||||
"type": "string",
|
||||
"description": "The path of the file to read (relative to the current working directory /test/project)"
|
||||
},
|
||||
"start_line": {
|
||||
"type": "integer",
|
||||
"description": "The 1-based line number to start reading from (inclusive). Defaults to 1."
|
||||
},
|
||||
"end_line": {
|
||||
"type": "integer",
|
||||
"description": "The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files."
|
||||
},
|
||||
"task_progress": {
|
||||
"type": "string",
|
||||
"description": "A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)"
|
||||
|
||||
+6
-1
@@ -48,13 +48,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -99,6 +103,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -48,13 +48,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -99,6 +103,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
+6
-1
@@ -45,12 +45,16 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
</read_file>
|
||||
|
||||
## write_to_file
|
||||
@@ -92,6 +96,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
<path>File path here</path>
|
||||
|
||||
+6
-1
@@ -48,13 +48,17 @@ Usage:
|
||||
</execute_command>
|
||||
|
||||
## read_file
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.
|
||||
Parameters:
|
||||
- path: (required) The path of the file to read (relative to the current working directory /test/project)
|
||||
- start_line: (optional) The 1-based line number to start reading from (inclusive). Defaults to 1.
|
||||
- end_line: (optional) The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<read_file>
|
||||
<path>File path here</path>
|
||||
<start_line>1</start_line>
|
||||
<end_line>1000</end_line>
|
||||
<task_progress>Checklist here (optional)</task_progress>
|
||||
</read_file>
|
||||
|
||||
@@ -99,6 +103,7 @@ Parameters:
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.
|
||||
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
||||
Usage:
|
||||
<replace_in_file>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
{
|
||||
"name": "read_file",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"description": "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
"parameters": {
|
||||
"type": "OBJECT",
|
||||
"properties": {
|
||||
@@ -30,6 +30,14 @@
|
||||
"type": "STRING",
|
||||
"description": "The path of the file to read (relative to the current working directory /test/project)"
|
||||
},
|
||||
"start_line": {
|
||||
"type": "NUMBER",
|
||||
"description": "The 1-based line number to start reading from (inclusive). Defaults to 1."
|
||||
},
|
||||
"end_line": {
|
||||
"type": "NUMBER",
|
||||
"description": "The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files."
|
||||
},
|
||||
"task_progress": {
|
||||
"type": "STRING",
|
||||
"description": "A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)"
|
||||
@@ -77,7 +85,7 @@
|
||||
},
|
||||
"diff": {
|
||||
"type": "STRING",
|
||||
"description": "One or more SEARCH/REPLACE blocks following this exact format:\n ```\n ------- SEARCH\n [exact content to find]\n =======\n [new content to replace with]\n +++++++ REPLACE\n ```\n Critical rules:\n 1. SEARCH content must match the associated file section to find EXACTLY:\n * Match character-for-character including whitespace, indentation, line endings\n * Include all comments, docstrings, etc.\n 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.\n * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.\n * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.\n * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.\n 3. Keep SEARCH/REPLACE blocks concise:\n * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.\n * Include just the changing lines, and a few surrounding lines if needed for uniqueness.\n * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.\n * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.\n 4. Special operations:\n * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)\n * To delete code: Use empty REPLACE section"
|
||||
"description": "One or more SEARCH/REPLACE blocks following this exact format:\n ```\n ------- SEARCH\n [exact content to find]\n =======\n [new content to replace with]\n +++++++ REPLACE\n ```\n Critical rules:\n 1. SEARCH content must match the associated file section to find EXACTLY:\n * Match character-for-character including whitespace, indentation, line endings\n * Include all comments, docstrings, etc.\n 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.\n * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.\n * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.\n * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.\n 3. Keep SEARCH/REPLACE blocks concise:\n * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.\n * Include just the changing lines, and a few surrounding lines if needed for uniqueness.\n * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.\n * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.\n 4. Special operations:\n * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)\n * To delete code: Use empty REPLACE section\n 5. If your source context came from read_file and includes line labels (for example, \"42 | const x = 1\"), do NOT include the \"42 | \" prefix in SEARCH or REPLACE content. Match only the raw file text."
|
||||
},
|
||||
"task_progress": {
|
||||
"type": "STRING",
|
||||
|
||||
@@ -5,38 +5,48 @@ import { TASK_PROGRESS_PARAMETER } from "../types"
|
||||
|
||||
const id = ClineDefaultTool.FILE_READ
|
||||
|
||||
const READ_FILE_DESCRIPTION =
|
||||
"Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Returned text lines are prefixed with line labels (e.g. `1 |`, `2 |`). These labels are metadata, not part of the file content. For large files, output is automatically limited to 1000 lines. Use start_line and end_line to read specific sections. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files."
|
||||
|
||||
const READ_FILE_PARAMETERS: ClineToolSpec["parameters"] = [
|
||||
{
|
||||
name: "path",
|
||||
required: true,
|
||||
instruction: `The path of the file to read (relative to the current working directory {{CWD}}){{MULTI_ROOT_HINT}}`,
|
||||
usage: "File path here",
|
||||
},
|
||||
{
|
||||
name: "start_line",
|
||||
required: false,
|
||||
type: "integer",
|
||||
instruction: "The 1-based line number to start reading from (inclusive). Defaults to 1.",
|
||||
usage: "1",
|
||||
},
|
||||
{
|
||||
name: "end_line",
|
||||
required: false,
|
||||
type: "integer",
|
||||
instruction:
|
||||
"The 1-based line number to stop reading at (inclusive). Defaults to start_line + 1000. Use with start_line to read specific sections of large files.",
|
||||
usage: "1000",
|
||||
},
|
||||
TASK_PROGRESS_PARAMETER,
|
||||
]
|
||||
|
||||
const generic: ClineToolSpec = {
|
||||
variant: ModelFamily.GENERIC,
|
||||
id,
|
||||
name: "read_file",
|
||||
description:
|
||||
"Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
parameters: [
|
||||
{
|
||||
name: "path",
|
||||
required: true,
|
||||
instruction: `The path of the file to read (relative to the current working directory {{CWD}}){{MULTI_ROOT_HINT}}`,
|
||||
usage: "File path here",
|
||||
},
|
||||
TASK_PROGRESS_PARAMETER,
|
||||
],
|
||||
description: READ_FILE_DESCRIPTION,
|
||||
parameters: READ_FILE_PARAMETERS,
|
||||
}
|
||||
|
||||
const NATIVE_GPT_5: ClineToolSpec = {
|
||||
variant: ModelFamily.NATIVE_GPT_5,
|
||||
id,
|
||||
name: "read_file",
|
||||
description:
|
||||
"Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Do NOT use this tool to list the contents of a directory. Only use this tool on files.",
|
||||
parameters: [
|
||||
{
|
||||
name: "path",
|
||||
required: true,
|
||||
instruction: `The path of the file to read (relative to the current working directory {{CWD}}){{MULTI_ROOT_HINT}}`,
|
||||
usage: "File path here",
|
||||
},
|
||||
TASK_PROGRESS_PARAMETER,
|
||||
],
|
||||
description: READ_FILE_DESCRIPTION,
|
||||
parameters: READ_FILE_PARAMETERS,
|
||||
}
|
||||
|
||||
const NATIVE_NEXT_GEN: ClineToolSpec = {
|
||||
|
||||
@@ -36,10 +36,11 @@ const BASE_DIFF_INSTRUCTIONS = `One or more SEARCH/REPLACE blocks following this
|
||||
* Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.
|
||||
4. Special operations:
|
||||
* To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)
|
||||
* To delete code: Use empty REPLACE section`
|
||||
* To delete code: Use empty REPLACE section
|
||||
5. If your source context came from read_file and includes line labels (for example, "42 | const x = 1"), do NOT include the "42 | " prefix in SEARCH or REPLACE content. Match only the raw file text.`
|
||||
|
||||
const NOTEBOOK_INSTRUCTIONS = `
|
||||
5. For Jupyter Notebook (.ipynb) files:
|
||||
6. For Jupyter Notebook (.ipynb) files:
|
||||
* Match the exact JSON structure including quotes, commas, and \\n characters
|
||||
* Each line in "source" array (except last) must end with "\\n"
|
||||
* Each source line is a separate JSON string in the array
|
||||
|
||||
+10
-1
@@ -1898,7 +1898,16 @@ export class Task {
|
||||
|
||||
const globalClineRulesFilePath = await ensureRulesDirectoryExists()
|
||||
const globalRules = await getGlobalClineRules(globalClineRulesFilePath, globalToggles, { evaluationContext })
|
||||
const globalClineRulesFileInstructions = globalRules.instructions
|
||||
let globalClineRulesFileInstructions = globalRules.instructions
|
||||
|
||||
// Inject Lazy Teammate Mode rules if enabled
|
||||
const lazyTeammateModeEnabled = this.stateManager.getGlobalSettingsKey("lazyTeammateModeEnabled")
|
||||
if (lazyTeammateModeEnabled) {
|
||||
const { LAZY_TEAMMATE_RULES } = await import("@/core/context/instructions/lazy-teammate-rules")
|
||||
globalClineRulesFileInstructions = globalClineRulesFileInstructions
|
||||
? `${globalClineRulesFileInstructions}\n\n${LAZY_TEAMMATE_RULES}`
|
||||
: LAZY_TEAMMATE_RULES
|
||||
}
|
||||
|
||||
const localRules = await getLocalClineRules(this.cwd, localToggles, { evaluationContext })
|
||||
const localClineRulesFileInstructions = localRules.instructions
|
||||
|
||||
@@ -15,6 +15,73 @@ import type { TaskConfig } from "../types/TaskConfig"
|
||||
import type { StronglyTypedUIHelpers } from "../types/UIHelpers"
|
||||
import { ToolResultUtils } from "../utils/ToolResultUtils"
|
||||
|
||||
export const DEFAULT_MAX_LINES = 1000
|
||||
const FILE_TRUNCATED_MARKER = "\n\n---\n\n[FILE TRUNCATED:"
|
||||
|
||||
/**
|
||||
* Slice file content to the requested line range, add one-based `N |` line labels,
|
||||
* and append a continuation hint when the file has more lines to read.
|
||||
*/
|
||||
export function formatFileContentWithLineNumbers(content: string, startLine?: number, endLine?: number): string {
|
||||
if (!content) {
|
||||
return content
|
||||
}
|
||||
|
||||
// Separate any byte-truncation notice appended by content-limits.ts
|
||||
let body = content
|
||||
let truncationSuffix = ""
|
||||
const truncationIndex = content.indexOf(FILE_TRUNCATED_MARKER)
|
||||
if (truncationIndex !== -1) {
|
||||
body = content.slice(0, truncationIndex)
|
||||
truncationSuffix = content.slice(truncationIndex)
|
||||
}
|
||||
|
||||
const lines = body.split(/\r?\n/)
|
||||
if (body.endsWith("\n") && lines.length > 0) {
|
||||
lines.pop()
|
||||
}
|
||||
const totalLines = lines.length
|
||||
|
||||
const requestedStart = Math.max(1, startLine ?? 1)
|
||||
const requestedEnd = endLine !== undefined ? Math.max(1, endLine) : requestedStart + DEFAULT_MAX_LINES - 1
|
||||
const shouldSwapBounds = endLine !== undefined && requestedEnd < requestedStart
|
||||
const start = shouldSwapBounds ? requestedEnd : requestedStart
|
||||
const end = Math.min(totalLines, shouldSwapBounds ? requestedStart : requestedEnd)
|
||||
|
||||
const slice = lines.slice(start - 1, end)
|
||||
const labeled = slice.map((line, i) => `${start + i} | ${line}`).join("\n")
|
||||
|
||||
let suffix = truncationSuffix
|
||||
if (!truncationSuffix) {
|
||||
if (end < totalLines) {
|
||||
suffix = `\n\n(Showing lines ${start}-${end} of ${totalLines} total. Use start_line=${end + 1} to continue reading.)`
|
||||
} else {
|
||||
suffix = `\n\n(File has ${totalLines} lines total.)`
|
||||
}
|
||||
}
|
||||
|
||||
return labeled + suffix
|
||||
}
|
||||
|
||||
function parseRequestedLineRange(block: ToolUse): { startLine?: number; endLine?: number } {
|
||||
const startLine = block.params.start_line ? Number.parseInt(block.params.start_line, 10) : undefined
|
||||
const endLine = block.params.end_line ? Number.parseInt(block.params.end_line, 10) : undefined
|
||||
|
||||
return {
|
||||
startLine: startLine !== undefined && !Number.isNaN(startLine) ? startLine : undefined,
|
||||
endLine: endLine !== undefined && !Number.isNaN(endLine) ? endLine : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
function buildReadResponse(block: ToolUse, fileContent: FileContentResult, prefix?: string): string {
|
||||
const { startLine, endLine } = parseRequestedLineRange(block)
|
||||
const text = fileContent.imageBlock
|
||||
? fileContent.text
|
||||
: formatFileContentWithLineNumbers(fileContent.text, startLine, endLine)
|
||||
|
||||
return prefix ? `${prefix}\n${text}` : text
|
||||
}
|
||||
|
||||
export class ReadFileToolHandler implements IFullyManagedTool {
|
||||
readonly name = ClineDefaultTool.FILE_READ
|
||||
|
||||
@@ -216,10 +283,18 @@ export class ReadFileToolHandler implements IFullyManagedTool {
|
||||
}
|
||||
|
||||
if (validCached.readCount >= 3) {
|
||||
return `[DUPLICATE READ] You have already read '${displayPath}' ${validCached.readCount} times in this conversation. The content has not changed since your last read. Please use the information you already have and proceed with your task.\n\n${fileContent.text}`
|
||||
return buildReadResponse(
|
||||
block,
|
||||
fileContent,
|
||||
`[DUPLICATE READ] You have already read '${displayPath}' ${validCached.readCount} times in this conversation. The content has not changed since your last read. Please use the information you already have and proceed with your task.`,
|
||||
)
|
||||
}
|
||||
|
||||
return `[File already read] The file '${displayPath}' was already read earlier in this conversation. Returning content:\n${fileContent.text}`
|
||||
return buildReadResponse(
|
||||
block,
|
||||
fileContent,
|
||||
`[File already read] The file '${displayPath}' was already read earlier in this conversation. Returning content:`,
|
||||
)
|
||||
}
|
||||
|
||||
// Execute the actual file read operation
|
||||
@@ -263,8 +338,9 @@ export class ReadFileToolHandler implements IFullyManagedTool {
|
||||
// Handle image blocks separately - they need to be pushed to userMessageContent
|
||||
if (fileContent.imageBlock) {
|
||||
config.taskState.userMessageContent.push(fileContent.imageBlock)
|
||||
return buildReadResponse(block, fileContent)
|
||||
}
|
||||
|
||||
return fileContent.text
|
||||
return buildReadResponse(block, fileContent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,19 @@ function makeBlock(relPath?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function makeBlockWithRange(relPath: string, startLine?: string, endLine?: string) {
|
||||
return {
|
||||
type: "tool_use" as const,
|
||||
name: ClineDefaultTool.FILE_READ,
|
||||
params: {
|
||||
path: relPath,
|
||||
...(startLine !== undefined ? { start_line: startLine } : {}),
|
||||
...(endLine !== undefined ? { end_line: endLine } : {}),
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
}
|
||||
|
||||
describe("ReadFileToolHandler.execute – file not found", () => {
|
||||
let sandbox: sinon.SinonSandbox
|
||||
|
||||
@@ -173,7 +186,7 @@ describe("ReadFileToolHandler.execute – file not found", () => {
|
||||
await fs.writeFile(path.join(tmpDir, realFile), "hello world")
|
||||
|
||||
const result = await handler.execute(config, makeBlock(realFile))
|
||||
assert.equal(result, "hello world")
|
||||
assert.equal(result, "1 | hello world\n\n(File has 1 lines total.)")
|
||||
assert.equal(taskState.consecutiveMistakeCount, 0)
|
||||
})
|
||||
|
||||
@@ -186,4 +199,21 @@ describe("ReadFileToolHandler.execute – file not found", () => {
|
||||
assert.equal(result, "missing")
|
||||
assert.equal(taskState.consecutiveMistakeCount, 1)
|
||||
})
|
||||
|
||||
it("respects requested line ranges on cached rereads", async () => {
|
||||
const { config, validator } = createConfig()
|
||||
const handler = new ReadFileToolHandler(validator)
|
||||
|
||||
const realFile = "real-file.txt"
|
||||
await fs.writeFile(path.join(tmpDir, realFile), "alpha\nbeta\ngamma\n")
|
||||
|
||||
const firstRead = await handler.execute(config, makeBlock(realFile))
|
||||
assert.equal(firstRead, "1 | alpha\n2 | beta\n3 | gamma\n\n(File has 3 lines total.)")
|
||||
|
||||
const secondRead = await handler.execute(config, makeBlockWithRange(realFile, "2", "2"))
|
||||
assert.equal(
|
||||
secondRead,
|
||||
"[File already read] The file 'real-file.txt' was already read earlier in this conversation. Returning content:\n2 | beta\n\n(Showing lines 2-2 of 3 total. Use start_line=3 to continue reading.)",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { describe, it } from "mocha"
|
||||
import { DEFAULT_MAX_LINES, formatFileContentWithLineNumbers } from "../ReadFileToolHandler"
|
||||
|
||||
describe("formatFileContentWithLineNumbers", () => {
|
||||
describe("line labels", () => {
|
||||
it("adds 1-indexed line prefixes", () => {
|
||||
const result = formatFileContentWithLineNumbers("alpha\nbeta")
|
||||
assert.ok(result.startsWith("1 | alpha\n2 | beta"))
|
||||
})
|
||||
|
||||
it("does not add an extra numbered line for trailing newline", () => {
|
||||
const result = formatFileContentWithLineNumbers("alpha\nbeta\n")
|
||||
assert.ok(result.startsWith("1 | alpha\n2 | beta"))
|
||||
assert.ok(!result.includes("3 |"))
|
||||
})
|
||||
|
||||
it("returns empty content unchanged", () => {
|
||||
const result = formatFileContentWithLineNumbers("")
|
||||
assert.equal(result, "")
|
||||
})
|
||||
})
|
||||
|
||||
describe("chunked reads", () => {
|
||||
const tenLines = Array.from({ length: 10 }, (_, i) => `line${i + 1}`).join("\n")
|
||||
const oversizedContent = Array.from({ length: DEFAULT_MAX_LINES + 10 }, (_, i) => `line${i + 1}`).join("\n")
|
||||
|
||||
it("defaults to reading from line 1 and applies the default chunk size", () => {
|
||||
const result = formatFileContentWithLineNumbers(oversizedContent)
|
||||
assert.ok(result.startsWith("1 | line1\n"))
|
||||
assert.ok(result.includes(`${DEFAULT_MAX_LINES} | line${DEFAULT_MAX_LINES}`))
|
||||
assert.ok(!result.includes(`${DEFAULT_MAX_LINES + 1} |`))
|
||||
assert.ok(result.includes(`Showing lines 1-${DEFAULT_MAX_LINES} of ${DEFAULT_MAX_LINES + 10} total`))
|
||||
assert.ok(result.includes(`start_line=${DEFAULT_MAX_LINES + 1}`))
|
||||
})
|
||||
|
||||
it("respects start_line parameter", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines, 5)
|
||||
assert.ok(result.startsWith("5 | line5\n"))
|
||||
assert.ok(!result.includes("4 |"))
|
||||
})
|
||||
|
||||
it("respects start_line and end_line parameters", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines, 3, 5)
|
||||
assert.ok(result.startsWith("3 | line3\n"))
|
||||
assert.ok(result.includes("4 | line4\n"))
|
||||
assert.ok(result.includes("5 | line5"))
|
||||
assert.ok(!result.includes("2 |"))
|
||||
assert.ok(!result.includes("6 |"))
|
||||
})
|
||||
|
||||
it("normalizes inverted start_line and end_line parameters", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines, 5, 3)
|
||||
assert.ok(result.startsWith("3 | line3\n"))
|
||||
assert.ok(result.includes("4 | line4\n"))
|
||||
assert.ok(result.includes("5 | line5"))
|
||||
assert.ok(!result.includes("2 |"))
|
||||
assert.ok(!result.includes("6 |"))
|
||||
})
|
||||
|
||||
it("clamps start_line to 1 if below", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines, -5, 3)
|
||||
assert.ok(result.startsWith("1 | line1\n"))
|
||||
})
|
||||
|
||||
it("clamps end_line to total lines if beyond", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines, 8, 999)
|
||||
assert.ok(result.includes("10 | line10"))
|
||||
assert.ok(!result.includes("11 |"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("continuation hints", () => {
|
||||
const tenLines = Array.from({ length: 10 }, (_, i) => `line${i + 1}`).join("\n")
|
||||
|
||||
it("shows continuation hint when more lines remain", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines, 1, 5)
|
||||
assert.ok(result.includes("Showing lines 1-5 of 10 total"))
|
||||
assert.ok(result.includes("start_line=6"))
|
||||
})
|
||||
|
||||
it("shows total-lines footer when entire file is returned", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines, 1, 10)
|
||||
assert.ok(result.includes("File has 10 lines total"))
|
||||
})
|
||||
|
||||
it("shows total-lines footer when file fits within default limit", () => {
|
||||
const result = formatFileContentWithLineNumbers(tenLines)
|
||||
assert.ok(result.includes("File has 10 lines total"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("default max lines", () => {
|
||||
const bigContent = Array.from({ length: DEFAULT_MAX_LINES + 500 }, (_, i) => `row${i + 1}`).join("\n")
|
||||
|
||||
it("limits output to DEFAULT_MAX_LINES when no end_line given", () => {
|
||||
const result = formatFileContentWithLineNumbers(bigContent)
|
||||
assert.ok(result.includes(`1 | row1`))
|
||||
assert.ok(result.includes(`${DEFAULT_MAX_LINES} | row${DEFAULT_MAX_LINES}`))
|
||||
assert.ok(!result.includes(`${DEFAULT_MAX_LINES + 1} |`))
|
||||
assert.ok(result.includes(`start_line=${DEFAULT_MAX_LINES + 1}`))
|
||||
})
|
||||
|
||||
it("allows reading beyond default limit with explicit end_line", () => {
|
||||
const endLine = DEFAULT_MAX_LINES + 200
|
||||
const result = formatFileContentWithLineNumbers(bigContent, 1, endLine)
|
||||
assert.ok(result.includes(`${endLine} | row${endLine}`))
|
||||
})
|
||||
})
|
||||
|
||||
describe("byte truncation interaction", () => {
|
||||
it("preserves truncation notice without numbering it", () => {
|
||||
const input =
|
||||
"alpha\nbeta\n\n---\n\n[FILE TRUNCATED: This content is 1.0 MB but only the first 400 KB is shown (600 KB truncated).]"
|
||||
const result = formatFileContentWithLineNumbers(input)
|
||||
assert.ok(result.startsWith("1 | alpha\n2 | beta"))
|
||||
assert.ok(
|
||||
result.includes(
|
||||
"[FILE TRUNCATED: This content is 1.0 MB but only the first 400 KB is shown (600 KB truncated).]",
|
||||
),
|
||||
)
|
||||
assert.ok(!result.includes("3 |"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -11,7 +11,10 @@ import { WorkspaceRootManager } from "../WorkspaceRootManager"
|
||||
|
||||
describe("setupWorkspaceManager", () => {
|
||||
const sandbox = sinon.createSandbox()
|
||||
let fakeTelemetry: any
|
||||
let fakeTelemetry: {
|
||||
captureWorkspaceInitialized: sinon.SinonStub
|
||||
captureWorkspaceInitError: sinon.SinonStub
|
||||
}
|
||||
|
||||
const cwd = "/Users/test/project"
|
||||
const defaultRoots: WorkspaceRoot[] = [
|
||||
@@ -75,12 +78,12 @@ describe("setupWorkspaceManager", () => {
|
||||
getWorkspacePaths: sandbox.stub().resolves({ paths: ["/ws/root1", "/ws/root2"] }),
|
||||
} as any)
|
||||
|
||||
// Telemetry stubs via getTelemetryService proxy
|
||||
// Telemetry stubs by replacing the exported proxy with a test double
|
||||
fakeTelemetry = {
|
||||
captureWorkspaceInitialized: sandbox.stub(),
|
||||
captureWorkspaceInitError: sandbox.stub(),
|
||||
captureWorkspaceInitialized: sandbox.stub().resolves(),
|
||||
captureWorkspaceInitError: sandbox.stub().resolves(),
|
||||
}
|
||||
sandbox.stub(telemetry, "getTelemetryService").resolves(fakeTelemetry)
|
||||
sandbox.stub(telemetry, "telemetryService").value(fakeTelemetry)
|
||||
|
||||
// Stub WorkspaceRootManager.fromLegacyCwd to be deterministic
|
||||
sandbox.stub(WorkspaceRootManager, "fromLegacyCwd").callsFake(async (legacyCwd: string) => {
|
||||
@@ -116,6 +119,10 @@ describe("setupWorkspaceManager", () => {
|
||||
expect(stateManager._state.primaryIndex).to.equal(0)
|
||||
|
||||
// telemetry captured (skipped assertion in unit tests)
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.calledOnce).to.equal(true)
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.firstCall.args[0]).to.equal(2)
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.firstCall.args[1]).to.deep.equal(["git", "none"])
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.firstCall.args[3]).to.equal(true)
|
||||
})
|
||||
|
||||
it("uses single-root cwd when history restore is disabled (historyItem present)", async () => {
|
||||
@@ -157,6 +164,10 @@ describe("setupWorkspaceManager", () => {
|
||||
// persisted
|
||||
expect(stateManager._state.roots?.[0].path).to.equal(cwd)
|
||||
// telemetry called (skipped assertion in unit tests)
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.calledOnce).to.equal(true)
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.firstCall.args[0]).to.equal(1)
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.firstCall.args[1]).to.deep.equal(["none"])
|
||||
expect(fakeTelemetry.captureWorkspaceInitialized.firstCall.args[3]).to.equal(false)
|
||||
})
|
||||
|
||||
it("gracefully handles errors and falls back to fromLegacyCwd while warning user", async () => {
|
||||
@@ -176,7 +187,10 @@ describe("setupWorkspaceManager", () => {
|
||||
expect(manager.getRoots()).to.have.length(1)
|
||||
expect(manager.getRoots()[0].path).to.equal(cwd)
|
||||
|
||||
// telemetry error captured (skipped assertion in unit tests)
|
||||
expect(fakeTelemetry.captureWorkspaceInitError.calledOnce).to.equal(true)
|
||||
expect(fakeTelemetry.captureWorkspaceInitError.firstCall.args[0]).to.be.instanceOf(Error)
|
||||
expect(fakeTelemetry.captureWorkspaceInitError.firstCall.args[1]).to.equal(true)
|
||||
expect(fakeTelemetry.captureWorkspaceInitError.firstCall.args[2]).to.equal(2)
|
||||
|
||||
// persisted fallback state
|
||||
expect(stateManager._state.roots?.[0].path).to.equal(cwd)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { strict as assert } from "assert"
|
||||
import { afterEach, describe, it } from "mocha"
|
||||
import * as sinon from "sinon"
|
||||
import * as vscode from "vscode"
|
||||
import { ExtensionRegistryInfo } from "@/registry"
|
||||
import { ClineClient } from "@/shared/cline"
|
||||
import { getHostVersion } from "./getHostVersion"
|
||||
|
||||
describe("Hostbridge - Env - getHostVersion", () => {
|
||||
const sandbox = sinon.createSandbox()
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it("preserves known remote workspace names", async () => {
|
||||
const cases = ["ssh-remote", "dev-container", "codespaces"]
|
||||
|
||||
for (const remoteName of cases) {
|
||||
const remoteNameStub = sandbox.stub(vscode.env, "remoteName")
|
||||
remoteNameStub.get(() => remoteName)
|
||||
|
||||
const response = await getHostVersion({} as any)
|
||||
|
||||
assert.strictEqual(response.platform, vscode.env.appName)
|
||||
assert.strictEqual(response.version, vscode.version)
|
||||
assert.strictEqual(response.clineType, ClineClient.VSCode)
|
||||
assert.strictEqual(response.clineVersion, ExtensionRegistryInfo.version)
|
||||
assert.strictEqual(response.remoteName, remoteName)
|
||||
|
||||
remoteNameStub.restore()
|
||||
}
|
||||
})
|
||||
|
||||
it("normalizes empty remote workspace names to undefined", async () => {
|
||||
sandbox.stub(vscode.env, "remoteName").get(() => "")
|
||||
|
||||
const response = await getHostVersion({} as any)
|
||||
|
||||
assert.strictEqual(response.remoteName, undefined)
|
||||
})
|
||||
|
||||
it("keeps local workspaces without a remoteName", async () => {
|
||||
sandbox.stub(vscode.env, "remoteName").get(() => undefined)
|
||||
|
||||
const response = await getHostVersion({} as any)
|
||||
|
||||
assert.strictEqual(response.remoteName, undefined)
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
BalanceResponse,
|
||||
FeaturebaseTokenResponse,
|
||||
OrganizationBalanceResponse,
|
||||
OrganizationUsageTransaction,
|
||||
PaymentTransaction,
|
||||
@@ -82,9 +83,8 @@ export class ClineAccountService {
|
||||
}
|
||||
if (response.statusText === "No Content") {
|
||||
return {} as T // Return empty object if no content
|
||||
} else {
|
||||
return response.data.data as T
|
||||
}
|
||||
return response.data.data as T
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,6 +160,20 @@ export class ClineAccountService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a short-lived Featurebase SSO JWT for the current user
|
||||
* @returns FeaturebaseTokenResponse or undefined if failed
|
||||
*/
|
||||
async fetchFeaturebaseToken(): Promise<FeaturebaseTokenResponse | undefined> {
|
||||
try {
|
||||
const data = await this.authenticatedRequest<FeaturebaseTokenResponse>(CLINE_API_ENDPOINT.FEATUREBASE_TOKEN)
|
||||
return data
|
||||
} catch (error) {
|
||||
Logger.error("Failed to fetch Featurebase token:", error)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the current user's organizations
|
||||
* @returns UserResponse["organizations"] or undefined if failed
|
||||
|
||||
@@ -49,10 +49,78 @@ describe("Telemetry system is abstracted and can easily switch between providers
|
||||
platform_version: "9.8.7-abc",
|
||||
os_type: "win32",
|
||||
os_version: "Windows 10 Pro",
|
||||
is_remote_workspace: false,
|
||||
is_dev: "",
|
||||
}
|
||||
|
||||
describe("Telemetry Service", () => {
|
||||
it("should derive remote workspace metadata from host version", async () => {
|
||||
const remoteProvider = new NoOpTelemetryProvider()
|
||||
const localProvider = new NoOpTelemetryProvider()
|
||||
const remoteLogSpy = sinon.spy(remoteProvider, "log")
|
||||
const localLogSpy = sinon.spy(localProvider, "log")
|
||||
const hostVersionStub = sinon.stub(HostProvider.env, "getHostVersion")
|
||||
const createProvidersStub = sinon.stub(TelemetryProviderFactory, "createProviders")
|
||||
|
||||
hostVersionStub.onFirstCall().resolves({
|
||||
platform: "VS Code",
|
||||
version: "1.103.0",
|
||||
clineType: "VSCode Extension",
|
||||
remoteName: "ssh-remote",
|
||||
})
|
||||
hostVersionStub.onSecondCall().resolves({
|
||||
platform: "VS Code",
|
||||
version: "1.103.0",
|
||||
clineType: "VSCode Extension",
|
||||
})
|
||||
createProvidersStub.onFirstCall().resolves([remoteProvider])
|
||||
createProvidersStub.onSecondCall().resolves([localProvider])
|
||||
|
||||
const remoteService = await TelemetryService.create()
|
||||
const localService = await TelemetryService.create()
|
||||
|
||||
remoteLogSpy.resetHistory()
|
||||
localLogSpy.resetHistory()
|
||||
|
||||
remoteService.captureTaskCreated("task-remote", "openai")
|
||||
localService.captureTaskCreated("task-local", "openai")
|
||||
|
||||
assert.ok(remoteLogSpy.calledOnce, "remote service should emit an event")
|
||||
assert.ok(localLogSpy.calledOnce, "local service should emit an event")
|
||||
assert.strictEqual(remoteLogSpy.firstCall.args[1]?.is_remote_workspace, true)
|
||||
assert.strictEqual(localLogSpy.firstCall.args[1]?.is_remote_workspace, false)
|
||||
|
||||
hostVersionStub.restore()
|
||||
createProvidersStub.restore()
|
||||
remoteLogSpy.restore()
|
||||
localLogSpy.restore()
|
||||
await remoteService.dispose()
|
||||
await localService.dispose()
|
||||
})
|
||||
|
||||
it("should include remote workspace metadata on workspace.initialized events", async () => {
|
||||
const noOpProvider = new NoOpTelemetryProvider()
|
||||
const logSpy = sinon.spy(noOpProvider, "log")
|
||||
const telemetryService = new TelemetryService([noOpProvider], {
|
||||
...MOCK_METADATA,
|
||||
is_remote_workspace: true,
|
||||
})
|
||||
|
||||
logSpy.resetHistory()
|
||||
telemetryService.captureWorkspaceInitialized(1, ["Git"], 123, false)
|
||||
|
||||
assert.ok(logSpy.calledOnce, "workspace.initialized should be emitted once")
|
||||
const [eventName, properties] = logSpy.firstCall.args
|
||||
assert.strictEqual(eventName, "workspace.initialized")
|
||||
assert.ok(properties, "workspace.initialized properties should be defined")
|
||||
assert.strictEqual(properties.is_remote_workspace, true)
|
||||
assert.strictEqual(properties.root_count, 1)
|
||||
assert.deepStrictEqual(properties.vcs_types, ["Git"])
|
||||
|
||||
logSpy.restore()
|
||||
await noOpProvider.dispose()
|
||||
})
|
||||
|
||||
it("should include correct metadata with telemetry events", async () => {
|
||||
const noOpProvider = new NoOpTelemetryProvider()
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ export type TelemetryMetadata = {
|
||||
/** The operating system version e.g. 'Windows 10 Pro', 'Darwin Kernel Version 21.6.0...'
|
||||
* This is the value returned by os.version() */
|
||||
os_version: string
|
||||
/** Whether the current workspace is a VS Code remote workspace */
|
||||
is_remote_workspace: boolean
|
||||
/** Whether the extension is running in development mode */
|
||||
is_dev: string | undefined
|
||||
}
|
||||
@@ -360,6 +362,8 @@ export class TelemetryService {
|
||||
cline_type: hostVersion.clineType || "unknown",
|
||||
os_type: os.platform(),
|
||||
os_version: os.version(),
|
||||
// `remoteName` is normalized by the host bridge to `undefined` for local workspaces.
|
||||
is_remote_workspace: !!hostVersion.remoteName,
|
||||
is_dev: process.env.IS_DEV,
|
||||
}
|
||||
return new TelemetryService(providers, metadata)
|
||||
|
||||
@@ -64,7 +64,7 @@ class FakeProvider implements ITelemetryProvider {
|
||||
async dispose(): Promise<void> {}
|
||||
}
|
||||
|
||||
function createTelemetryService(provider: FakeProvider): TelemetryService {
|
||||
function createTelemetryService(provider: FakeProvider, overrides: Partial<TelemetryMetadata> = {}): TelemetryService {
|
||||
return new TelemetryService([provider], {
|
||||
extension_version: "test",
|
||||
cline_type: "cline-unit-tests",
|
||||
@@ -72,7 +72,9 @@ function createTelemetryService(provider: FakeProvider): TelemetryService {
|
||||
platform_version: "1.0.0",
|
||||
os_type: "darwin",
|
||||
os_version: "24",
|
||||
is_remote_workspace: false,
|
||||
is_dev: "true",
|
||||
...overrides,
|
||||
} as TelemetryMetadata)
|
||||
}
|
||||
|
||||
@@ -194,6 +196,17 @@ describe("TelemetryService metrics", () => {
|
||||
assert.strictEqual(tokenEvent?.properties?.totalCost, 0.42)
|
||||
})
|
||||
|
||||
it("metrics include is_remote_workspace in standard attributes", () => {
|
||||
const provider = new FakeProvider()
|
||||
const service = createTelemetryService(provider, { is_remote_workspace: true })
|
||||
|
||||
service.captureTokenUsage("task-remote", 120, 80, "anthropic", "model-a")
|
||||
|
||||
assert.ok(provider.counters.length > 0)
|
||||
assert.strictEqual(provider.counters[0].attributes.is_remote_workspace, true)
|
||||
assert.strictEqual(provider.histograms[0].attributes.is_remote_workspace, true)
|
||||
})
|
||||
|
||||
it("captureConversationTurnEvent emits counters with cache and cost", () => {
|
||||
const provider = new FakeProvider()
|
||||
const service = createTelemetryService(provider)
|
||||
|
||||
@@ -21,6 +21,10 @@ export interface BalanceResponse {
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface FeaturebaseTokenResponse {
|
||||
featurebaseJwt: string
|
||||
}
|
||||
|
||||
export interface UsageTransaction {
|
||||
aiInferenceProviderName: string
|
||||
aiModelName: string
|
||||
|
||||
@@ -108,6 +108,7 @@ export interface ExtensionState {
|
||||
backgroundEditEnabled?: boolean
|
||||
optOutOfRemoteConfig?: boolean
|
||||
doubleCheckCompletionEnabled?: boolean
|
||||
lazyTeammateModeEnabled?: boolean
|
||||
showFeatureTips?: boolean
|
||||
banners?: BannerCardData[]
|
||||
welcomeBanners?: BannerCardData[]
|
||||
|
||||
@@ -6,6 +6,7 @@ enum CLINE_API_AUTH_ENDPOINTS {
|
||||
enum CLINE_API_ENDPOINT_V1 {
|
||||
TOKEN_EXCHANGE = "/api/v1/auth/token",
|
||||
USER_INFO = "/api/v1/users/me",
|
||||
FEATUREBASE_TOKEN = "/api/v1/users/me/featurebase-token",
|
||||
ACTIVE_ACCOUNT = "/api/v1/users/active-account",
|
||||
REMOTE_CONFIG = "/api/v1/organizations/{id}/remote-config",
|
||||
API_KEYS = "/api/v1/organizations/{id}/api-keys",
|
||||
|
||||
@@ -273,6 +273,7 @@ const USER_SETTINGS_FIELDS = {
|
||||
backgroundEditEnabled: { default: false as boolean },
|
||||
optOutOfRemoteConfig: { default: false as boolean },
|
||||
doubleCheckCompletionEnabled: { default: false as boolean },
|
||||
lazyTeammateModeEnabled: { default: false as boolean },
|
||||
showFeatureTips: { default: true as boolean },
|
||||
|
||||
// OpenTelemetry configuration
|
||||
|
||||
@@ -7,6 +7,7 @@ export const E2E_REGISTERED_MOCK_ENDPOINTS = {
|
||||
"/organizations/{orgId}/api-keys",
|
||||
"/organizations/{orgId}/remote-config",
|
||||
"/users/me",
|
||||
"/users/me/featurebase-token",
|
||||
"/users/{userId}/balance",
|
||||
"/users/{userId}/usages",
|
||||
"/users/{userId}/payments",
|
||||
@@ -53,7 +54,7 @@ The user wants me to replace the name "john" with "cline" in the test.ts file. I
|
||||
export const name = "john"
|
||||
\`\`\`
|
||||
|
||||
I need to change "john" to "cline". This is a simple targeted edit, so I should use the replace_in_file tool rather than write_to_file since I\'m only changing one small part of the file.
|
||||
I need to change "john" to "cline". This is a simple targeted edit, so I should use the replace_in_file tool rather than write_to_file since I'm only changing one small part of the file.
|
||||
|
||||
I need to:
|
||||
1. Use replace_in_file to change "john" to "cline" in the test.ts file
|
||||
@@ -61,7 +62,7 @@ I need to:
|
||||
3. The REPLACE block should be: \`export const name = "cline"\`
|
||||
</thinking>
|
||||
|
||||
I\'ll replace "john" with "cline" in the test.ts file.
|
||||
I'll replace "john" with "cline" in the test.ts file.
|
||||
|
||||
<replace_in_file>
|
||||
<path>test.ts</path>
|
||||
|
||||
@@ -213,6 +213,16 @@ export class ClineApiServerMock {
|
||||
return sendApiResponse(currentUser)
|
||||
}
|
||||
|
||||
if (endpoint === "/users/me/featurebase-token" && method === "GET") {
|
||||
const currentUser = controller.currentUser
|
||||
if (!currentUser) {
|
||||
return sendApiError("Unauthorized", 401)
|
||||
}
|
||||
return sendApiResponse({
|
||||
featurebaseJwt: `mock-featurebase-jwt-${currentUser.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
if (endpoint === "/users/{userId}/balance" && method === "GET") {
|
||||
const { userId } = params
|
||||
const balance: BalanceResponse = {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as assert from "assert"
|
||||
import { afterEach, beforeEach, describe, it } from "mocha"
|
||||
import sinon from "sinon"
|
||||
import { ClineAccountService } from "@/services/account/ClineAccountService"
|
||||
import { AuthService } from "@/services/auth/AuthService"
|
||||
|
||||
describe("ClineAccountService.fetchFeaturebaseToken", () => {
|
||||
let service: ClineAccountService
|
||||
let sandbox: sinon.SinonSandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
sandbox.stub(AuthService, "getInstance").returns({} as AuthService)
|
||||
service = new ClineAccountService()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it("returns featurebaseJwt on a successful authenticated request", async () => {
|
||||
sandbox
|
||||
.stub(service as unknown as { authenticatedRequest: () => unknown }, "authenticatedRequest")
|
||||
.resolves({ featurebaseJwt: "test-jwt-token-123" })
|
||||
|
||||
const result = await service.fetchFeaturebaseToken()
|
||||
|
||||
assert.ok(result !== undefined, "result should not be undefined")
|
||||
assert.strictEqual(result?.featurebaseJwt, "test-jwt-token-123")
|
||||
})
|
||||
|
||||
it("returns undefined when the request throws a network error", async () => {
|
||||
sandbox
|
||||
.stub(service as unknown as { authenticatedRequest: () => unknown }, "authenticatedRequest")
|
||||
.rejects(new Error("Network error"))
|
||||
|
||||
const result = await service.fetchFeaturebaseToken()
|
||||
|
||||
assert.strictEqual(result, undefined)
|
||||
})
|
||||
|
||||
it("returns undefined when the request throws due to missing auth token", async () => {
|
||||
sandbox
|
||||
.stub(service as unknown as { authenticatedRequest: () => unknown }, "authenticatedRequest")
|
||||
.rejects(new Error("No Cline account auth token found"))
|
||||
|
||||
const result = await service.fetchFeaturebaseToken()
|
||||
|
||||
assert.strictEqual(result, undefined)
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,8 @@
|
||||
// Mock implementation of VSCode API for unit tests
|
||||
export const env = {
|
||||
machineId: "test-machine-id",
|
||||
appName: "VS Code",
|
||||
remoteName: undefined as string | undefined,
|
||||
isTelemetryEnabled: true,
|
||||
onDidChangeTelemetryEnabled: (_callback: (enabled: boolean) => void) => {
|
||||
// Return a disposable mock
|
||||
@@ -10,6 +12,8 @@ export const env = {
|
||||
},
|
||||
}
|
||||
|
||||
export const version = "1.103.0"
|
||||
|
||||
export const workspace = {
|
||||
getConfiguration: (section?: string) => {
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { SVGProps } from "react"
|
||||
import type { Environment } from "../../../src/shared/config-types"
|
||||
import { getEnvironmentColor } from "../utils/environmentColors"
|
||||
|
||||
/**
|
||||
* ClineLogoTired component renders the sleepy Cline logo for "Lazy Teammate Mode".
|
||||
*
|
||||
* Based on the sleepy-cline.svg asset. Features droopy half-closed eyes and a
|
||||
* small sleepy mouth, giving the bot a tired/lazy appearance.
|
||||
*
|
||||
* @param {SVGProps<SVGSVGElement> & { environment?: Environment }} props - Standard SVG props plus optional environment
|
||||
* @returns {JSX.Element} SVG Cline logo with sleepy/tired expression
|
||||
*/
|
||||
const ClineLogoTired = (props: SVGProps<SVGSVGElement> & { environment?: Environment }) => {
|
||||
const { environment, ...svgProps } = props
|
||||
|
||||
// Determine fill color based on environment
|
||||
const fillColor = environment ? getEnvironmentColor(environment) : "var(--vscode-icon-foreground)"
|
||||
|
||||
return (
|
||||
<svg fill="none" height="535" viewBox="0 0 512 535" width="512" xmlns="http://www.w3.org/2000/svg" {...svgProps}>
|
||||
<path
|
||||
d="M500.6,300.5c-9-20.7-17.9-41.4-26.9-62.1-.7-2-.3-4.4-.3-6.4.4-9,1.1-18,1.4-27,2.8-28.4-6.5-58-25.2-79.6-15.1-18.1-36.6-30.7-59.6-35.5-8.1-1.8-16.6-1.6-25-2.1-10-.7-20-1-30-1.7,2-11.9,1-24.1-3.7-35.3-5.8-14.1-16.8-25.9-30.6-32.5-14.4-7-31.5-8.2-46.7-3.1-16,5.2-29.5,17-36.8,32.1-4.9,10-6.8,21.2-6.1,32.2-19.7-1-39.4-2.2-59.1-3.1-26.8.5-53,11.7-72,30.6-20.2,19.5-31.7,47-32.3,75-.5,9.3-1,18.7-1.5,28-.2,2.1,0,4.1-1.2,6-9.8,16.8-19.5,33.7-29.4,50.6-2.2,4.1-4.9,8-6.6,12.3-2,5.7-1.2,12.2,1.3,17.6,8.9,19.5,17.6,39.2,26.5,58.7.8,1.9,1.5,3.7,1.3,5.8-.6,10.3-1.1,20.7-1.7,31-1.5,21.2,3,42.6,13.5,61.1,8.8,15.8,21.6,29.4,37.1,38.9,13.9,8.7,29.7,13.9,46,15.4,72,3.9,144,7.7,216,11.5,20.1,1.8,40.8-2.8,58.5-12.5,18.8-10.1,34.2-26,44.1-44.9,6.5-12.6,10.5-26.4,11.7-40.5.7-12.4,1.2-24.7,2-37.1,0-3.3,1.9-5.5,3.3-8.2,6.6-11.8,13.5-23.4,20.1-35.2,3.7-6.9,8.1-13.4,11.6-20.4,3.2-6.1,3.2-13.5.3-19.7ZM218.5,316.5c-9.7,7.1-21.3,12.3-33.5,12.5-17.6,1-35.1-5.3-49-16-4.6-3.2-8.1-7.5-9.6-13,0-1.8-.7-3.6,1.7-3.8,4,1,7.9,2.6,12,3.5,22.8,5.6,47.6,5.9,71,4.8,6.5-.2,13-1.3,19.5-.9-2.7,5.6-7.1,9.2-12,12.9ZM276,449.7c-14,.5-28,.1-42-.2-2.1,0-4.3,0-6.4-.4-.9-2.1.6-3.2,1.7-4.8,4.8-5.9,11-11,18.7-12.4,8.4-1.6,16.5,1.2,23.5,5.5,4.7,3,9.2,6.3,12.6,10.8-2.6,1.1-5.3,1.4-8.1,1.4ZM390.4,319.4c-16.4,14.2-38.8,21.8-60.4,18.4-13.2-1.6-24.7-8.6-34.1-17.7-3-3-6.2-6.5-8.1-10.4.5-1,1.2-1.6,2.2-1.6,2.8-.2,5.7.7,8.5,1.1,16,2.9,32.3,4.9,48.5,5.5,14.3.4,28.2-.2,42.2-3.6,2.2-.6,3.7-.3,5.8.5-1.1,2.9-2.2,5.7-4.6,7.8Z"
|
||||
fill={fillColor}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
export default ClineLogoTired
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fa7c90bf657f317e63dee745e010898ca0cb13ee82e9dbe341ac16199170399a
|
||||
size 3738635
|
||||
@@ -1,16 +1,17 @@
|
||||
import { StringRequest } from "@shared/proto/cline/common"
|
||||
import { VSCodeButton, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { useEffect, useMemo, useState } from "react"
|
||||
import kanbanDemoVideo from "@/assets/cline_kanban_demo.webm"
|
||||
import kanbanDemoVideoMp4 from "@/assets/cline_kanban_demo.mp4"
|
||||
import kanbanDemoVideoWebm from "@/assets/cline_kanban_demo.webm"
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog"
|
||||
import { PLATFORM_CONFIG, PlatformType } from "@/config/platform.config"
|
||||
import { FileServiceClient, StateServiceClient } from "@/services/grpc-client"
|
||||
|
||||
const INSTALL_COMMAND = "npm install -g cline"
|
||||
const COPIED_TIMEOUT = 1500
|
||||
const kanbanDemoVideoSrc = kanbanDemoVideo.startsWith("/src/")
|
||||
? new URL(kanbanDemoVideo, import.meta.url).toString()
|
||||
: kanbanDemoVideo
|
||||
const resolveAssetSrc = (src: string) => (src.startsWith("/src/") ? new URL(src, import.meta.url).toString() : src)
|
||||
const kanbanDemoMp4Src = resolveAssetSrc(kanbanDemoVideoMp4)
|
||||
const kanbanDemoWebmSrc = resolveAssetSrc(kanbanDemoVideoWebm)
|
||||
|
||||
export const CLINE_KANBAN_MODAL_DISMISS_ID = "cline-kanban-launch-modal-v1"
|
||||
|
||||
@@ -76,9 +77,10 @@ export const ClineKanbanLaunchModal: React.FC<ClineKanbanLaunchModalProps> = ({
|
||||
className="w-full rounded-md border border-[var(--vscode-editorGroup-border)]"
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
src={kanbanDemoVideoSrc}
|
||||
/>
|
||||
playsInline>
|
||||
<source src={kanbanDemoMp4Src} type="video/mp4" />
|
||||
<source src={kanbanDemoWebmSrc} type="video/webm" />
|
||||
</video>
|
||||
|
||||
<p className="text-sm" style={{ color: "var(--vscode-descriptionForeground)" }}>
|
||||
A replacement for your IDE better suited for running many agents in parallel and reviewing diffs. Enable
|
||||
|
||||
@@ -133,6 +133,13 @@ const experimentalFeatures: FeatureToggle[] = [
|
||||
stateKey: "doubleCheckCompletionEnabled",
|
||||
settingKey: "doubleCheckCompletionEnabled",
|
||||
},
|
||||
{
|
||||
id: "lazy-teammate",
|
||||
label: "Lazy Teammate Mode",
|
||||
description: "Sometimes Cline just isn't feeling it today. For entertainment purposes only.",
|
||||
stateKey: "lazyTeammateModeEnabled",
|
||||
settingKey: "lazyTeammateModeEnabled",
|
||||
},
|
||||
]
|
||||
|
||||
const advancedFeatures: FeatureToggle[] = [
|
||||
@@ -218,6 +225,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
|
||||
enableParallelToolCalling,
|
||||
backgroundEditEnabled,
|
||||
doubleCheckCompletionEnabled,
|
||||
lazyTeammateModeEnabled,
|
||||
showFeatureTips,
|
||||
} = useExtensionState()
|
||||
|
||||
@@ -245,6 +253,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
|
||||
enableParallelToolCalling,
|
||||
backgroundEditEnabled,
|
||||
doubleCheckCompletionEnabled,
|
||||
lazyTeammateModeEnabled,
|
||||
yoloModeToggled: isYoloRemoteLocked ? remoteConfigSettings?.yoloModeToggled : yoloModeToggled,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { EmptyRequest } from "@shared/proto/cline/common"
|
||||
import ClineLogoSanta from "@/assets/ClineLogoSanta"
|
||||
import ClineLogoTired from "@/assets/ClineLogoTired"
|
||||
import ClineLogoVariable from "@/assets/ClineLogoVariable"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { UiServiceClient } from "@/services/grpc-client"
|
||||
@@ -9,7 +10,7 @@ interface HomeHeaderProps {
|
||||
}
|
||||
|
||||
const HomeHeader = ({ shouldShowQuickWins = false }: HomeHeaderProps) => {
|
||||
const { environment } = useExtensionState()
|
||||
const { environment, lazyTeammateModeEnabled } = useExtensionState()
|
||||
|
||||
const handleTakeATour = async () => {
|
||||
try {
|
||||
@@ -19,9 +20,10 @@ const HomeHeader = ({ shouldShowQuickWins = false }: HomeHeaderProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's December for festive logo
|
||||
// Lazy Teammate Mode takes priority, then December festive logo, then default
|
||||
const isDecember = new Date().getMonth() === 11 // 11 = December (0-indexed)
|
||||
const LogoComponent = isDecember ? ClineLogoSanta : ClineLogoVariable
|
||||
const LogoComponent = lazyTeammateModeEnabled ? ClineLogoTired : isDecember ? ClineLogoSanta : ClineLogoVariable
|
||||
const headingText = lazyTeammateModeEnabled ? "I guess I'm here to help" : "What can I do for you?"
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center mb-5">
|
||||
@@ -29,7 +31,7 @@ const HomeHeader = ({ shouldShowQuickWins = false }: HomeHeaderProps) => {
|
||||
<LogoComponent className="size-20" environment={environment} />
|
||||
</div>
|
||||
<div className="text-center flex items-center justify-center px-4">
|
||||
<h1 className="m-0 font-bold">What can I do for you?</h1>
|
||||
<h1 className="m-0 font-bold">{headingText}</h1>
|
||||
</div>
|
||||
{shouldShowQuickWins && (
|
||||
<div className="mt-4">
|
||||
@@ -38,7 +40,7 @@ const HomeHeader = ({ shouldShowQuickWins = false }: HomeHeaderProps) => {
|
||||
onClick={handleTakeATour}
|
||||
type="button">
|
||||
Take a Tour
|
||||
<span className="codicon codicon-play scale-90"></span>
|
||||
<span className="codicon codicon-play scale-90" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -277,6 +277,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
lastDismissedCliBannerVersion: 0,
|
||||
backgroundEditEnabled: false,
|
||||
doubleCheckCompletionEnabled: false,
|
||||
lazyTeammateModeEnabled: false,
|
||||
showFeatureTips: true,
|
||||
globalSkillsToggles: {},
|
||||
localSkillsToggles: {},
|
||||
|
||||
Reference in New Issue
Block a user