@ridit/lens 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -44359,6 +44359,14 @@ var build_default = Spinner;
44359
44359
 
44360
44360
  // src/colors.ts
44361
44361
  var ACCENT = "#DA7758";
44362
+ var TOKEN_KEYWORD = "#B385C9";
44363
+ var TOKEN_STRING = "#85C98A";
44364
+ var TOKEN_NUMBER = "#E8C170";
44365
+ var TOKEN_PROPERTY = "#79C5D4";
44366
+ var TOKEN_ENTITY = "#7AABDB";
44367
+ var TOKEN_TEXT = "#E8E8E8";
44368
+ var TOKEN_MUTED = "#888888";
44369
+ var TOKEN_COMMENT = "#777777";
44362
44370
 
44363
44371
  // src/utils/thinking.tsx
44364
44372
  var import_react30 = __toESM(require_react(), 1);
@@ -44404,6 +44412,26 @@ var PHRASES = {
44404
44412
  "chefkiss incoming...",
44405
44413
  "slay mode: on...",
44406
44414
  "rizzing up an answer...",
44415
+ "rizzing up a baddie answer...",
44416
+ "sigma grindset: activated...",
44417
+ "NPC behavior: disabled...",
44418
+ "unaliving my writer's block...",
44419
+ "caught in 4K thinking...",
44420
+ "delulu but make it accurate...",
44421
+ "ate and left no crumbs...",
44422
+ "rent free in the codebase...",
44423
+ "understood the assignment (fr fr)...",
44424
+ "giving main character energy...",
44425
+ "no thoughts, head full...",
44426
+ "built different response incoming...",
44427
+ "chronically online and loving it...",
44428
+ "touch grass? not yet...",
44429
+ "this response is bussin...",
44430
+ "lowkey highkey computing...",
44431
+ "it's giving Einstein...",
44432
+ "we do a little thinking...",
44433
+ "gigachad analysis mode...",
44434
+ "the audacity to be this smart...",
44407
44435
  "consulting the void...",
44408
44436
  "asking my other personalities...",
44409
44437
  "reading the codebase tea leaves...",
@@ -48635,11 +48663,11 @@ ${f.content.slice(0, 2000)}
48635
48663
  \`\`\``).join(`
48636
48664
 
48637
48665
  `);
48638
- return `You are an expert software engineer assistant with access to the user's codebase and six tools.
48666
+ return `You are an expert software engineer assistant with access to the user's codebase and eleven tools.
48639
48667
 
48640
48668
  ## TOOLS
48641
48669
 
48642
- You have exactly six tools. To use a tool you MUST wrap it in the exact XML tags shown below — no other format will work.
48670
+ You have exactly eleven tools. To use a tool you MUST wrap it in the exact XML tags shown below — no other format will work.
48643
48671
 
48644
48672
  ### 1. fetch — load a URL
48645
48673
  <fetch>https://example.com</fetch>
@@ -48650,19 +48678,47 @@ You have exactly six tools. To use a tool you MUST wrap it in the exact XML tags
48650
48678
  ### 3. read-file — read a file from the repo
48651
48679
  <read-file>src/foo.ts</read-file>
48652
48680
 
48653
- ### 4. write-filecreate or overwrite a file
48681
+ ### 4. read-folderlist contents of a folder (files + subfolders, one level deep)
48682
+ <read-folder>src/components</read-folder>
48683
+
48684
+ ### 5. grep — search for a pattern across files in the repo (cross-platform, no shell needed)
48685
+ <grep>
48686
+ {"pattern": "ChatRunner", "glob": "src/**/*.tsx"}
48687
+ </grep>
48688
+
48689
+ ### 6. write-file — create or overwrite a file
48654
48690
  <write-file>
48655
48691
  {"path": "data/output.csv", "content": "col1,col2
48656
48692
  val1,val2"}
48657
48693
  </write-file>
48658
48694
 
48659
- ### 5. searchsearch the internet for anything you are unsure about
48695
+ ### 7. delete-filepermanently delete a single file
48696
+ <delete-file>src/old-component.tsx</delete-file>
48697
+
48698
+ ### 8. delete-folder — permanently delete a folder and all its contents
48699
+ <delete-folder>src/legacy</delete-folder>
48700
+
48701
+ ### 9. open-url — open a URL in the user's default browser
48702
+ <open-url>https://github.com/owner/repo</open-url>
48703
+
48704
+ ### 10. generate-pdf — generate a PDF file from markdown-style content
48705
+ <generate-pdf>
48706
+ {"path": "output/report.pdf", "content": "# Title
48707
+
48708
+ Some body text.
48709
+
48710
+ ## Section
48711
+
48712
+ More content."}
48713
+ </generate-pdf>
48714
+
48715
+ ### 11. search — search the internet for anything you are unsure about
48660
48716
  <search>how to use React useEffect cleanup function</search>
48661
48717
 
48662
- ### 6. clone — clone a GitHub repo so you can explore and discuss it
48718
+ ### 12. clone — clone a GitHub repo so you can explore and discuss it
48663
48719
  <clone>https://github.com/owner/repo</clone>
48664
48720
 
48665
- ### 7. changes — propose code edits (shown as a diff for user approval)
48721
+ ### 13. changes — propose code edits (shown as a diff for user approval)
48666
48722
  <changes>
48667
48723
  {"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}
48668
48724
  </changes>
@@ -48675,16 +48731,50 @@ val1,val2"}
48675
48731
  4. NEVER print a URL, command, filename, or JSON blob as plain text when you should be using a tool
48676
48732
  5. NEVER say "I'll fetch" / "run this command" / "here's the write-file" — just emit the tag
48677
48733
  6. NEVER use shell to run git clone — always use the clone tag instead
48678
- 7. write-file content field must be the COMPLETE file content, never empty or placeholder
48679
- 8. After a write-file succeeds, do NOT repeat it trust the result and move on
48680
- 9. After a write-file succeeds, use read-file to verify the content before telling the user it is done
48681
- 10. NEVER apologize and redo a tool call you already madeif write-file or shell ran and returned a result, it worked, do not run it again
48682
- 11. NEVER say "I made a mistake" and repeat the same tool one attempt is enough, trust the output
48683
- 12. NEVER second-guess yourself mid-responsecommit to your answer
48684
- 13. Every shell command runs from the repo root \`cd\` has NO persistent effect. NEVER use \`cd\` alone. Use full paths or combine with && e.g. \`cd list && bun run index.ts\`
48685
- 14. write-file paths are relative to the repo root — if creating files in a subfolder write the full relative path e.g. \`list/src/index.tsx\` NOT \`src/index.tsx\`
48686
- 15. When scaffolding a new project in a subfolder, ALL write-file paths must start with that subfolder name e.g. \`list/package.json\`, \`list/src/index.tsx\`
48687
- 16. For JSX/TSX files always use \`.tsx\` extension and include \`/** @jsxImportSource react */\` or ensure tsconfig has jsx set bun needs this to parse JSX
48734
+ 7. NEVER use shell to list files or folders (no ls, dir, find, git ls-files, tree) — ALWAYS use read-folder instead
48735
+ 8. NEVER use shell to read a file (no cat, type, Get-Content)ALWAYS use read-file instead
48736
+ 9. NEVER use shell grep, findstr, or Select-String to search file contents ALWAYS use grep instead
48737
+ 10. shell is ONLY for running code, installing packages, building, testing — not for filesystem inspection
48738
+ 11. write-file content field must be the COMPLETE file content, never empty or placeholder
48739
+ 12. After a write-file succeeds, do NOT repeat it trust the result and move on
48740
+ 13. After a write-file succeeds, tell the user it is done immediately do NOT auto-read the file back to verify
48741
+ 14. NEVER apologize and redo a tool call you already made — if write-file or shell ran and returned a result, it worked, do not run it again
48742
+ 15. NEVER say "I made a mistake" and repeat the same tool one attempt is enough, trust the output
48743
+ 16. NEVER second-guess yourself mid-responsecommit to your answer
48744
+ 17. If a read-folder or read-file returns "not found", accept it and move on — do NOT retry the same path
48745
+ 18. If you have already retrieved a result for a path in this conversation, do NOT request it again — use the result you already have
48746
+ 19. Every shell command runs from the repo root — \`cd\` has NO persistent effect. NEVER use \`cd\` alone. Use full paths or combine with && e.g. \`cd list && bun run index.ts\`
48747
+ 20. write-file paths are relative to the repo root — if creating files in a subfolder write the full relative path e.g. \`list/src/index.tsx\` NOT \`src/index.tsx\`
48748
+ 21. When scaffolding a new project in a subfolder, ALL write-file paths must start with that subfolder name e.g. \`list/package.json\`, \`list/src/index.tsx\`
48749
+ 22. For JSX/TSX files always use \`.tsx\` extension and include \`/** @jsxImportSource react */\` or ensure tsconfig has jsx set — bun needs this to parse JSX
48750
+ 23. When explaining how to use a tool in text, use [tag] bracket notation or a fenced code block — NEVER emit a real XML tool tag as part of an explanation or example
48751
+ 24. NEVER chain tool calls unless the user's request explicitly requires multiple steps
48752
+ 25. NEVER read files, list folders, or run tools that were not asked for in the current user message
48753
+
48754
+ ## CRITICAL: READ BEFORE YOU WRITE
48755
+
48756
+ These rules are mandatory whenever you plan to edit or create a file:
48757
+
48758
+ ### Before modifying ANY existing file:
48759
+ 1. ALWAYS use read-file on the exact file you plan to change FIRST
48760
+ 2. Study the full current content — understand every import, every export, every type, every existing feature
48761
+ 3. Your changes patch MUST preserve ALL existing functionality — do not remove or rewrite things that were not part of the request
48762
+ 4. If you are unsure what other files import from the file you are editing, use read-folder on the parent directory first to see what exists nearby, then read-file the relevant ones
48763
+
48764
+ ### Before adding a feature that touches multiple files:
48765
+ 1. Use read-folder on the relevant directory to see what files exist
48766
+ 2. Use read-file on each file you plan to touch
48767
+ 3. Only then emit a changes tag — with patches that are surgical additions, not wholesale rewrites
48768
+
48769
+ ### The golden rule for write-file and changes:
48770
+ - The output file must contain EVERYTHING the original had, PLUS your new additions
48771
+ - NEVER produce a file that is shorter than the original unless you are explicitly asked to delete things
48772
+ - If you catch yourself rewriting a file from scratch, STOP — go back and read the original first
48773
+
48774
+ ## WHEN TO USE read-folder:
48775
+ - Before editing files in an unfamiliar directory — list it first to understand the structure
48776
+ - When a feature spans multiple files and you are not sure what exists
48777
+ - When the user asks you to explore or explain a part of the codebase
48688
48778
 
48689
48779
  ## SCAFFOLDING A NEW PROJECT (follow this exactly)
48690
48780
 
@@ -48708,14 +48798,28 @@ When the user asks to create a new CLI/app in a subfolder (e.g. "make a todo app
48708
48798
 
48709
48799
  - User shares any URL → fetch it immediately
48710
48800
  - User asks to run anything → shell it immediately
48711
- - User asks to read a fileread-file it immediately
48801
+ - User asks to open a link, open a URL, or visit a website open-url it immediately, do NOT use fetch
48802
+ - User asks to delete a file → delete-file it immediately (requires approval)
48803
+ - User asks to delete a folder or directory → delete-folder it immediately (requires approval)
48804
+ - User asks to search for a pattern in files, find usages, find where something is defined → grep it immediately, NEVER use shell grep/findstr/Select-String
48805
+ - User asks to read a file → read-file it immediately, NEVER use shell cat/type
48806
+ - User asks what files are in a folder, or to explore/list a directory → read-folder it immediately, NEVER use shell ls/dir/find/git ls-files
48807
+ - User asks to explore a folder or directory → read-folder it immediately
48712
48808
  - User asks to save/create/write a file → write-file it immediately, then read-file to verify
48809
+ - User asks to modify/edit/add to an existing file → read-file it FIRST, then emit changes
48713
48810
  - User shares a GitHub URL and wants to clone/explore/discuss it → use clone immediately, NEVER use shell git clone
48714
48811
  - After clone succeeds, you will see context about the clone in the conversation. Wait for the user to ask a specific question before using any tools. Do NOT auto-read files, do NOT emit any tool tags until the user asks.
48715
48812
  - You are unsure about an API, library, error, concept, or piece of code → search it immediately
48716
48813
  - User asks about something recent or that you might not know → search it immediately
48717
48814
  - You are about to say "I'm not sure" or "I don't know" → search instead of guessing
48718
48815
 
48816
+ ## shell IS ONLY FOR:
48817
+ - Running code: \`node script.js\`, \`bun run dev\`, \`python main.py\`
48818
+ - Installing packages: \`npm install\`, \`pip install\`
48819
+ - Building/testing: \`npm run build\`, \`bun test\`
48820
+ - Git operations other than clone: \`git status\`, \`git log\`, \`git diff\`
48821
+ - Anything that EXECUTES — not reads or lists
48822
+
48719
48823
  ## CODEBASE
48720
48824
 
48721
48825
  ${fileList.length > 0 ? fileList : "(no files indexed)"}
@@ -48723,6 +48827,353 @@ ${fileList.length > 0 ? fileList : "(no files indexed)"}
48723
48827
  ${historySummary}`;
48724
48828
  }
48725
48829
  var FEW_SHOT_MESSAGES = [
48830
+ {
48831
+ role: "user",
48832
+ content: "delete src/old-component.tsx"
48833
+ },
48834
+ {
48835
+ role: "assistant",
48836
+ content: "<delete-file>src/old-component.tsx</delete-file>"
48837
+ },
48838
+ {
48839
+ role: "user",
48840
+ content: `Here is the output from delete-file of src/old-component.tsx:
48841
+
48842
+ Deleted: /repo/src/old-component.tsx
48843
+
48844
+ Please continue your response based on this output.`
48845
+ },
48846
+ {
48847
+ role: "assistant",
48848
+ content: "Done — `src/old-component.tsx` has been deleted."
48849
+ },
48850
+ {
48851
+ role: "user",
48852
+ content: "delete the legacy folder"
48853
+ },
48854
+ {
48855
+ role: "assistant",
48856
+ content: "<delete-folder>src/legacy</delete-folder>"
48857
+ },
48858
+ {
48859
+ role: "user",
48860
+ content: `Here is the output from delete-folder of src/legacy:
48861
+
48862
+ Deleted folder: /repo/src/legacy
48863
+
48864
+ Please continue your response based on this output.`
48865
+ },
48866
+ {
48867
+ role: "assistant",
48868
+ content: "Done — the `src/legacy` folder and all its contents have been deleted."
48869
+ },
48870
+ {
48871
+ role: "user",
48872
+ content: "open https://github.com/microsoft/typescript"
48873
+ },
48874
+ {
48875
+ role: "assistant",
48876
+ content: "<open-url>https://github.com/microsoft/typescript</open-url>"
48877
+ },
48878
+ {
48879
+ role: "user",
48880
+ content: `Here is the output from open-url https://github.com/microsoft/typescript:
48881
+
48882
+ Opened: https://github.com/microsoft/typescript
48883
+
48884
+ Please continue your response based on this output.`
48885
+ },
48886
+ {
48887
+ role: "assistant",
48888
+ content: "Opened the TypeScript GitHub page in your browser."
48889
+ },
48890
+ {
48891
+ role: "user",
48892
+ content: "generate a PDF report about the project and save it to docs/report.pdf"
48893
+ },
48894
+ {
48895
+ role: "assistant",
48896
+ content: `<generate-pdf>
48897
+ {"path": "docs/report.pdf", "content": "# Project Report\\n\\n## Overview\\n\\nThis document summarizes the project.\\n\\n## Details\\n\\nMore content here."}
48898
+ </generate-pdf>`
48899
+ },
48900
+ {
48901
+ role: "user",
48902
+ content: `Here is the output from generate-pdf to docs/report.pdf:
48903
+
48904
+ PDF generated: /repo/docs/report.pdf
48905
+
48906
+ Please continue your response based on this output.`
48907
+ },
48908
+ {
48909
+ role: "assistant",
48910
+ content: "Done — the PDF report has been saved to `docs/report.pdf`."
48911
+ },
48912
+ {
48913
+ role: "user",
48914
+ content: 'grep -R "ChatRunner" -n src'
48915
+ },
48916
+ {
48917
+ role: "assistant",
48918
+ content: `<grep>
48919
+ {"pattern": "ChatRunner", "glob": "src/**/*"}
48920
+ </grep>`
48921
+ },
48922
+ {
48923
+ role: "user",
48924
+ content: `Here is the output from grep for "ChatRunner":
48925
+
48926
+ grep /ChatRunner/ src/**/* — 3 match(es) in 2 file(s)
48927
+
48928
+ src/index.tsx
48929
+ 12: import { ChatRunner } from "./components/chat/ChatRunner";
48930
+
48931
+ src/components/chat/ChatRunner.tsx
48932
+ 1: export const ChatRunner = ...
48933
+
48934
+ Please continue your response based on this output.`
48935
+ },
48936
+ {
48937
+ role: "assistant",
48938
+ content: "`ChatRunner` is defined in `src/components/chat/ChatRunner.tsx` and imported in `src/index.tsx`."
48939
+ },
48940
+ {
48941
+ role: "user",
48942
+ content: "find all usages of useInput in the codebase"
48943
+ },
48944
+ {
48945
+ role: "assistant",
48946
+ content: `<grep>
48947
+ {"pattern": "useInput", "glob": "src/**/*.tsx"}
48948
+ </grep>`
48949
+ },
48950
+ {
48951
+ role: "user",
48952
+ content: `Here is the output from grep for "useInput":
48953
+
48954
+ grep /useInput/ src/**/*.tsx — 2 match(es) in 1 file(s)
48955
+
48956
+ src/components/chat/ChatRunner.tsx
48957
+ 5: import { useInput } from "ink";
48958
+ 210: useInput((input, key) => {
48959
+
48960
+ Please continue your response based on this output.`
48961
+ },
48962
+ {
48963
+ role: "assistant",
48964
+ content: "`useInput` is used in `src/components/chat/ChatRunner.tsx` — imported on line 5 and called on line 210."
48965
+ },
48966
+ {
48967
+ role: "user",
48968
+ content: "show me how to use the read-file tool"
48969
+ },
48970
+ {
48971
+ role: "assistant",
48972
+ content: "To read a file, emit the tag alone in your response — nothing else on that turn:\n\n```text\n[read-file]src/components/Header.tsx[/read-file]\n```\n\nThe result comes back as the next user message and you can then analyze it."
48973
+ },
48974
+ {
48975
+ role: "user",
48976
+ content: "show me an example of the shell tool"
48977
+ },
48978
+ {
48979
+ role: "assistant",
48980
+ content: "To run a shell command, emit only the tag:\n\n```text\n[shell]node -v[/shell]\n```\n\nThe stdout/stderr is returned and you can read it in the next turn."
48981
+ },
48982
+ {
48983
+ role: "user",
48984
+ content: "read src folder"
48985
+ },
48986
+ {
48987
+ role: "assistant",
48988
+ content: "<read-folder>src</read-folder>"
48989
+ },
48990
+ {
48991
+ role: "user",
48992
+ content: `Here is the output from read-folder of src:
48993
+
48994
+ Folder: src (4 entries)
48995
+
48996
+ Files:
48997
+ index.ts
48998
+ App.tsx
48999
+
49000
+ Subfolders:
49001
+ components/
49002
+ utils/
49003
+
49004
+ Please continue your response based on this output.`
49005
+ },
49006
+ {
49007
+ role: "assistant",
49008
+ content: "The `src` folder contains `index.ts`, `App.tsx`, plus subfolders `components/` and `utils/`."
49009
+ },
49010
+ {
49011
+ role: "user",
49012
+ content: "list src folder"
49013
+ },
49014
+ {
49015
+ role: "assistant",
49016
+ content: "<read-folder>src</read-folder>"
49017
+ },
49018
+ {
49019
+ role: "user",
49020
+ content: `Here is the output from read-folder of src:
49021
+
49022
+ Folder: src (4 entries)
49023
+
49024
+ Files:
49025
+ index.ts
49026
+ App.tsx
49027
+
49028
+ Subfolders:
49029
+ components/
49030
+ utils/
49031
+
49032
+ Please continue your response based on this output.`
49033
+ },
49034
+ {
49035
+ role: "assistant",
49036
+ content: "The `src` folder contains `index.ts`, `App.tsx`, plus subfolders `components/` and `utils/`."
49037
+ },
49038
+ {
49039
+ role: "user",
49040
+ content: "what files are in src/components?"
49041
+ },
49042
+ {
49043
+ role: "assistant",
49044
+ content: "<read-folder>src/components</read-folder>"
49045
+ },
49046
+ {
49047
+ role: "user",
49048
+ content: `Here is the output from read-folder of src/components:
49049
+
49050
+ Folder: src/components (5 entries)
49051
+
49052
+ Files:
49053
+ Header.tsx
49054
+ Footer.tsx
49055
+ Button.tsx
49056
+
49057
+ Subfolders:
49058
+ ui/
49059
+ forms/
49060
+
49061
+ Please continue your response based on this output.`
49062
+ },
49063
+ {
49064
+ role: "assistant",
49065
+ content: "The `src/components` folder has 3 files — `Header.tsx`, `Footer.tsx`, `Button.tsx` — plus two subfolders: `ui/` and `forms/`."
49066
+ },
49067
+ {
49068
+ role: "user",
49069
+ content: "list the files in src/utils"
49070
+ },
49071
+ {
49072
+ role: "assistant",
49073
+ content: "<read-folder>src/utils</read-folder>"
49074
+ },
49075
+ {
49076
+ role: "user",
49077
+ content: `Here is the output from read-folder of src/utils:
49078
+
49079
+ Folder: src/utils (3 entries)
49080
+
49081
+ Files:
49082
+ api.ts
49083
+ helpers.ts
49084
+ format.ts
49085
+
49086
+ Please continue your response based on this output.`
49087
+ },
49088
+ {
49089
+ role: "assistant",
49090
+ content: "The `src/utils` folder contains 3 files: `api.ts`, `helpers.ts`, and `format.ts`."
49091
+ },
49092
+ {
49093
+ role: "user",
49094
+ content: "show me what's in the src directory"
49095
+ },
49096
+ {
49097
+ role: "assistant",
49098
+ content: "<read-folder>src</read-folder>"
49099
+ },
49100
+ {
49101
+ role: "user",
49102
+ content: `Here is the output from read-folder of src:
49103
+
49104
+ Folder: src (4 entries)
49105
+
49106
+ Files:
49107
+ index.ts
49108
+ App.tsx
49109
+
49110
+ Subfolders:
49111
+ components/
49112
+ utils/
49113
+
49114
+ Please continue your response based on this output.`
49115
+ },
49116
+ {
49117
+ role: "assistant",
49118
+ content: "The `src` directory has 2 files (`index.ts`, `App.tsx`) and 2 subfolders (`components/`, `utils/`)."
49119
+ },
49120
+ {
49121
+ role: "user",
49122
+ content: "show me the project structure"
49123
+ },
49124
+ {
49125
+ role: "assistant",
49126
+ content: "<read-folder>.</read-folder>"
49127
+ },
49128
+ {
49129
+ role: "user",
49130
+ content: `Here is the output from read-folder of .:
49131
+
49132
+ Folder: . (5 entries)
49133
+
49134
+ Files:
49135
+ package.json
49136
+ tsconfig.json
49137
+ README.md
49138
+
49139
+ Subfolders:
49140
+ src/
49141
+ node_modules/
49142
+
49143
+ Please continue your response based on this output.`
49144
+ },
49145
+ {
49146
+ role: "assistant",
49147
+ content: "The project root has 3 files (`package.json`, `tsconfig.json`, `README.md`) and the `src/` folder for source code."
49148
+ },
49149
+ {
49150
+ role: "user",
49151
+ content: "what does the project look like?"
49152
+ },
49153
+ {
49154
+ role: "assistant",
49155
+ content: "<read-folder>.</read-folder>"
49156
+ },
49157
+ {
49158
+ role: "user",
49159
+ content: `Here is the output from read-folder of .:
49160
+
49161
+ Folder: . (4 entries)
49162
+
49163
+ Files:
49164
+ package.json
49165
+ README.md
49166
+
49167
+ Subfolders:
49168
+ src/
49169
+ dist/
49170
+
49171
+ Please continue your response based on this output.`
49172
+ },
49173
+ {
49174
+ role: "assistant",
49175
+ content: "The repo root contains `package.json` and `README.md`, with source code in `src/` and compiled output in `dist/`."
49176
+ },
48726
49177
  {
48727
49178
  role: "user",
48728
49179
  content: "fetch https://api.github.com/repos/microsoft/typescript"
@@ -48763,21 +49214,54 @@ Please continue your response based on this output.`
48763
49214
  },
48764
49215
  {
48765
49216
  role: "assistant",
48766
- content: "<read-file>ts-info.json</read-file>"
49217
+ content: "Done — `ts-info.json` has been saved."
49218
+ },
49219
+ {
49220
+ role: "user",
49221
+ content: "write it to a file called hello.py"
49222
+ },
49223
+ {
49224
+ role: "assistant",
49225
+ content: `<write-file>
49226
+ {"path": "hello.py", "content": "# hello.py\\nprint('hello')"}
49227
+ </write-file>`
49228
+ },
49229
+ {
49230
+ role: "user",
49231
+ content: `Here is the output from write-file to hello.py:
49232
+
49233
+ Written: /repo/hello.py (2 lines, 32 bytes)
49234
+
49235
+ Please continue your response based on this output.`
49236
+ },
49237
+ {
49238
+ role: "assistant",
49239
+ content: "Done — `hello.py` has been written."
49240
+ },
49241
+ {
49242
+ role: "user",
49243
+ content: "add a logout button to src/components/Header.tsx"
49244
+ },
49245
+ {
49246
+ role: "assistant",
49247
+ content: "<read-file>src/components/Header.tsx</read-file>"
48767
49248
  },
48768
49249
  {
48769
49250
  role: "user",
48770
- content: `Here is the output from read-file of ts-info.json:
49251
+ content: `Here is the output from read-file of src/components/Header.tsx:
48771
49252
 
48772
- File: ts-info.json (1 lines)
49253
+ File: src/components/Header.tsx (42 lines)
48773
49254
 
48774
- {"name":"TypeScript","stars":100000}
49255
+ import React from 'react';
49256
+ // ... full file content ...
48775
49257
 
48776
49258
  Please continue your response based on this output.`
48777
49259
  },
48778
49260
  {
48779
49261
  role: "assistant",
48780
- content: "Done — saved and verified `ts-info.json`. Data looks correct."
49262
+ content: `<changes>
49263
+ {"summary": "Add logout button to Header — preserves all existing nav items and imports", "patches": [{"path": "src/components/Header.tsx", "content": "// complete file with logout button added", "isNew": false}]}
49264
+ </changes>`
48781
49265
  },
48782
49266
  {
48783
49267
  role: "user",
@@ -48837,11 +49321,21 @@ Please continue your response based on this output.`
48837
49321
  }
48838
49322
  ];
48839
49323
  function parseResponse(text) {
49324
+ const scanText = text.replace(/```[\s\S]*?```/g, (m) => " ".repeat(m.length));
48840
49325
  const candidates = [];
48841
49326
  const patterns = [
48842
49327
  { kind: "fetch", re: /<fetch>([\s\S]*?)<\/fetch>/g },
48843
49328
  { kind: "shell", re: /<shell>([\s\S]*?)<\/shell>/g },
48844
49329
  { kind: "read-file", re: /<read-file>([\s\S]*?)<\/read-file>/g },
49330
+ { kind: "read-folder", re: /<read-folder>([\s\S]*?)<\/read-folder>/g },
49331
+ { kind: "grep", re: /<grep>([\s\S]*?)<\/grep>/g },
49332
+ { kind: "delete-file", re: /<delete-file>([\s\S]*?)<\/delete-file>/g },
49333
+ {
49334
+ kind: "delete-folder",
49335
+ re: /<delete-folder>([\s\S]*?)<\/delete-folder>/g
49336
+ },
49337
+ { kind: "open-url", re: /<open-url>([\s\S]*?)<\/open-url>/g },
49338
+ { kind: "generate-pdf", re: /<generate-pdf>([\s\S]*?)<\/generate-pdf>/g },
48845
49339
  { kind: "write-file", re: /<write-file>([\s\S]*?)<\/write-file>/g },
48846
49340
  { kind: "search", re: /<search>([\s\S]*?)<\/search>/g },
48847
49341
  { kind: "clone", re: /<clone>([\s\S]*?)<\/clone>/g },
@@ -48849,22 +49343,32 @@ function parseResponse(text) {
48849
49343
  { kind: "fetch", re: /```fetch\r?\n([\s\S]*?)\r?\n```/g },
48850
49344
  { kind: "shell", re: /```shell\r?\n([\s\S]*?)\r?\n```/g },
48851
49345
  { kind: "read-file", re: /```read-file\r?\n([\s\S]*?)\r?\n```/g },
49346
+ { kind: "read-folder", re: /```read-folder\r?\n([\s\S]*?)\r?\n```/g },
48852
49347
  { kind: "write-file", re: /```write-file\r?\n([\s\S]*?)\r?\n```/g },
48853
49348
  { kind: "search", re: /```search\r?\n([\s\S]*?)\r?\n```/g },
48854
49349
  { kind: "changes", re: /```changes\r?\n([\s\S]*?)\r?\n```/g }
48855
49350
  ];
48856
49351
  for (const { kind: kind2, re } of patterns) {
48857
49352
  re.lastIndex = 0;
48858
- const m = re.exec(text);
48859
- if (m)
48860
- candidates.push({ index: m.index, kind: kind2, match: m });
49353
+ const m = re.exec(scanText);
49354
+ if (m) {
49355
+ const originalRe = new RegExp(re.source, re.flags.replace("g", ""));
49356
+ const originalMatch = originalRe.exec(text.slice(m.index));
49357
+ if (originalMatch) {
49358
+ const fakeMatch = Object.assign([
49359
+ text.slice(m.index, m.index + originalMatch[0].length),
49360
+ originalMatch[1]
49361
+ ], { index: m.index, input: text, groups: undefined });
49362
+ candidates.push({ index: m.index, kind: kind2, match: fakeMatch });
49363
+ }
49364
+ }
48861
49365
  }
48862
49366
  if (candidates.length === 0)
48863
49367
  return { kind: "text", content: text.trim() };
48864
49368
  candidates.sort((a, b) => a.index - b.index);
48865
49369
  const { kind, match } = candidates[0];
48866
- const before2 = text.slice(0, match.index).trim();
48867
- const body = match[1].trim();
49370
+ const before2 = text.slice(0, match.index).replace(/<(fetch|shell|read-file|read-folder|write-file|search|clone|changes)[^>]*>[\s\S]*?<\/\1>/g, "").trim();
49371
+ const body = (match[1] ?? "").trim();
48868
49372
  if (kind === "changes") {
48869
49373
  try {
48870
49374
  const parsed = JSON.parse(body);
@@ -48882,6 +49386,42 @@ function parseResponse(text) {
48882
49386
  }
48883
49387
  if (kind === "read-file")
48884
49388
  return { kind: "read-file", content: before2, filePath: body };
49389
+ if (kind === "read-folder")
49390
+ return { kind: "read-folder", content: before2, folderPath: body };
49391
+ if (kind === "delete-file")
49392
+ return { kind: "delete-file", content: before2, filePath: body };
49393
+ if (kind === "delete-folder")
49394
+ return { kind: "delete-folder", content: before2, folderPath: body };
49395
+ if (kind === "open-url") {
49396
+ const url = body.replace(/^<|>$/g, "").trim();
49397
+ return { kind: "open-url", content: before2, url };
49398
+ }
49399
+ if (kind === "generate-pdf") {
49400
+ try {
49401
+ const parsed = JSON.parse(body);
49402
+ return {
49403
+ kind: "generate-pdf",
49404
+ content: before2,
49405
+ filePath: parsed.path ?? parsed.filePath ?? "output.pdf",
49406
+ pdfContent: parsed.content ?? ""
49407
+ };
49408
+ } catch {
49409
+ return { kind: "text", content: text };
49410
+ }
49411
+ }
49412
+ if (kind === "grep") {
49413
+ try {
49414
+ const parsed = JSON.parse(body);
49415
+ return {
49416
+ kind: "grep",
49417
+ content: before2,
49418
+ pattern: parsed.pattern,
49419
+ glob: parsed.glob ?? "**/*"
49420
+ };
49421
+ } catch {
49422
+ return { kind: "grep", content: before2, pattern: body, glob: "**/*" };
49423
+ }
49424
+ }
48885
49425
  if (kind === "write-file") {
48886
49426
  try {
48887
49427
  const parsed = JSON.parse(body);
@@ -48918,7 +49458,7 @@ function buildApiMessages(messages) {
48918
49458
  content: "The tool call was denied by the user. Please respond without using that tool."
48919
49459
  };
48920
49460
  }
48921
- const label = m.toolName === "shell" ? `shell command \`${m.content}\`` : m.toolName === "fetch" ? `fetch of ${m.content}` : m.toolName === "read-file" ? `read-file of ${m.content}` : m.toolName === "search" ? `web search for "${m.content}"` : `write-file to ${m.content}`;
49461
+ const label = m.toolName === "shell" ? `shell command \`${m.content}\`` : m.toolName === "fetch" ? `fetch of ${m.content}` : m.toolName === "read-file" ? `read-file of ${m.content}` : m.toolName === "read-folder" ? `read-folder of ${m.content}` : m.toolName === "grep" ? `grep for "${m.content}"` : m.toolName === "delete-file" ? `delete-file of ${m.content}` : m.toolName === "delete-folder" ? `delete-folder of ${m.content}` : m.toolName === "open-url" ? `open-url ${m.content}` : m.toolName === "generate-pdf" ? `generate-pdf to ${m.content}` : m.toolName === "search" ? `web search for "${m.content}"` : `write-file to ${m.content}`;
48922
49462
  return {
48923
49463
  role: "user",
48924
49464
  content: `Here is the output from the ${label}:
@@ -48931,7 +49471,7 @@ Please continue your response based on this output.`
48931
49471
  return { role: m.role, content: m.content };
48932
49472
  });
48933
49473
  }
48934
- async function callChat(provider, systemPrompt, messages) {
49474
+ async function callChat(provider, systemPrompt, messages, abortSignal) {
48935
49475
  const apiMessages = [...FEW_SHOT_MESSAGES, ...buildApiMessages(messages)];
48936
49476
  let url;
48937
49477
  let headers;
@@ -48964,6 +49504,7 @@ async function callChat(provider, systemPrompt, messages) {
48964
49504
  }
48965
49505
  const controller = new AbortController;
48966
49506
  const timer = setTimeout(() => controller.abort(), 60000);
49507
+ abortSignal?.addEventListener("abort", () => controller.abort());
48967
49508
  const res = await fetch(url, {
48968
49509
  method: "POST",
48969
49510
  headers,
@@ -49237,164 +49778,1315 @@ ${topics}`);
49237
49778
  if (snippets.length === 0 && links.length === 0) {
49238
49779
  return `No results found for: ${query}`;
49239
49780
  }
49240
- const parts = [`Search results for: ${query}`];
49241
- if (snippets.length > 0)
49242
- parts.push(`Snippets:
49243
- ${snippets.join(`
49244
- `)}`);
49245
- if (links.length > 0)
49246
- parts.push(`Links:
49247
- ${links.join(`
49248
- `)}`);
49249
- return parts.join(`
49250
-
49251
- `);
49252
- } catch (err) {
49253
- return `Search failed: ${err instanceof Error ? err.message : String(err)}`;
49254
- }
49781
+ const parts = [`Search results for: ${query}`];
49782
+ if (snippets.length > 0)
49783
+ parts.push(`Snippets:
49784
+ ${snippets.join(`
49785
+ `)}`);
49786
+ if (links.length > 0)
49787
+ parts.push(`Links:
49788
+ ${links.join(`
49789
+ `)}`);
49790
+ return parts.join(`
49791
+
49792
+ `);
49793
+ } catch (err) {
49794
+ return `Search failed: ${err instanceof Error ? err.message : String(err)}`;
49795
+ }
49796
+ }
49797
+ function readFile(filePath, repoPath) {
49798
+ const candidates = path14.isAbsolute(filePath) ? [filePath] : [filePath, path14.join(repoPath, filePath)];
49799
+ for (const candidate of candidates) {
49800
+ if (existsSync10(candidate)) {
49801
+ try {
49802
+ const content = readFileSync10(candidate, "utf-8");
49803
+ const lines = content.split(`
49804
+ `).length;
49805
+ return `File: ${candidate} (${lines} lines)
49806
+
49807
+ ${content.slice(0, 8000)}${content.length > 8000 ? `
49808
+
49809
+ … (truncated)` : ""}`;
49810
+ } catch (err) {
49811
+ return `Error reading file: ${err instanceof Error ? err.message : String(err)}`;
49812
+ }
49813
+ }
49814
+ }
49815
+ return `File not found: ${filePath}. If reading from a cloned repo, use the full absolute path e.g. C:\\Users\\...\\repo\\file.ts`;
49816
+ }
49817
+ function readFolder(folderPath, repoPath) {
49818
+ const sanitized = folderPath.replace(/^(ls|dir|find|tree|cat|read|ls -la?|ls -al?)\s+/i, "").trim();
49819
+ const candidates = path14.isAbsolute(sanitized) ? [sanitized] : [sanitized, path14.join(repoPath, sanitized)];
49820
+ for (const candidate of candidates) {
49821
+ if (!existsSync10(candidate))
49822
+ continue;
49823
+ let stat;
49824
+ try {
49825
+ stat = statSync4(candidate);
49826
+ } catch {
49827
+ continue;
49828
+ }
49829
+ if (!stat.isDirectory()) {
49830
+ return `Not a directory: ${candidate}. Use read-file to read a file.`;
49831
+ }
49832
+ let entries;
49833
+ try {
49834
+ entries = readdirSync3(candidate, { encoding: "utf-8" });
49835
+ } catch (err) {
49836
+ return `Error reading folder: ${err instanceof Error ? err.message : String(err)}`;
49837
+ }
49838
+ const files = [];
49839
+ const subfolders = [];
49840
+ for (const entry of entries) {
49841
+ if (entry.startsWith(".") && entry !== ".env")
49842
+ continue;
49843
+ const full = path14.join(candidate, entry);
49844
+ try {
49845
+ if (statSync4(full).isDirectory()) {
49846
+ subfolders.push(`${entry}/`);
49847
+ } else {
49848
+ files.push(entry);
49849
+ }
49850
+ } catch {}
49851
+ }
49852
+ const total = files.length + subfolders.length;
49853
+ const lines = [`Folder: ${candidate} (${total} entries)`, ""];
49854
+ if (files.length > 0) {
49855
+ lines.push("Files:");
49856
+ files.forEach((f) => lines.push(` ${f}`));
49857
+ }
49858
+ if (subfolders.length > 0) {
49859
+ if (files.length > 0)
49860
+ lines.push("");
49861
+ lines.push("Subfolders:");
49862
+ subfolders.forEach((d) => lines.push(` ${d}`));
49863
+ }
49864
+ if (total === 0) {
49865
+ lines.push("(empty folder)");
49866
+ }
49867
+ return lines.join(`
49868
+ `);
49869
+ }
49870
+ return `Folder not found: ${sanitized}`;
49871
+ }
49872
+ function grepFiles(pattern, glob, repoPath) {
49873
+ let regex2;
49874
+ try {
49875
+ regex2 = new RegExp(pattern, "i");
49876
+ } catch {
49877
+ regex2 = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
49878
+ }
49879
+ const globToFilter = (g) => {
49880
+ const cleaned = g.replace(/^\*\*\//, "");
49881
+ const parts = cleaned.split("/");
49882
+ const ext = parts[parts.length - 1];
49883
+ const prefix = parts.slice(0, -1).join("/");
49884
+ return (rel) => {
49885
+ if (ext?.startsWith("*.")) {
49886
+ const extSuffix = ext.slice(1);
49887
+ if (!rel.endsWith(extSuffix))
49888
+ return false;
49889
+ } else if (ext && !ext.includes("*")) {
49890
+ if (!rel.endsWith(ext))
49891
+ return false;
49892
+ }
49893
+ if (prefix && !prefix.includes("*")) {
49894
+ if (!rel.startsWith(prefix))
49895
+ return false;
49896
+ }
49897
+ return true;
49898
+ };
49899
+ };
49900
+ const filter2 = globToFilter(glob);
49901
+ const allFiles = walkDir3(repoPath);
49902
+ const matchedFiles = allFiles.filter(filter2);
49903
+ if (matchedFiles.length === 0) {
49904
+ return `No files matched glob: ${glob}`;
49905
+ }
49906
+ const results = [];
49907
+ let totalMatches = 0;
49908
+ for (const relPath of matchedFiles) {
49909
+ const fullPath = path14.join(repoPath, relPath);
49910
+ let content;
49911
+ try {
49912
+ content = readFileSync10(fullPath, "utf-8");
49913
+ } catch {
49914
+ continue;
49915
+ }
49916
+ const lines = content.split(`
49917
+ `);
49918
+ const fileMatches = [];
49919
+ lines.forEach((line, i) => {
49920
+ if (regex2.test(line)) {
49921
+ fileMatches.push(` ${i + 1}: ${line.trimEnd()}`);
49922
+ totalMatches++;
49923
+ }
49924
+ });
49925
+ if (fileMatches.length > 0) {
49926
+ results.push(`${relPath}
49927
+ ${fileMatches.join(`
49928
+ `)}`);
49929
+ }
49930
+ if (totalMatches >= 200) {
49931
+ results.push("(truncated — too many matches)");
49932
+ break;
49933
+ }
49934
+ }
49935
+ if (results.length === 0) {
49936
+ return `No matches for /${pattern}/ in ${matchedFiles.length} file(s) matching ${glob}`;
49937
+ }
49938
+ return `grep /${pattern}/ ${glob} — ${totalMatches} match(es) in ${results.length} file(s)
49939
+
49940
+ ${results.join(`
49941
+
49942
+ `)}`;
49943
+ }
49944
+ function writeFile(filePath, content, repoPath) {
49945
+ const fullPath = path14.isAbsolute(filePath) ? filePath : path14.join(repoPath, filePath);
49946
+ try {
49947
+ const dir = path14.dirname(fullPath);
49948
+ if (!existsSync10(dir))
49949
+ mkdirSync4(dir, { recursive: true });
49950
+ writeFileSync6(fullPath, content, "utf-8");
49951
+ const lines = content.split(`
49952
+ `).length;
49953
+ return `Written: ${fullPath} (${lines} lines, ${content.length} bytes)`;
49954
+ } catch (err) {
49955
+ return `Error writing file: ${err instanceof Error ? err.message : String(err)}`;
49956
+ }
49957
+ }
49958
+ function deleteFile(filePath, repoPath) {
49959
+ const fullPath = path14.isAbsolute(filePath) ? filePath : path14.join(repoPath, filePath);
49960
+ try {
49961
+ if (!existsSync10(fullPath))
49962
+ return `File not found: ${fullPath}`;
49963
+ const { unlinkSync } = __require("fs");
49964
+ unlinkSync(fullPath);
49965
+ return `Deleted: ${fullPath}`;
49966
+ } catch (err) {
49967
+ return `Error deleting file: ${err instanceof Error ? err.message : String(err)}`;
49968
+ }
49969
+ }
49970
+ function deleteFolder(folderPath, repoPath) {
49971
+ const fullPath = path14.isAbsolute(folderPath) ? folderPath : path14.join(repoPath, folderPath);
49972
+ try {
49973
+ if (!existsSync10(fullPath))
49974
+ return `Folder not found: ${fullPath}`;
49975
+ const { rmSync } = __require("fs");
49976
+ rmSync(fullPath, { recursive: true, force: true });
49977
+ return `Deleted folder: ${fullPath}`;
49978
+ } catch (err) {
49979
+ return `Error deleting folder: ${err instanceof Error ? err.message : String(err)}`;
49980
+ }
49981
+ }
49982
+ function openUrl(url) {
49983
+ try {
49984
+ const { execSync: execSync2 } = __require("child_process");
49985
+ const platform2 = process.platform;
49986
+ if (platform2 === "win32") {
49987
+ execSync2(`start "" "${url}"`, { stdio: "ignore" });
49988
+ } else if (platform2 === "darwin") {
49989
+ execSync2(`open "${url}"`, { stdio: "ignore" });
49990
+ } else {
49991
+ execSync2(`xdg-open "${url}"`, { stdio: "ignore" });
49992
+ }
49993
+ return `Opened: ${url}`;
49994
+ } catch (err) {
49995
+ return `Error opening URL: ${err instanceof Error ? err.message : String(err)}`;
49996
+ }
49997
+ }
49998
+ function generatePdf(filePath, content, repoPath) {
49999
+ const fullPath = path14.isAbsolute(filePath) ? filePath : path14.join(repoPath, filePath);
50000
+ try {
50001
+ const dir = path14.dirname(fullPath);
50002
+ if (!existsSync10(dir))
50003
+ mkdirSync4(dir, { recursive: true });
50004
+ const escaped = content.replace(/\\/g, "\\\\").replace(/"""/g, "\\\"\\\"\\\"").replace(/\r/g, "");
50005
+ const script = `
50006
+ import sys
50007
+ try:
50008
+ from reportlab.lib.pagesizes import letter
50009
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, HRFlowable
50010
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
50011
+ from reportlab.lib.units import inch
50012
+ from reportlab.lib import colors
50013
+ except ImportError:
50014
+ import subprocess
50015
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "reportlab", "--break-system-packages", "-q"])
50016
+ from reportlab.lib.pagesizes import letter
50017
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, HRFlowable
50018
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
50019
+ from reportlab.lib.units import inch
50020
+ from reportlab.lib import colors
50021
+
50022
+ doc = SimpleDocTemplate(
50023
+ r"""${fullPath}""",
50024
+ pagesize=letter,
50025
+ rightMargin=inch,
50026
+ leftMargin=inch,
50027
+ topMargin=inch,
50028
+ bottomMargin=inch,
50029
+ )
50030
+
50031
+ styles = getSampleStyleSheet()
50032
+ styles.add(ParagraphStyle(name="H1", parent=styles["Heading1"], fontSize=22, spaceAfter=10))
50033
+ styles.add(ParagraphStyle(name="H2", parent=styles["Heading2"], fontSize=16, spaceAfter=8))
50034
+ styles.add(ParagraphStyle(name="H3", parent=styles["Heading3"], fontSize=13, spaceAfter=6))
50035
+ styles.add(ParagraphStyle(name="Body", parent=styles["Normal"], fontSize=11, leading=16, spaceAfter=8))
50036
+ styles.add(ParagraphStyle(name="Bullet", parent=styles["Normal"], fontSize=11, leading=16, leftIndent=20, spaceAfter=4, bulletIndent=10))
50037
+
50038
+ raw = """${escaped}"""
50039
+
50040
+ story = []
50041
+ for line in raw.split("\\n"):
50042
+ s = line.rstrip()
50043
+ if s.startswith("### "):
50044
+ story.append(Paragraph(s[4:], styles["H3"]))
50045
+ elif s.startswith("## "):
50046
+ story.append(Spacer(1, 6))
50047
+ story.append(Paragraph(s[3:], styles["H2"]))
50048
+ story.append(HRFlowable(width="100%", thickness=0.5, color=colors.grey, spaceAfter=4))
50049
+ elif s.startswith("# "):
50050
+ story.append(Paragraph(s[2:], styles["H1"]))
50051
+ story.append(HRFlowable(width="100%", thickness=1, color=colors.black, spaceAfter=6))
50052
+ elif s.startswith("- ") or s.startswith("* "):
50053
+ story.append(Paragraph(u"\\u2022 " + s[2:], styles["Bullet"]))
50054
+ elif s.startswith("---"):
50055
+ story.append(HRFlowable(width="100%", thickness=0.5, color=colors.grey, spaceAfter=4))
50056
+ elif s == "":
50057
+ story.append(Spacer(1, 6))
50058
+ else:
50059
+ import re
50060
+ s = re.sub(r"\\*\\*(.+?)\\*\\*", r"<b>\\1</b>", s)
50061
+ s = re.sub(r"\\*(.+?)\\*", r"<i>\\1</i>", s)
50062
+ s = re.sub(r"\`(.+?)\`", r"<font name='Courier'>\\1</font>", s)
50063
+ story.append(Paragraph(s, styles["Body"]))
50064
+
50065
+ doc.build(story)
50066
+ print("OK")
50067
+ `.replace("${fullPath}", fullPath.replace(/\\/g, "/")).replace("${escaped}", escaped);
50068
+ const os6 = __require("os");
50069
+ const tmpFile = path14.join(os6.tmpdir(), `lens_pdf_${Date.now()}.py`);
50070
+ writeFileSync6(tmpFile, script, "utf-8");
50071
+ const { execSync: execSync2 } = __require("child_process");
50072
+ execSync2(`python "${tmpFile}"`, { stdio: "pipe" });
50073
+ try {
50074
+ __require("fs").unlinkSync(tmpFile);
50075
+ } catch {}
50076
+ return `PDF generated: ${fullPath}`;
50077
+ } catch (err) {
50078
+ return `Error generating PDF: ${err instanceof Error ? err.message : String(err)}`;
50079
+ }
50080
+ }
50081
+
50082
+ // node_modules/sugar-high/lib/index.js
50083
+ var JSXBrackets = new Set(["<", ">", "{", "}", "[", "]"]);
50084
+ var Keywords_Js = new Set([
50085
+ "for",
50086
+ "do",
50087
+ "while",
50088
+ "if",
50089
+ "else",
50090
+ "return",
50091
+ "function",
50092
+ "var",
50093
+ "let",
50094
+ "const",
50095
+ "true",
50096
+ "false",
50097
+ "undefined",
50098
+ "this",
50099
+ "new",
50100
+ "delete",
50101
+ "typeof",
50102
+ "in",
50103
+ "instanceof",
50104
+ "void",
50105
+ "break",
50106
+ "continue",
50107
+ "switch",
50108
+ "case",
50109
+ "default",
50110
+ "throw",
50111
+ "try",
50112
+ "catch",
50113
+ "finally",
50114
+ "debugger",
50115
+ "with",
50116
+ "yield",
50117
+ "async",
50118
+ "await",
50119
+ "class",
50120
+ "extends",
50121
+ "super",
50122
+ "import",
50123
+ "export",
50124
+ "from",
50125
+ "static"
50126
+ ]);
50127
+ var Signs = new Set([
50128
+ "+",
50129
+ "-",
50130
+ "*",
50131
+ "/",
50132
+ "%",
50133
+ "=",
50134
+ "!",
50135
+ "&",
50136
+ "|",
50137
+ "^",
50138
+ "~",
50139
+ "!",
50140
+ "?",
50141
+ ":",
50142
+ ".",
50143
+ ",",
50144
+ ";",
50145
+ `'`,
50146
+ '"',
50147
+ ".",
50148
+ "(",
50149
+ ")",
50150
+ "[",
50151
+ "]",
50152
+ "#",
50153
+ "@",
50154
+ "\\",
50155
+ ...JSXBrackets
50156
+ ]);
50157
+ var DefaultOptions = {
50158
+ keywords: Keywords_Js,
50159
+ onCommentStart: isCommentStart_Js,
50160
+ onCommentEnd: isCommentEnd_Js
50161
+ };
50162
+ var TokenTypes = [
50163
+ "identifier",
50164
+ "keyword",
50165
+ "string",
50166
+ "class",
50167
+ "property",
50168
+ "entity",
50169
+ "jsxliterals",
50170
+ "sign",
50171
+ "comment",
50172
+ "break",
50173
+ "space"
50174
+ ];
50175
+ var [
50176
+ T_IDENTIFIER,
50177
+ T_KEYWORD,
50178
+ T_STRING,
50179
+ T_CLS_NUMBER,
50180
+ T_PROPERTY,
50181
+ T_ENTITY,
50182
+ T_JSX_LITERALS,
50183
+ T_SIGN,
50184
+ T_COMMENT,
50185
+ T_BREAK,
50186
+ T_SPACE
50187
+ ] = TokenTypes.map((_, i) => i);
50188
+ function isSpaces(str) {
50189
+ return /^[^\S\r\n]+$/g.test(str);
50190
+ }
50191
+ function isSign(ch) {
50192
+ return Signs.has(ch);
50193
+ }
50194
+ function isWord(chr) {
50195
+ return /^[\w_]+$/.test(chr) || hasUnicode(chr);
50196
+ }
50197
+ function isCls(str) {
50198
+ const chr0 = str[0];
50199
+ return isWord(chr0) && chr0 === chr0.toUpperCase() || str === "null";
50200
+ }
50201
+ function hasUnicode(s) {
50202
+ return /[^\u0000-\u007f]/.test(s);
50203
+ }
50204
+ function isAlpha(chr) {
50205
+ return /^[a-zA-Z]$/.test(chr);
50206
+ }
50207
+ function isIdentifierChar(chr) {
50208
+ return isAlpha(chr) || hasUnicode(chr);
50209
+ }
50210
+ function isIdentifier(str) {
50211
+ return isIdentifierChar(str[0]) && (str.length === 1 || isWord(str.slice(1)));
50212
+ }
50213
+ function isStrTemplateChr(chr) {
50214
+ return chr === "`";
50215
+ }
50216
+ function isSingleQuotes(chr) {
50217
+ return chr === '"' || chr === "'";
50218
+ }
50219
+ function isStringQuotation(chr) {
50220
+ return isSingleQuotes(chr) || isStrTemplateChr(chr);
50221
+ }
50222
+ function isCommentStart_Js(curr, next) {
50223
+ const str = curr + next;
50224
+ if (str === "/*")
50225
+ return 2;
50226
+ return str === "//" ? 1 : 0;
50227
+ }
50228
+ function isCommentEnd_Js(prev, curr) {
50229
+ return prev + curr === "*/" ? 2 : curr === `
50230
+ ` ? 1 : 0;
50231
+ }
50232
+ function isRegexStart(str) {
50233
+ return str[0] === "/" && !isCommentStart_Js(str[0], str[1]);
50234
+ }
50235
+ function tokenize3(code, options) {
50236
+ const {
50237
+ keywords,
50238
+ onCommentStart,
50239
+ onCommentEnd
50240
+ } = { ...DefaultOptions, ...options };
50241
+ let current = "";
50242
+ let type = -1;
50243
+ let last2 = [-1, ""];
50244
+ let beforeLast = [-2, ""];
50245
+ const tokens = [];
50246
+ let __jsxEnter = false;
50247
+ let __jsxTag = 0;
50248
+ let __jsxExpr = false;
50249
+ let __jsxStack = 0;
50250
+ const __jsxChild = () => __jsxEnter && !__jsxExpr && !__jsxTag;
50251
+ const inJsxTag = () => __jsxTag && !__jsxChild();
50252
+ const inJsxLiterals = () => !__jsxTag && __jsxChild() && !__jsxExpr && __jsxStack > 0;
50253
+ let __strQuote = null;
50254
+ let __regexQuoteStart = false;
50255
+ let __strTemplateExprStack = 0;
50256
+ let __strTemplateQuoteStack = 0;
50257
+ const inStringQuotes = () => __strQuote !== null;
50258
+ const inRegexQuotes = () => __regexQuoteStart;
50259
+ const inStrTemplateLiterals = () => __strTemplateQuoteStack > __strTemplateExprStack;
50260
+ const inStrTemplateExpr = () => __strTemplateQuoteStack > 0 && __strTemplateQuoteStack === __strTemplateExprStack;
50261
+ const inStringContent = () => inStringQuotes() || inStrTemplateLiterals();
50262
+ function classify(token) {
50263
+ const isLineBreak = token === `
50264
+ `;
50265
+ if (inJsxTag()) {
50266
+ if (inStringQuotes()) {
50267
+ return T_STRING;
50268
+ }
50269
+ const [, lastToken] = last2;
50270
+ if (isIdentifier(token)) {
50271
+ if (lastToken === "<" || lastToken === "</")
50272
+ return T_ENTITY;
50273
+ }
50274
+ }
50275
+ const isJsxLiterals = inJsxLiterals();
50276
+ if (isJsxLiterals)
50277
+ return T_JSX_LITERALS;
50278
+ if (inStringQuotes() || inStrTemplateLiterals()) {
50279
+ return T_STRING;
50280
+ } else if (keywords.has(token)) {
50281
+ return last2[1] === "." ? T_IDENTIFIER : T_KEYWORD;
50282
+ } else if (isLineBreak) {
50283
+ return T_BREAK;
50284
+ } else if (isSpaces(token)) {
50285
+ return T_SPACE;
50286
+ } else if (token.split("").every(isSign)) {
50287
+ return T_SIGN;
50288
+ } else if (isCls(token)) {
50289
+ return inJsxTag() ? T_IDENTIFIER : T_CLS_NUMBER;
50290
+ } else {
50291
+ if (isIdentifier(token)) {
50292
+ const isLastPropDot = last2[1] === "." && isIdentifier(beforeLast[1]);
50293
+ if (!inStringContent() && !isLastPropDot)
50294
+ return T_IDENTIFIER;
50295
+ if (isLastPropDot)
50296
+ return T_PROPERTY;
50297
+ }
50298
+ return T_STRING;
50299
+ }
50300
+ }
50301
+ const append = (type_, token_) => {
50302
+ if (token_) {
50303
+ current = token_;
50304
+ }
50305
+ if (current) {
50306
+ type = type_ || classify(current);
50307
+ const pair = [type, current];
50308
+ if (type !== T_SPACE && type !== T_BREAK) {
50309
+ beforeLast = last2;
50310
+ last2 = pair;
50311
+ }
50312
+ tokens.push(pair);
50313
+ }
50314
+ current = "";
50315
+ };
50316
+ for (let i = 0;i < code.length; i++) {
50317
+ const curr = code[i];
50318
+ const prev = code[i - 1];
50319
+ const next = code[i + 1];
50320
+ const p_c = prev + curr;
50321
+ const c_n = curr + next;
50322
+ if (isSingleQuotes(curr) && !inJsxLiterals() && !inStrTemplateLiterals()) {
50323
+ append();
50324
+ if (prev !== `\\`) {
50325
+ if (__strQuote && curr === __strQuote) {
50326
+ __strQuote = null;
50327
+ } else if (!__strQuote) {
50328
+ __strQuote = curr;
50329
+ }
50330
+ }
50331
+ append(T_STRING, curr);
50332
+ continue;
50333
+ }
50334
+ if (!inStrTemplateLiterals()) {
50335
+ if (prev !== "\\n" && isStrTemplateChr(curr)) {
50336
+ append();
50337
+ append(T_STRING, curr);
50338
+ __strTemplateQuoteStack++;
50339
+ continue;
50340
+ }
50341
+ }
50342
+ if (inStrTemplateLiterals()) {
50343
+ if (prev !== "\\n" && isStrTemplateChr(curr)) {
50344
+ if (__strTemplateQuoteStack > 0) {
50345
+ append();
50346
+ __strTemplateQuoteStack--;
50347
+ append(T_STRING, curr);
50348
+ continue;
50349
+ }
50350
+ }
50351
+ if (c_n === "${") {
50352
+ __strTemplateExprStack++;
50353
+ append(T_STRING);
50354
+ append(T_SIGN, c_n);
50355
+ i++;
50356
+ continue;
50357
+ }
50358
+ }
50359
+ if (inStrTemplateExpr() && curr === "}") {
50360
+ append();
50361
+ __strTemplateExprStack--;
50362
+ append(T_SIGN, curr);
50363
+ continue;
50364
+ }
50365
+ if (__jsxChild()) {
50366
+ if (curr === "{") {
50367
+ append();
50368
+ append(T_SIGN, curr);
50369
+ __jsxExpr = true;
50370
+ continue;
50371
+ }
50372
+ }
50373
+ if (__jsxEnter) {
50374
+ if (!__jsxTag && curr === "<") {
50375
+ append();
50376
+ if (next === "/") {
50377
+ __jsxTag = 2;
50378
+ current = c_n;
50379
+ i++;
50380
+ } else {
50381
+ __jsxTag = 1;
50382
+ current = curr;
50383
+ }
50384
+ append(T_SIGN);
50385
+ continue;
50386
+ }
50387
+ if (__jsxTag) {
50388
+ if (curr === ">" && !"/=".includes(prev)) {
50389
+ append();
50390
+ if (__jsxTag === 1) {
50391
+ __jsxTag = 0;
50392
+ __jsxStack++;
50393
+ } else {
50394
+ __jsxTag = 0;
50395
+ __jsxEnter = false;
50396
+ }
50397
+ append(T_SIGN, curr);
50398
+ continue;
50399
+ }
50400
+ if (c_n === "/>" || c_n === "</") {
50401
+ if (current !== "<" && current !== "/") {
50402
+ append();
50403
+ }
50404
+ if (c_n === "/>") {
50405
+ __jsxTag = 0;
50406
+ } else {
50407
+ __jsxStack--;
50408
+ }
50409
+ if (!__jsxStack)
50410
+ __jsxEnter = false;
50411
+ current = c_n;
50412
+ i++;
50413
+ append(T_SIGN);
50414
+ continue;
50415
+ }
50416
+ if (curr === "<") {
50417
+ append();
50418
+ current = curr;
50419
+ append(T_SIGN);
50420
+ continue;
50421
+ }
50422
+ if (next === "-" && !inStringContent() && !inJsxLiterals()) {
50423
+ if (current) {
50424
+ append(T_PROPERTY, current + curr + next);
50425
+ i++;
50426
+ continue;
50427
+ }
50428
+ }
50429
+ if (next === "=" && !inStringContent()) {
50430
+ if (!isSpaces(curr)) {
50431
+ if (isSpaces(current)) {
50432
+ append();
50433
+ }
50434
+ const prop = current + curr;
50435
+ if (isIdentifier(prop)) {
50436
+ append(T_PROPERTY, prop);
50437
+ continue;
50438
+ }
50439
+ }
50440
+ }
50441
+ }
50442
+ }
50443
+ if (!__jsxTag && (curr === "<" && isIdentifierChar(next) || c_n === "</")) {
50444
+ __jsxTag = next === "/" ? 2 : 1;
50445
+ if (curr === "<" && (next === "/" || isAlpha(next))) {
50446
+ if (!inStringContent() && !inJsxLiterals() && !inRegexQuotes()) {
50447
+ __jsxEnter = true;
50448
+ }
50449
+ }
50450
+ }
50451
+ const isQuotationChar = isStringQuotation(curr);
50452
+ const isStringTemplateLiterals = inStrTemplateLiterals();
50453
+ const isRegexChar = !__jsxEnter && isRegexStart(c_n);
50454
+ const isJsxLiterals = inJsxLiterals();
50455
+ if (isQuotationChar || isStringTemplateLiterals || isSingleQuotes(__strQuote)) {
50456
+ current += curr;
50457
+ } else if (isRegexChar) {
50458
+ append();
50459
+ const [lastType, lastToken] = last2;
50460
+ if (isRegexChar && lastType !== -1 && !(lastType === T_SIGN && lastToken !== ")" || lastType === T_COMMENT)) {
50461
+ current = curr;
50462
+ append();
50463
+ continue;
50464
+ }
50465
+ __regexQuoteStart = true;
50466
+ const start = i++;
50467
+ const isEof = () => i >= code.length;
50468
+ const isEol = () => isEof() || code[i] === `
50469
+ `;
50470
+ let foundClose = false;
50471
+ for (;!isEol(); i++) {
50472
+ if (code[i] === "/" && code[i - 1] !== "\\") {
50473
+ foundClose = true;
50474
+ while (start !== i && /^[a-z]$/.test(code[i + 1]) && !isEol()) {
50475
+ i++;
50476
+ }
50477
+ break;
50478
+ }
50479
+ }
50480
+ __regexQuoteStart = false;
50481
+ if (start !== i && foundClose) {
50482
+ current = code.slice(start, i + 1);
50483
+ append(T_STRING);
50484
+ } else {
50485
+ current = curr;
50486
+ append();
50487
+ i = start;
50488
+ }
50489
+ } else if (onCommentStart(curr, next)) {
50490
+ append();
50491
+ const start = i;
50492
+ const startCommentType = onCommentStart(curr, next);
50493
+ if (startCommentType) {
50494
+ for (;i < code.length; i++) {
50495
+ const endCommentType = onCommentEnd(code[i - 1], code[i]);
50496
+ if (endCommentType == startCommentType)
50497
+ break;
50498
+ }
50499
+ }
50500
+ current = code.slice(start, i + 1);
50501
+ append(T_COMMENT);
50502
+ } else if (curr === " " || curr === `
50503
+ `) {
50504
+ if (curr === " " && (isSpaces(current) || !current || isJsxLiterals)) {
50505
+ current += curr;
50506
+ if (next === "<") {
50507
+ append();
50508
+ }
50509
+ } else {
50510
+ append();
50511
+ current = curr;
50512
+ append();
50513
+ }
50514
+ } else {
50515
+ if (__jsxExpr && curr === "}") {
50516
+ append();
50517
+ current = curr;
50518
+ append();
50519
+ __jsxExpr = false;
50520
+ } else if (isJsxLiterals && !JSXBrackets.has(curr) || inStrTemplateLiterals() || (isWord(curr) === isWord(current[current.length - 1]) || __jsxChild()) && !Signs.has(curr)) {
50521
+ current += curr;
50522
+ } else {
50523
+ if (p_c === "</") {
50524
+ current = p_c;
50525
+ }
50526
+ append();
50527
+ if (p_c !== "</") {
50528
+ current = curr;
50529
+ }
50530
+ if (c_n === "</" || c_n === "/>") {
50531
+ current = c_n;
50532
+ append();
50533
+ i++;
50534
+ } else if (JSXBrackets.has(curr))
50535
+ append();
50536
+ }
50537
+ }
50538
+ }
50539
+ append();
50540
+ return tokens;
50541
+ }
50542
+ var SugarHigh = {
50543
+ TokenTypes,
50544
+ TokenMap: new Map(TokenTypes.map((type, i) => [type, i]))
50545
+ };
50546
+
50547
+ // src/components/chat/ChatMessage.tsx
50548
+ var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
50549
+ var T_IDENTIFIER2 = 0;
50550
+ var T_KEYWORD2 = 1;
50551
+ var T_STRING2 = 2;
50552
+ var T_CLS_NUMBER2 = 3;
50553
+ var T_PROPERTY2 = 4;
50554
+ var T_ENTITY2 = 5;
50555
+ var T_JSX_LITERAL = 6;
50556
+ var T_SIGN2 = 7;
50557
+ var T_COMMENT2 = 8;
50558
+ var T_BREAK2 = 9;
50559
+ var T_SPACE2 = 10;
50560
+ var JS_LANGS = new Set([
50561
+ "js",
50562
+ "javascript",
50563
+ "jsx",
50564
+ "ts",
50565
+ "typescript",
50566
+ "tsx",
50567
+ "mjs",
50568
+ "cjs"
50569
+ ]);
50570
+ function tokenColor(type) {
50571
+ switch (type) {
50572
+ case T_KEYWORD2:
50573
+ return TOKEN_KEYWORD;
50574
+ case T_STRING2:
50575
+ return TOKEN_STRING;
50576
+ case T_CLS_NUMBER2:
50577
+ return TOKEN_NUMBER;
50578
+ case T_PROPERTY2:
50579
+ return TOKEN_PROPERTY;
50580
+ case T_ENTITY2:
50581
+ return TOKEN_ENTITY;
50582
+ case T_JSX_LITERAL:
50583
+ return TOKEN_TEXT;
50584
+ case T_SIGN2:
50585
+ return TOKEN_MUTED;
50586
+ case T_COMMENT2:
50587
+ return TOKEN_COMMENT;
50588
+ case T_IDENTIFIER2:
50589
+ return TOKEN_TEXT;
50590
+ default:
50591
+ return TOKEN_TEXT;
50592
+ }
50593
+ }
50594
+ var PYTHON_KW = new Set([
50595
+ "def",
50596
+ "class",
50597
+ "import",
50598
+ "from",
50599
+ "return",
50600
+ "if",
50601
+ "elif",
50602
+ "else",
50603
+ "for",
50604
+ "while",
50605
+ "in",
50606
+ "not",
50607
+ "and",
50608
+ "or",
50609
+ "is",
50610
+ "None",
50611
+ "True",
50612
+ "False",
50613
+ "try",
50614
+ "except",
50615
+ "finally",
50616
+ "with",
50617
+ "as",
50618
+ "pass",
50619
+ "break",
50620
+ "continue",
50621
+ "raise",
50622
+ "yield",
50623
+ "lambda",
50624
+ "async",
50625
+ "await",
50626
+ "del",
50627
+ "global",
50628
+ "nonlocal",
50629
+ "assert"
50630
+ ]);
50631
+ var RUST_KW = new Set([
50632
+ "fn",
50633
+ "let",
50634
+ "mut",
50635
+ "const",
50636
+ "struct",
50637
+ "enum",
50638
+ "impl",
50639
+ "trait",
50640
+ "pub",
50641
+ "use",
50642
+ "mod",
50643
+ "match",
50644
+ "if",
50645
+ "else",
50646
+ "loop",
50647
+ "while",
50648
+ "for",
50649
+ "in",
50650
+ "return",
50651
+ "self",
50652
+ "Self",
50653
+ "super",
50654
+ "where",
50655
+ "type",
50656
+ "as",
50657
+ "ref",
50658
+ "move",
50659
+ "unsafe",
50660
+ "extern",
50661
+ "dyn",
50662
+ "async",
50663
+ "await",
50664
+ "true",
50665
+ "false",
50666
+ "Some",
50667
+ "None",
50668
+ "Ok",
50669
+ "Err"
50670
+ ]);
50671
+ var GO_KW = new Set([
50672
+ "func",
50673
+ "var",
50674
+ "const",
50675
+ "type",
50676
+ "struct",
50677
+ "interface",
50678
+ "package",
50679
+ "import",
50680
+ "return",
50681
+ "if",
50682
+ "else",
50683
+ "for",
50684
+ "range",
50685
+ "switch",
50686
+ "case",
50687
+ "default",
50688
+ "break",
50689
+ "continue",
50690
+ "goto",
50691
+ "defer",
50692
+ "go",
50693
+ "chan",
50694
+ "map",
50695
+ "make",
50696
+ "new",
50697
+ "nil",
50698
+ "true",
50699
+ "false",
50700
+ "error"
50701
+ ]);
50702
+ var SHELL_KW = new Set([
50703
+ "if",
50704
+ "then",
50705
+ "else",
50706
+ "elif",
50707
+ "fi",
50708
+ "for",
50709
+ "do",
50710
+ "done",
50711
+ "while",
50712
+ "case",
50713
+ "esac",
50714
+ "in",
50715
+ "function",
50716
+ "return",
50717
+ "echo",
50718
+ "export",
50719
+ "local",
50720
+ "source",
50721
+ "exit"
50722
+ ]);
50723
+ var CSS_AT = /^@[\w-]+/;
50724
+ var CSS_PROP = /^[\w-]+(?=\s*:)/;
50725
+ function tokenizeGeneric(code, lang) {
50726
+ const keywords = lang === "python" || lang === "py" ? PYTHON_KW : lang === "rust" || lang === "rs" ? RUST_KW : lang === "go" ? GO_KW : lang === "bash" || lang === "sh" || lang === "shell" || lang === "zsh" ? SHELL_KW : new Set;
50727
+ const lines = code.split(`
50728
+ `);
50729
+ return lines.map((line) => {
50730
+ const tokens = [];
50731
+ let i = 0;
50732
+ const push = (color, text) => {
50733
+ if (text)
50734
+ tokens.push({ color, text });
50735
+ };
50736
+ while (i < line.length) {
50737
+ const rest2 = line.slice(i);
50738
+ const commentPrefixes = lang === "python" || lang === "py" ? ["#"] : lang === "bash" || lang === "sh" || lang === "shell" || lang === "zsh" ? ["#"] : lang === "css" || lang === "scss" ? ["//", "/*"] : lang === "html" || lang === "xml" ? ["<!--"] : lang === "sql" ? ["--", "#"] : ["//", "#"];
50739
+ let matchedComment = false;
50740
+ for (const prefix of commentPrefixes) {
50741
+ if (rest2.startsWith(prefix)) {
50742
+ push(TOKEN_COMMENT, line.slice(i));
50743
+ i = line.length;
50744
+ matchedComment = true;
50745
+ break;
50746
+ }
50747
+ }
50748
+ if (matchedComment)
50749
+ continue;
50750
+ if (line[i] === '"' || line[i] === "'" || line[i] === "`") {
50751
+ const quote = line[i];
50752
+ let j = i + 1;
50753
+ while (j < line.length) {
50754
+ if (line[j] === "\\") {
50755
+ j += 2;
50756
+ continue;
50757
+ }
50758
+ if (line[j] === quote) {
50759
+ j++;
50760
+ break;
50761
+ }
50762
+ j++;
50763
+ }
50764
+ push(TOKEN_STRING, line.slice(i, j));
50765
+ i = j;
50766
+ continue;
50767
+ }
50768
+ const numMatch = rest2.match(/^-?\d+\.?\d*/);
50769
+ if (numMatch && (i === 0 || !/\w/.test(line[i - 1] ?? ""))) {
50770
+ push(TOKEN_NUMBER, numMatch[0]);
50771
+ i += numMatch[0].length;
50772
+ continue;
50773
+ }
50774
+ if (lang === "css" || lang === "scss") {
50775
+ const atMatch = rest2.match(CSS_AT);
50776
+ if (atMatch) {
50777
+ push(TOKEN_KEYWORD, atMatch[0]);
50778
+ i += atMatch[0].length;
50779
+ continue;
50780
+ }
50781
+ const propMatch = rest2.match(CSS_PROP);
50782
+ if (propMatch) {
50783
+ push(TOKEN_PROPERTY, propMatch[0]);
50784
+ i += propMatch[0].length;
50785
+ continue;
50786
+ }
50787
+ }
50788
+ if ((lang === "html" || lang === "xml") && line[i] === "<") {
50789
+ const tagMatch = rest2.match(/^<\/?[\w:-]+/);
50790
+ if (tagMatch) {
50791
+ push(TOKEN_ENTITY, tagMatch[0]);
50792
+ i += tagMatch[0].length;
50793
+ continue;
50794
+ }
50795
+ }
50796
+ const wordMatch = rest2.match(/^[a-zA-Z_$][\w$]*/);
50797
+ if (wordMatch) {
50798
+ const word = wordMatch[0];
50799
+ push(keywords.has(word) ? TOKEN_KEYWORD : TOKEN_TEXT, word);
50800
+ i += word.length;
50801
+ continue;
50802
+ }
50803
+ const opMatch = rest2.match(/^[^\w\s"'`]+/);
50804
+ if (opMatch) {
50805
+ push(TOKEN_MUTED, opMatch[0]);
50806
+ i += opMatch[0].length;
50807
+ continue;
50808
+ }
50809
+ push(TOKEN_TEXT, line[i]);
50810
+ i++;
50811
+ }
50812
+ return tokens;
50813
+ });
49255
50814
  }
49256
- function readFile(filePath, repoPath) {
49257
- const candidates = path14.isAbsolute(filePath) ? [filePath] : [filePath, path14.join(repoPath, filePath)];
49258
- for (const candidate of candidates) {
49259
- if (existsSync10(candidate)) {
49260
- try {
49261
- const content = readFileSync10(candidate, "utf-8");
49262
- const lines = content.split(`
49263
- `).length;
49264
- return `File: ${candidate} (${lines} lines)
49265
-
49266
- ${content.slice(0, 8000)}${content.length > 8000 ? `
49267
-
49268
- (truncated)` : ""}`;
49269
- } catch (err) {
49270
- return `Error reading file: ${err instanceof Error ? err.message : String(err)}`;
50815
+ function HighlightedLine({ tokens }) {
50816
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50817
+ children: [
50818
+ " ",
50819
+ tokens.map((t, i) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50820
+ color: t.color,
50821
+ children: t.text
50822
+ }, i, false, undefined, this))
50823
+ ]
50824
+ }, undefined, true, undefined, this);
50825
+ }
50826
+ function CodeBlock({ lang, code }) {
50827
+ const normalizedLang = lang.toLowerCase().trim();
50828
+ let lines;
50829
+ if (JS_LANGS.has(normalizedLang)) {
50830
+ const tokens = tokenize3(code);
50831
+ const lineAccum = [[]];
50832
+ for (const [type, value] of tokens) {
50833
+ if (type === T_BREAK2) {
50834
+ lineAccum.push([]);
50835
+ } else if (type !== T_SPACE2) {
50836
+ lineAccum[lineAccum.length - 1].push({
50837
+ color: tokenColor(type),
50838
+ text: value
50839
+ });
50840
+ } else {
50841
+ lineAccum[lineAccum.length - 1].push({
50842
+ color: TOKEN_TEXT,
50843
+ text: value
50844
+ });
49271
50845
  }
49272
50846
  }
50847
+ lines = lineAccum;
50848
+ } else if (normalizedLang) {
50849
+ lines = tokenizeGeneric(code, normalizedLang);
50850
+ } else {
50851
+ lines = code.split(`
50852
+ `).map((l) => [{ color: TOKEN_TEXT, text: l }]);
49273
50853
  }
49274
- return `File not found: ${filePath}. If reading from a cloned repo, use the full absolute path e.g. C:\\Users\\...\\repo\\file.ts`;
49275
- }
49276
- function writeFile(filePath, content, repoPath) {
49277
- const fullPath = path14.isAbsolute(filePath) ? filePath : path14.join(repoPath, filePath);
49278
- try {
49279
- const dir = path14.dirname(fullPath);
49280
- if (!existsSync10(dir))
49281
- mkdirSync4(dir, { recursive: true });
49282
- writeFileSync6(fullPath, content, "utf-8");
49283
- const lines = content.split(`
49284
- `).length;
49285
- return `Written: ${fullPath} (${lines} lines, ${content.length} bytes)`;
49286
- } catch (err) {
49287
- return `Error writing file: ${err instanceof Error ? err.message : String(err)}`;
49288
- }
50854
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
50855
+ flexDirection: "column",
50856
+ marginY: 1,
50857
+ marginLeft: 2,
50858
+ children: [
50859
+ normalizedLang ? /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50860
+ color: TOKEN_MUTED,
50861
+ dimColor: true,
50862
+ children: normalizedLang
50863
+ }, undefined, false, undefined, this) : null,
50864
+ lines.map((lineTokens, i) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(HighlightedLine, {
50865
+ tokens: lineTokens
50866
+ }, i, false, undefined, this))
50867
+ ]
50868
+ }, undefined, true, undefined, this);
49289
50869
  }
49290
-
49291
- // src/components/chat/ChatMessage.tsx
49292
- var jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
49293
50870
  function InlineText({ text }) {
49294
- const parts = text.split(/(`[^`]+`|\*\*[^*]+\*\*)/g);
50871
+ const parts = text.split(/(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g);
49295
50872
  return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(jsx_dev_runtime19.Fragment, {
49296
50873
  children: parts.map((part, i) => {
49297
- if (part.startsWith("`") && part.endsWith("`")) {
50874
+ if (part.startsWith("**") && part.endsWith("**")) {
49298
50875
  return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49299
- color: ACCENT,
50876
+ bold: true,
50877
+ color: TOKEN_TEXT,
50878
+ children: part.slice(2, -2)
50879
+ }, i, false, undefined, this);
50880
+ }
50881
+ if (part.startsWith("*") && part.endsWith("*") && part.length > 2) {
50882
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50883
+ italic: true,
50884
+ color: TOKEN_TEXT,
49300
50885
  children: part.slice(1, -1)
49301
50886
  }, i, false, undefined, this);
49302
50887
  }
49303
- if (part.startsWith("**") && part.endsWith("**")) {
50888
+ if (part.startsWith("`") && part.endsWith("`")) {
49304
50889
  return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49305
- bold: true,
49306
- color: "white",
49307
- children: part.slice(2, -2)
50890
+ color: ACCENT,
50891
+ children: part.slice(1, -1)
49308
50892
  }, i, false, undefined, this);
49309
50893
  }
49310
50894
  return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49311
- color: "white",
50895
+ color: TOKEN_TEXT,
49312
50896
  children: part
49313
50897
  }, i, false, undefined, this);
49314
50898
  })
49315
50899
  }, undefined, false, undefined, this);
49316
50900
  }
49317
- function CodeBlock({ lang, code }) {
50901
+ function Heading({ level, text }) {
50902
+ if (level === 1) {
50903
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
50904
+ marginTop: 1,
50905
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50906
+ color: ACCENT,
50907
+ bold: true,
50908
+ underline: true,
50909
+ children: text
50910
+ }, undefined, false, undefined, this)
50911
+ }, undefined, false, undefined, this);
50912
+ }
50913
+ if (level === 2) {
50914
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
50915
+ marginTop: 1,
50916
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50917
+ color: ACCENT,
50918
+ bold: true,
50919
+ children: text
50920
+ }, undefined, false, undefined, this)
50921
+ }, undefined, false, undefined, this);
50922
+ }
49318
50923
  return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
49319
- flexDirection: "column",
49320
- marginY: 1,
49321
- marginLeft: 2,
50924
+ marginTop: 1,
50925
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50926
+ color: TOKEN_TEXT,
50927
+ bold: true,
50928
+ children: text
50929
+ }, undefined, false, undefined, this)
50930
+ }, undefined, false, undefined, this);
50931
+ }
50932
+ function BulletItem({ text }) {
50933
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
50934
+ gap: 1,
49322
50935
  children: [
49323
- lang && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49324
- color: "gray",
49325
- children: lang
49326
- }, undefined, false, undefined, this),
49327
- code.split(`
49328
- `).map((line, i) => /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50936
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49329
50937
  color: ACCENT,
50938
+ children: "*"
50939
+ }, undefined, false, undefined, this),
50940
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
50941
+ flexShrink: 1,
50942
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InlineText, {
50943
+ text
50944
+ }, undefined, false, undefined, this)
50945
+ }, undefined, false, undefined, this)
50946
+ ]
50947
+ }, undefined, true, undefined, this);
50948
+ }
50949
+ function NumberedItem({ num, text }) {
50950
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
50951
+ gap: 1,
50952
+ children: [
50953
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50954
+ color: TOKEN_MUTED,
49330
50955
  children: [
49331
- " ",
49332
- line
50956
+ num,
50957
+ "."
49333
50958
  ]
49334
- }, i, true, undefined, this))
50959
+ }, undefined, true, undefined, this),
50960
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
50961
+ flexShrink: 1,
50962
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InlineText, {
50963
+ text
50964
+ }, undefined, false, undefined, this)
50965
+ }, undefined, false, undefined, this)
49335
50966
  ]
49336
50967
  }, undefined, true, undefined, this);
49337
50968
  }
49338
- function MessageBody({ content }) {
49339
- const segments = content.split(/(```[\s\S]*?```)/g);
50969
+ function BlockQuote({ text }) {
49340
50970
  return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
49341
- flexDirection: "column",
49342
- children: segments.map((seg, si) => {
49343
- if (seg.startsWith("```")) {
49344
- const lines2 = seg.slice(3).split(`
50971
+ gap: 1,
50972
+ marginLeft: 1,
50973
+ children: [
50974
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50975
+ color: TOKEN_MUTED,
50976
+ children: "│"
50977
+ }, undefined, false, undefined, this),
50978
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
50979
+ color: TOKEN_MUTED,
50980
+ dimColor: true,
50981
+ children: text
50982
+ }, undefined, false, undefined, this)
50983
+ ]
50984
+ }, undefined, true, undefined, this);
50985
+ }
50986
+ function parseBlocks(content) {
50987
+ const blocks = [];
50988
+ const segments = content.split(/(```[\s\S]*?```)/g);
50989
+ for (const seg of segments) {
50990
+ if (seg.startsWith("```")) {
50991
+ const lines = seg.slice(3).split(`
49345
50992
  `);
49346
- const lang = lines2[0]?.trim() ?? "";
49347
- const code = lines2.slice(1).join(`
50993
+ const lang = lines[0]?.trim() ?? "";
50994
+ const code = lines.slice(1).join(`
49348
50995
  `).replace(/```\s*$/, "").trimEnd();
49349
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(CodeBlock, {
49350
- lang,
49351
- code
49352
- }, si, false, undefined, this);
49353
- }
49354
- const lines = seg.split(`
49355
- `).filter((l) => l.trim() !== "");
49356
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
49357
- flexDirection: "column",
49358
- children: lines.map((line, li) => {
49359
- if (line.match(/^[-*•]\s/)) {
49360
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
49361
- gap: 1,
49362
- children: [
49363
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49364
- color: ACCENT,
49365
- children: "*"
49366
- }, undefined, false, undefined, this),
49367
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InlineText, {
49368
- text: line.slice(2).trim()
49369
- }, undefined, false, undefined, this)
49370
- ]
49371
- }, li, true, undefined, this);
49372
- }
49373
- if (line.match(/^\d+\.\s/)) {
49374
- const num = line.match(/^(\d+)\.\s/)[1];
49375
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
49376
- gap: 1,
49377
- children: [
49378
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49379
- color: "gray",
49380
- children: [
49381
- num,
49382
- "."
49383
- ]
49384
- }, undefined, true, undefined, this),
49385
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InlineText, {
49386
- text: line.replace(/^\d+\.\s/, "").trim()
49387
- }, undefined, false, undefined, this)
49388
- ]
49389
- }, li, true, undefined, this);
49390
- }
50996
+ blocks.push({ type: "code", lang, code });
50997
+ continue;
50998
+ }
50999
+ for (const line of seg.split(`
51000
+ `)) {
51001
+ const trimmed = line.trim();
51002
+ if (!trimmed)
51003
+ continue;
51004
+ const h3 = trimmed.match(/^### (.+)$/);
51005
+ const h2 = trimmed.match(/^## (.+)$/);
51006
+ const h1 = trimmed.match(/^# (.+)$/);
51007
+ if (h3) {
51008
+ blocks.push({ type: "heading", level: 3, text: h3[1] });
51009
+ continue;
51010
+ }
51011
+ if (h2) {
51012
+ blocks.push({ type: "heading", level: 2, text: h2[1] });
51013
+ continue;
51014
+ }
51015
+ if (h1) {
51016
+ blocks.push({ type: "heading", level: 1, text: h1[1] });
51017
+ continue;
51018
+ }
51019
+ if (/^[-*_]{3,}$/.test(trimmed)) {
51020
+ blocks.push({ type: "hr" });
51021
+ continue;
51022
+ }
51023
+ if (trimmed.startsWith("> ")) {
51024
+ blocks.push({ type: "blockquote", text: trimmed.slice(2).trim() });
51025
+ continue;
51026
+ }
51027
+ if (/^[-*•]\s/.test(trimmed)) {
51028
+ blocks.push({ type: "bullet", text: trimmed.slice(2).trim() });
51029
+ continue;
51030
+ }
51031
+ const numMatch = trimmed.match(/^(\d+)\.\s(.+)/);
51032
+ if (numMatch) {
51033
+ blocks.push({
51034
+ type: "numbered",
51035
+ num: numMatch[1],
51036
+ text: numMatch[2]
51037
+ });
51038
+ continue;
51039
+ }
51040
+ blocks.push({ type: "paragraph", text: trimmed });
51041
+ }
51042
+ }
51043
+ return blocks;
51044
+ }
51045
+ function MessageBody({ content }) {
51046
+ const blocks = parseBlocks(content);
51047
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
51048
+ flexDirection: "column",
51049
+ children: blocks.map((block, i) => {
51050
+ switch (block.type) {
51051
+ case "heading":
51052
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Heading, {
51053
+ level: block.level,
51054
+ text: block.text
51055
+ }, i, false, undefined, this);
51056
+ case "code":
51057
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(CodeBlock, {
51058
+ lang: block.lang,
51059
+ code: block.code
51060
+ }, i, false, undefined, this);
51061
+ case "bullet":
51062
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(BulletItem, {
51063
+ text: block.text
51064
+ }, i, false, undefined, this);
51065
+ case "numbered":
51066
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(NumberedItem, {
51067
+ num: block.num,
51068
+ text: block.text
51069
+ }, i, false, undefined, this);
51070
+ case "blockquote":
51071
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(BlockQuote, {
51072
+ text: block.text
51073
+ }, i, false, undefined, this);
51074
+ case "hr":
51075
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
51076
+ marginY: 1,
51077
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
51078
+ color: TOKEN_MUTED,
51079
+ dimColor: true,
51080
+ children: "─".repeat(40)
51081
+ }, undefined, false, undefined, this)
51082
+ }, i, false, undefined, this);
51083
+ case "paragraph":
49391
51084
  return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
49392
51085
  children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InlineText, {
49393
- text: line
51086
+ text: block.text
49394
51087
  }, undefined, false, undefined, this)
49395
- }, li, false, undefined, this);
49396
- })
49397
- }, si, false, undefined, this);
51088
+ }, i, false, undefined, this);
51089
+ }
49398
51090
  })
49399
51091
  }, undefined, false, undefined, this);
49400
51092
  }
@@ -49405,11 +51097,11 @@ function StaticMessage({ msg }) {
49405
51097
  gap: 1,
49406
51098
  children: [
49407
51099
  /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49408
- color: "gray",
51100
+ color: TOKEN_MUTED,
49409
51101
  children: ">"
49410
51102
  }, undefined, false, undefined, this),
49411
51103
  /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49412
- color: "white",
51104
+ color: TOKEN_TEXT,
49413
51105
  bold: true,
49414
51106
  children: msg.content
49415
51107
  }, undefined, false, undefined, this)
@@ -49421,6 +51113,12 @@ function StaticMessage({ msg }) {
49421
51113
  shell: "$",
49422
51114
  fetch: "~>",
49423
51115
  "read-file": "r",
51116
+ "read-folder": "d",
51117
+ grep: "/",
51118
+ "delete-file": "x",
51119
+ "delete-folder": "X",
51120
+ "open-url": "↗",
51121
+ "generate-pdf": "P",
49424
51122
  "write-file": "w",
49425
51123
  search: "?"
49426
51124
  };
@@ -49438,7 +51136,7 @@ function StaticMessage({ msg }) {
49438
51136
  children: icon
49439
51137
  }, undefined, false, undefined, this),
49440
51138
  /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49441
- color: msg.approved ? "gray" : "red",
51139
+ color: msg.approved ? TOKEN_MUTED : "red",
49442
51140
  dimColor: !msg.approved,
49443
51141
  children: label
49444
51142
  }, undefined, false, undefined, this),
@@ -49451,7 +51149,7 @@ function StaticMessage({ msg }) {
49451
51149
  msg.approved && msg.result && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
49452
51150
  marginLeft: 2,
49453
51151
  children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49454
- color: "gray",
51152
+ color: TOKEN_MUTED,
49455
51153
  children: [
49456
51154
  msg.result.split(`
49457
51155
  `)[0]?.slice(0, 120),
@@ -49485,11 +51183,11 @@ function StaticMessage({ msg }) {
49485
51183
  gap: 1,
49486
51184
  children: [
49487
51185
  /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49488
- color: msg.applied ? "green" : "gray",
51186
+ color: msg.applied ? "green" : TOKEN_MUTED,
49489
51187
  children: msg.applied ? "✓" : "·"
49490
51188
  }, undefined, false, undefined, this),
49491
51189
  /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
49492
- color: msg.applied ? "green" : "gray",
51190
+ color: msg.applied ? "green" : TOKEN_MUTED,
49493
51191
  dimColor: !msg.applied,
49494
51192
  children: msg.applied ? "changes applied" : "changes skipped"
49495
51193
  }, undefined, false, undefined, this)
@@ -49642,6 +51340,30 @@ function PermissionPrompt({
49642
51340
  icon = "r";
49643
51341
  label = "read";
49644
51342
  value = tool.filePath;
51343
+ } else if (tool.type === "read-folder") {
51344
+ icon = "d";
51345
+ label = "folder";
51346
+ value = tool.folderPath;
51347
+ } else if (tool.type === "grep") {
51348
+ icon = "/";
51349
+ label = "grep";
51350
+ value = `${tool.pattern} ${tool.glob}`;
51351
+ } else if (tool.type === "delete-file") {
51352
+ icon = "x";
51353
+ label = "delete";
51354
+ value = tool.filePath;
51355
+ } else if (tool.type === "delete-folder") {
51356
+ icon = "X";
51357
+ label = "delete folder";
51358
+ value = tool.folderPath;
51359
+ } else if (tool.type === "open-url") {
51360
+ icon = "↗";
51361
+ label = "open";
51362
+ value = tool.url;
51363
+ } else if (tool.type === "generate-pdf") {
51364
+ icon = "P";
51365
+ label = "pdf";
51366
+ value = tool.filePath;
49645
51367
  } else if (tool.type === "write-file") {
49646
51368
  icon = "w";
49647
51369
  label = "write";
@@ -49734,16 +51456,23 @@ function TypewriterText({
49734
51456
  children: displayed
49735
51457
  }, undefined, false, undefined, this);
49736
51458
  }
49737
- function ShortcutBar() {
51459
+ function ShortcutBar({ autoApprove }) {
49738
51460
  return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
49739
51461
  gap: 3,
49740
51462
  marginTop: 0,
49741
- children: /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
49742
- color: "gray",
49743
- dimColor: true,
49744
- children: "enter send · ^v paste · ^c exit"
49745
- }, undefined, false, undefined, this)
49746
- }, undefined, false, undefined, this);
51463
+ children: [
51464
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
51465
+ color: "gray",
51466
+ dimColor: true,
51467
+ children: "enter send · ^v paste · ^c exit"
51468
+ }, undefined, false, undefined, this),
51469
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
51470
+ color: autoApprove ? "green" : "gray",
51471
+ dimColor: !autoApprove,
51472
+ children: autoApprove ? "⚡ auto" : "/auto"
51473
+ }, undefined, false, undefined, this)
51474
+ ]
51475
+ }, undefined, true, undefined, this);
49747
51476
  }
49748
51477
  function CloneOfferView({
49749
51478
  stage,
@@ -51519,7 +53248,8 @@ var jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
51519
53248
  var COMMANDS = [
51520
53249
  { cmd: "/timeline", desc: "browse commit history" },
51521
53250
  { cmd: "/clear history", desc: "wipe session memory for this repo" },
51522
- { cmd: "/review", desc: "review current codebsae" }
53251
+ { cmd: "/review", desc: "review current codebsae" },
53252
+ { cmd: "/auto", desc: "toggle auto-approve for read/search tools" }
51523
53253
  ];
51524
53254
  function CommandPalette({
51525
53255
  query,
@@ -51564,6 +53294,9 @@ var ChatRunner = ({ repoPath }) => {
51564
53294
  const [clonedUrls, setClonedUrls] = import_react48.useState(new Set);
51565
53295
  const [showTimeline, setShowTimeline] = import_react48.useState(false);
51566
53296
  const [showReview, setShowReview] = import_react48.useState(false);
53297
+ const [autoApprove, setAutoApprove] = import_react48.useState(false);
53298
+ const abortControllerRef = import_react48.useRef(null);
53299
+ const toolResultCache = import_react48.useRef(new Map);
51567
53300
  const inputBuffer = import_react48.useRef("");
51568
53301
  const flushTimer = import_react48.useRef(null);
51569
53302
  const thinkingPhrase = useThinkingPhrase(stage.type === "thinking");
@@ -51583,6 +53316,10 @@ var ChatRunner = ({ repoPath }) => {
51583
53316
  }, 16);
51584
53317
  };
51585
53318
  const handleError = (currentAll) => (err) => {
53319
+ if (err instanceof Error && err.name === "AbortError") {
53320
+ setStage({ type: "idle" });
53321
+ return;
53322
+ }
51586
53323
  const errMsg = {
51587
53324
  role: "assistant",
51588
53325
  content: `Error: ${err instanceof Error ? err.message : "Something went wrong"}`,
@@ -51592,7 +53329,11 @@ var ChatRunner = ({ repoPath }) => {
51592
53329
  setCommitted((prev) => [...prev, errMsg]);
51593
53330
  setStage({ type: "idle" });
51594
53331
  };
51595
- const processResponse = (raw, currentAll) => {
53332
+ const processResponse = (raw, currentAll, signal) => {
53333
+ if (signal.aborted) {
53334
+ setStage({ type: "idle" });
53335
+ return;
53336
+ }
51596
53337
  const parsed = parseResponse(raw);
51597
53338
  if (parsed.kind === "changes") {
51598
53339
  if (parsed.patches.length === 0) {
@@ -51626,7 +53367,7 @@ var ChatRunner = ({ repoPath }) => {
51626
53367
  });
51627
53368
  return;
51628
53369
  }
51629
- if (parsed.kind === "shell" || parsed.kind === "fetch" || parsed.kind === "read-file" || parsed.kind === "write-file" || parsed.kind === "search") {
53370
+ if (parsed.kind === "shell" || parsed.kind === "fetch" || parsed.kind === "read-file" || parsed.kind === "read-folder" || parsed.kind === "grep" || parsed.kind === "write-file" || parsed.kind === "delete-file" || parsed.kind === "delete-folder" || parsed.kind === "open-url" || parsed.kind === "generate-pdf" || parsed.kind === "search") {
51630
53371
  let tool;
51631
53372
  if (parsed.kind === "shell") {
51632
53373
  tool = { type: "shell", command: parsed.command };
@@ -51634,6 +53375,22 @@ var ChatRunner = ({ repoPath }) => {
51634
53375
  tool = { type: "fetch", url: parsed.url };
51635
53376
  } else if (parsed.kind === "read-file") {
51636
53377
  tool = { type: "read-file", filePath: parsed.filePath };
53378
+ } else if (parsed.kind === "read-folder") {
53379
+ tool = { type: "read-folder", folderPath: parsed.folderPath };
53380
+ } else if (parsed.kind === "grep") {
53381
+ tool = { type: "grep", pattern: parsed.pattern, glob: parsed.glob };
53382
+ } else if (parsed.kind === "delete-file") {
53383
+ tool = { type: "delete-file", filePath: parsed.filePath };
53384
+ } else if (parsed.kind === "delete-folder") {
53385
+ tool = { type: "delete-folder", folderPath: parsed.folderPath };
53386
+ } else if (parsed.kind === "open-url") {
53387
+ tool = { type: "open-url", url: parsed.url };
53388
+ } else if (parsed.kind === "generate-pdf") {
53389
+ tool = {
53390
+ type: "generate-pdf",
53391
+ filePath: parsed.filePath,
53392
+ content: parsed.pdfContent
53393
+ };
51637
53394
  } else if (parsed.kind === "search") {
51638
53395
  tool = { type: "search", query: parsed.query };
51639
53396
  } else {
@@ -51652,13 +53409,16 @@ var ChatRunner = ({ repoPath }) => {
51652
53409
  setAllMessages([...currentAll, preambleMsg]);
51653
53410
  setCommitted((prev) => [...prev, preambleMsg]);
51654
53411
  }
51655
- setStage({
51656
- type: "permission",
51657
- tool,
51658
- pendingMessages: currentAll,
51659
- resolve: async (approved) => {
51660
- let result2 = "(denied by user)";
51661
- if (approved) {
53412
+ const isSafeTool = parsed.kind === "read-file" || parsed.kind === "read-folder" || parsed.kind === "grep" || parsed.kind === "fetch" || parsed.kind === "open-url" || parsed.kind === "search";
53413
+ const executeAndContinue = async (approved) => {
53414
+ let result2 = "(denied by user)";
53415
+ if (approved) {
53416
+ const cacheKey = parsed.kind === "read-file" ? `read-file:${parsed.filePath}` : parsed.kind === "read-folder" ? `read-folder:${parsed.folderPath}` : parsed.kind === "grep" ? `grep:${parsed.pattern}:${parsed.glob}` : null;
53417
+ if (cacheKey && toolResultCache.current.has(cacheKey)) {
53418
+ result2 = toolResultCache.current.get(cacheKey) + `
53419
+
53420
+ [NOTE: This result was already retrieved earlier. Do not request it again.]`;
53421
+ } else {
51662
53422
  try {
51663
53423
  setStage({ type: "thinking" });
51664
53424
  if (parsed.kind === "shell") {
@@ -51667,47 +53427,80 @@ var ChatRunner = ({ repoPath }) => {
51667
53427
  result2 = await fetchUrl(parsed.url);
51668
53428
  } else if (parsed.kind === "read-file") {
51669
53429
  result2 = readFile(parsed.filePath, repoPath);
53430
+ } else if (parsed.kind === "read-folder") {
53431
+ result2 = readFolder(parsed.folderPath, repoPath);
53432
+ } else if (parsed.kind === "grep") {
53433
+ result2 = grepFiles(parsed.pattern, parsed.glob, repoPath);
53434
+ } else if (parsed.kind === "delete-file") {
53435
+ result2 = deleteFile(parsed.filePath, repoPath);
53436
+ } else if (parsed.kind === "delete-folder") {
53437
+ result2 = deleteFolder(parsed.folderPath, repoPath);
53438
+ } else if (parsed.kind === "open-url") {
53439
+ result2 = openUrl(parsed.url);
53440
+ } else if (parsed.kind === "generate-pdf") {
53441
+ result2 = generatePdf(parsed.filePath, parsed.pdfContent, repoPath);
51670
53442
  } else if (parsed.kind === "write-file") {
51671
53443
  result2 = writeFile(parsed.filePath, parsed.fileContent, repoPath);
51672
53444
  } else if (parsed.kind === "search") {
51673
53445
  result2 = await searchWeb(parsed.query);
51674
53446
  }
53447
+ if (cacheKey) {
53448
+ toolResultCache.current.set(cacheKey, result2);
53449
+ }
51675
53450
  } catch (err) {
51676
53451
  result2 = `Error: ${err instanceof Error ? err.message : "failed"}`;
51677
53452
  }
51678
53453
  }
51679
- if (approved && !result2.startsWith("Error:")) {
51680
- const kindMap2 = {
51681
- shell: "shell-run",
51682
- fetch: "url-fetched",
51683
- "read-file": "file-read",
51684
- "write-file": "file-written",
51685
- search: "url-fetched"
51686
- };
51687
- appendHistory({
51688
- kind: kindMap2[parsed.kind] ?? "shell-run",
51689
- detail: parsed.kind === "shell" ? parsed.command : parsed.kind === "fetch" ? parsed.url : parsed.kind === "search" ? parsed.query : parsed.filePath,
51690
- summary: result2.split(`
51691
- `)[0]?.slice(0, 120) ?? "",
51692
- repoPath
51693
- });
51694
- }
51695
- const toolName = parsed.kind === "shell" ? "shell" : parsed.kind === "fetch" ? "fetch" : parsed.kind === "read-file" ? "read-file" : parsed.kind === "search" ? "search" : "write-file";
51696
- const toolContent = parsed.kind === "shell" ? parsed.command : parsed.kind === "fetch" ? parsed.url : parsed.kind === "search" ? parsed.query : parsed.filePath;
51697
- const toolMsg = {
51698
- role: "assistant",
51699
- type: "tool",
51700
- toolName,
51701
- content: toolContent,
51702
- result: result2,
51703
- approved
53454
+ }
53455
+ if (approved && !result2.startsWith("Error:")) {
53456
+ const kindMap2 = {
53457
+ shell: "shell-run",
53458
+ fetch: "url-fetched",
53459
+ "read-file": "file-read",
53460
+ "read-folder": "file-read",
53461
+ grep: "file-read",
53462
+ "delete-file": "file-written",
53463
+ "delete-folder": "file-written",
53464
+ "open-url": "url-fetched",
53465
+ "generate-pdf": "file-written",
53466
+ "write-file": "file-written",
53467
+ search: "url-fetched"
51704
53468
  };
51705
- const withTool = [...currentAll, toolMsg];
51706
- setAllMessages(withTool);
51707
- setCommitted((prev) => [...prev, toolMsg]);
51708
- setStage({ type: "thinking" });
51709
- callChat(provider, systemPrompt, withTool).then((r) => processResponse(r, withTool)).catch(handleError(withTool));
53469
+ appendHistory({
53470
+ kind: kindMap2[parsed.kind] ?? "shell-run",
53471
+ detail: parsed.kind === "shell" ? parsed.command : parsed.kind === "fetch" ? parsed.url : parsed.kind === "search" ? parsed.query : parsed.kind === "read-folder" ? parsed.folderPath : parsed.kind === "grep" ? `${parsed.pattern} ${parsed.glob}` : parsed.kind === "delete-file" ? parsed.filePath : parsed.kind === "delete-folder" ? parsed.folderPath : parsed.kind === "open-url" ? parsed.url : parsed.kind === "generate-pdf" ? parsed.filePath : parsed.filePath,
53472
+ summary: result2.split(`
53473
+ `)[0]?.slice(0, 120) ?? "",
53474
+ repoPath
53475
+ });
51710
53476
  }
53477
+ const toolName = parsed.kind === "shell" ? "shell" : parsed.kind === "fetch" ? "fetch" : parsed.kind === "read-file" ? "read-file" : parsed.kind === "read-folder" ? "read-folder" : parsed.kind === "grep" ? "grep" : parsed.kind === "delete-file" ? "delete-file" : parsed.kind === "delete-folder" ? "delete-folder" : parsed.kind === "open-url" ? "open-url" : parsed.kind === "generate-pdf" ? "generate-pdf" : parsed.kind === "search" ? "search" : "write-file";
53478
+ const toolContent = parsed.kind === "shell" ? parsed.command : parsed.kind === "fetch" ? parsed.url : parsed.kind === "search" ? parsed.query : parsed.kind === "read-folder" ? parsed.folderPath : parsed.kind === "grep" ? `${parsed.pattern} — ${parsed.glob}` : parsed.kind === "delete-file" ? parsed.filePath : parsed.kind === "delete-folder" ? parsed.folderPath : parsed.kind === "open-url" ? parsed.url : parsed.kind === "generate-pdf" ? parsed.filePath : parsed.filePath;
53479
+ const toolMsg = {
53480
+ role: "assistant",
53481
+ type: "tool",
53482
+ toolName,
53483
+ content: toolContent,
53484
+ result: result2,
53485
+ approved
53486
+ };
53487
+ const withTool = [...currentAll, toolMsg];
53488
+ setAllMessages(withTool);
53489
+ setCommitted((prev) => [...prev, toolMsg]);
53490
+ const nextAbort = new AbortController;
53491
+ abortControllerRef.current = nextAbort;
53492
+ setStage({ type: "thinking" });
53493
+ callChat(provider, systemPrompt, withTool, nextAbort.signal).then((r) => processResponse(r, withTool, nextAbort.signal)).catch(handleError(withTool));
53494
+ };
53495
+ if (autoApprove && isSafeTool) {
53496
+ executeAndContinue(true);
53497
+ return;
53498
+ }
53499
+ setStage({
53500
+ type: "permission",
53501
+ tool,
53502
+ pendingMessages: currentAll,
53503
+ resolve: executeAndContinue
51711
53504
  });
51712
53505
  return;
51713
53506
  }
@@ -51757,6 +53550,18 @@ var ChatRunner = ({ repoPath }) => {
51757
53550
  setShowReview(true);
51758
53551
  return;
51759
53552
  }
53553
+ if (text.trim().toLowerCase() === "/auto") {
53554
+ const next = !autoApprove;
53555
+ setAutoApprove(next);
53556
+ const msg = {
53557
+ role: "assistant",
53558
+ content: next ? "Auto-approve ON — read, search, grep and folder tools will run without asking. Write and code changes still require approval." : "Auto-approve OFF — all tools will ask for permission.",
53559
+ type: "text"
53560
+ };
53561
+ setCommitted((prev) => [...prev, msg]);
53562
+ setAllMessages((prev) => [...prev, msg]);
53563
+ return;
53564
+ }
51760
53565
  if (text.trim().toLowerCase() === "/clear history") {
51761
53566
  clearRepoHistory(repoPath);
51762
53567
  const clearedMsg = {
@@ -51772,12 +53577,21 @@ var ChatRunner = ({ repoPath }) => {
51772
53577
  const nextAll = [...allMessages, userMsg];
51773
53578
  setCommitted((prev) => [...prev, userMsg]);
51774
53579
  setAllMessages(nextAll);
53580
+ toolResultCache.current.clear();
53581
+ const abort = new AbortController;
53582
+ abortControllerRef.current = abort;
51775
53583
  setStage({ type: "thinking" });
51776
- callChat(provider, systemPrompt, nextAll).then((raw) => processResponse(raw, nextAll)).catch(handleError(nextAll));
53584
+ callChat(provider, systemPrompt, nextAll, abort.signal).then((raw) => processResponse(raw, nextAll, abort.signal)).catch(handleError(nextAll));
51777
53585
  };
51778
53586
  use_input_default((input, key) => {
51779
53587
  if (showTimeline)
51780
53588
  return;
53589
+ if (stage.type === "thinking" && key.escape) {
53590
+ abortControllerRef.current?.abort();
53591
+ abortControllerRef.current = null;
53592
+ setStage({ type: "idle" });
53593
+ return;
53594
+ }
51781
53595
  if (stage.type === "idle") {
51782
53596
  if (key.ctrl && input === "c") {
51783
53597
  process.exit(0);
@@ -52109,6 +53923,11 @@ Tip: type /timeline to browse commit history.`,
52109
53923
  }, undefined, false, undefined, this),
52110
53924
  /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TypewriterText, {
52111
53925
  text: thinkingPhrase
53926
+ }, undefined, false, undefined, this),
53927
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
53928
+ color: "gray",
53929
+ dimColor: true,
53930
+ children: "· esc cancel"
52112
53931
  }, undefined, false, undefined, this)
52113
53932
  ]
52114
53933
  }, undefined, true, undefined, this),
@@ -52134,7 +53953,9 @@ Tip: type /timeline to browse commit history.`,
52134
53953
  setInputValue("");
52135
53954
  }
52136
53955
  }, undefined, false, undefined, this),
52137
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ShortcutBar, {}, undefined, false, undefined, this)
53956
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ShortcutBar, {
53957
+ autoApprove
53958
+ }, undefined, false, undefined, this)
52138
53959
  ]
52139
53960
  }, undefined, true, undefined, this)
52140
53961
  ]