@p4code/cli 0.1.6 → 0.1.7

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.
Files changed (86) hide show
  1. package/dist/bin.mjs +334 -188
  2. package/dist/client/assets/{DiffPanel-CcuhH24b.js → DiffPanel-B_RUbYdJ.js} +2 -2
  3. package/dist/client/assets/{FilePreviewPanel-IupIzTGt.js → FilePreviewPanel-D_TE6anL.js} +2 -2
  4. package/dist/client/assets/{PreviewPanel-CMRKi8Jr.js → PreviewPanel-Dx5mB3w6.js} +2 -2
  5. package/dist/client/assets/{angular-html-D4VEGoU_.js → angular-html-Z6GyEAg_.js} +2 -2
  6. package/dist/client/assets/{angular-ts-DDbN_Pcg.js → angular-ts-BdawRcND.js} +2 -2
  7. package/dist/client/assets/{apl-BH7XGSiX.js → apl-0Nzwg3c9.js} +2 -2
  8. package/dist/client/assets/arrow-right-BVDRuCFE.js +2 -0
  9. package/dist/client/assets/{astro-DKjLEfvq.js → astro-iJpYXO_m.js} +2 -2
  10. package/dist/client/assets/{blade-mO6atsi4.js → blade-BeMtIYzp.js} +2 -2
  11. package/dist/client/assets/{c-drphy09y.js → c-n5Rj77uj.js} +2 -2
  12. package/dist/client/assets/{cobol-Do365H-h.js → cobol-CfsSyjzP.js} +2 -2
  13. package/dist/client/assets/{coffee-CJ9y15YY.js → coffee-LZB1mvnz.js} +2 -2
  14. package/dist/client/assets/{cpp-C1FVZ39w.js → cpp-C689OQY_.js} +2 -2
  15. package/dist/client/assets/{crystal-D1Y9mCOy.js → crystal-CC7u-hEs.js} +2 -2
  16. package/dist/client/assets/{css-C1chQpfJ.js → css-eVqGM3Yx.js} +2 -2
  17. package/dist/client/assets/{edge-Cir90YUi.js → edge-CvU3NO69.js} +2 -2
  18. package/dist/client/assets/{elixir-D8U_xdUO.js → elixir-gblyDHJ2.js} +2 -2
  19. package/dist/client/assets/{elm-DFyeiMH7.js → elm-BEDREZ-r.js} +2 -2
  20. package/dist/client/assets/{erb-C6TvSrSi.js → erb-C_pVTICR.js} +2 -2
  21. package/dist/client/assets/{fileCommentAnnotations-BotL9n2H.js → fileCommentAnnotations-Z0wcn9Z2.js} +2 -2
  22. package/dist/client/assets/{git-rebase-C7PDtezn.js → git-rebase-BvBzTqE6.js} +2 -2
  23. package/dist/client/assets/{glimmer-js-CXugsloP.js → glimmer-js-y6xvI0dn.js} +2 -2
  24. package/dist/client/assets/{glimmer-ts-IiHVp7il.js → glimmer-ts-BrWleVTO.js} +2 -2
  25. package/dist/client/assets/{glsl-eBdXjOgx.js → glsl-Bkrdoinn.js} +2 -2
  26. package/dist/client/assets/{graphql-CBwWowq8.js → graphql-CSx1KLSt.js} +2 -2
  27. package/dist/client/assets/{hack-DoKoRNA-.js → hack-CEPENt6B.js} +2 -2
  28. package/dist/client/assets/{haml-B2cA0ELm.js → haml-CR4MMWD-.js} +2 -2
  29. package/dist/client/assets/{handlebars-OQNujFnM.js → handlebars-BvZ81eM2.js} +2 -2
  30. package/dist/client/assets/{html-B9l55IY7.js → html-CJJ_Yk-p.js} +2 -2
  31. package/dist/client/assets/{html-derivative-C24d_8BY.js → html-derivative-DyGTUpT_.js} +2 -2
  32. package/dist/client/assets/{http-Ca2KxOLT.js → http-CthnayZT.js} +2 -2
  33. package/dist/client/assets/{hurl-BuLZo6wH.js → hurl-CySpRHwn.js} +2 -2
  34. package/dist/client/assets/index-CTDRPB7h.js +664 -0
  35. package/dist/client/assets/index-CstNfQU9.css +1 -0
  36. package/dist/client/assets/{java-BEVFbCvA.js → java-eLetcmnB.js} +2 -2
  37. package/dist/client/assets/{javascript-AWDSrR1H.js → javascript-bze-DcnN.js} +2 -2
  38. package/dist/client/assets/{jinja-CFK-VDc5.js → jinja-BwBfXVzv.js} +2 -2
  39. package/dist/client/assets/{jison-CSmyyQ0V.js → jison-BG-6HBeN.js} +2 -2
  40. package/dist/client/assets/{json-D0qpEJUc.js → json-D04kql_u.js} +2 -2
  41. package/dist/client/assets/{jsx-smdyqdvA.js → jsx-B3RhaC78.js} +2 -2
  42. package/dist/client/assets/{julia-DWo_5QH9.js → julia-CLYU2NBT.js} +2 -2
  43. package/dist/client/assets/{just-74GGYpCk.js → just-CQGSktLF.js} +2 -2
  44. package/dist/client/assets/{latex-d8vfKbSv.js → latex-B2r18a0i.js} +2 -2
  45. package/dist/client/assets/{liquid-CsWNZJPs.js → liquid-DSPVRc0S.js} +2 -2
  46. package/dist/client/assets/{lua-x3z7TbKl.js → lua-CBxs-my9.js} +2 -2
  47. package/dist/client/assets/{marko-CJyhwQKw.js → marko-FkXUABHT.js} +2 -2
  48. package/dist/client/assets/{mdc-CtbrrOYL.js → mdc-BW-qJ07Y.js} +2 -2
  49. package/dist/client/assets/{nginx-CY-uw8dq.js → nginx-r-F4ISTc.js} +2 -2
  50. package/dist/client/assets/{nim-DqT4w7rK.js → nim-CqGONDki.js} +2 -2
  51. package/dist/client/assets/{perl-CE3_DH1L.js → perl-YqxSDkbB.js} +2 -2
  52. package/dist/client/assets/{php-Zxsq1XMP.js → php-o53m7ISl.js} +2 -2
  53. package/dist/client/assets/{pug-DnCImvlN.js → pug-DvC1IxmZ.js} +2 -2
  54. package/dist/client/assets/{qml-DLfoubjW.js → qml-B_UHgwrf.js} +2 -2
  55. package/dist/client/assets/{r-BtfLsgk8.js → r-BslRtXqA.js} +2 -2
  56. package/dist/client/assets/{razor-CzAGh9fe.js → razor-DbFRY8EJ.js} +2 -2
  57. package/dist/client/assets/{regexp-Bw17HBbP.js → regexp-LUjvkiV5.js} +2 -2
  58. package/dist/client/assets/{rst-DhRr0i5h.js → rst-DjMRC4dl.js} +2 -2
  59. package/dist/client/assets/{ruby-BnQcHU-H.js → ruby-D1SSXqCW.js} +2 -2
  60. package/dist/client/assets/{sas-mMUu1htl.js → sas-8pH9ZpYC.js} +2 -2
  61. package/dist/client/assets/{scss-0_lOQ1qp.js → scss-CLXZYmiL.js} +2 -2
  62. package/dist/client/assets/{shellscript-BAdG8zJQ.js → shellscript-C3x4iGUN.js} +2 -2
  63. package/dist/client/assets/{shellsession-upfFHc_2.js → shellsession-4iw-70AB.js} +2 -2
  64. package/dist/client/assets/{soy-Dzncl5z6.js → soy-BmuiXqhy.js} +2 -2
  65. package/dist/client/assets/{sql-Ca2FgbAh.js → sql-CYtr1JGt.js} +2 -2
  66. package/dist/client/assets/{stata-D8IymSlL.js → stata-D-p-ifu4.js} +2 -2
  67. package/dist/client/assets/{surrealql-CZsXvHAi.js → surrealql-BQtaSVD9.js} +2 -2
  68. package/dist/client/assets/{svelte-nM61Il-m.js → svelte-BZ0IuCtg.js} +2 -2
  69. package/dist/client/assets/{templ-DzjlBYVo.js → templ-D_0C9uOG.js} +2 -2
  70. package/dist/client/assets/{tex-BPwIdn9G.js → tex-Da564oMl.js} +2 -2
  71. package/dist/client/assets/{textarea-DhTMBBxg.js → textarea-CAyleL6s.js} +2 -2
  72. package/dist/client/assets/{ts-tags-BL9D6Ojm.js → ts-tags-CCvt3g0S.js} +2 -2
  73. package/dist/client/assets/{tsx-AKDVGVPx.js → tsx-C1pKkLrg.js} +2 -2
  74. package/dist/client/assets/{twig-B79f_Jz8.js → twig-ulf4RPD9.js} +2 -2
  75. package/dist/client/assets/{typescript-BsyojNKj.js → typescript-Cm2BDYNJ.js} +2 -2
  76. package/dist/client/assets/{vue-BTqtDJT-.js → vue-D38YVq_o.js} +2 -2
  77. package/dist/client/assets/{vue-html-B_UfyILk.js → vue-html-Dw9Iwm-j.js} +2 -2
  78. package/dist/client/assets/{vue-vine-B8EK-Iln.js → vue-vine-DGhLcVZx.js} +2 -2
  79. package/dist/client/assets/{xml-Do-3lCfL.js → xml-Bm9IDqQ-.js} +2 -2
  80. package/dist/client/assets/{xsl-CwgwYGXC.js → xsl-CLGFKBCX.js} +2 -2
  81. package/dist/client/assets/{yaml-C4XlY-p5.js → yaml-C5Olrwb1.js} +2 -2
  82. package/dist/client/index.html +3 -3
  83. package/package.json +1 -1
  84. package/dist/client/assets/arrow-right-B-_6vDIF.js +0 -2
  85. package/dist/client/assets/index-B-nUm8w7.js +0 -664
  86. package/dist/client/assets/index-DfKhU-QO.css +0 -1
package/dist/bin.mjs CHANGED
@@ -233,7 +233,7 @@ const make$73 = () => {
233
233
  const layer$69 = Layer.sync(NetService, make$73);
234
234
  //#endregion
235
235
  //#region package.json
236
- var version = "0.1.6";
236
+ var version = "0.1.7";
237
237
  //#endregion
238
238
  //#region src/config.ts
239
239
  /**
@@ -5634,22 +5634,11 @@ const ClaudeSettings = makeProviderSettingsSchema({
5634
5634
  placeholder: "e.g. --chrome",
5635
5635
  clearWhenEmpty: "omit"
5636
5636
  }
5637
- })),
5638
- /**
5639
- * The SDK preset system prompt omits the interactive CLI's terminal-tone
5640
- * sections, so the model tends to open a turn with a tool call instead of
5641
- * saying what it is about to do. This appends that instruction back.
5642
- */
5643
- narrateBeforeTools: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true)), Schema$1.annotateKey({
5644
- title: "Explain before tool calls",
5645
- description: "Ask Claude to write one short sentence before its first tool call in a turn.",
5646
- providerSettingsForm: { control: "switch" }
5647
5637
  }))
5648
5638
  }, { order: [
5649
5639
  "binaryPath",
5650
5640
  "homePath",
5651
- "launchArgs",
5652
- "narrateBeforeTools"
5641
+ "launchArgs"
5653
5642
  ] });
5654
5643
  const CursorSettings = makeProviderSettingsSchema({
5655
5644
  enabled: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false)), Schema$1.annotateKey({ providerSettingsForm: { hidden: true } })),
@@ -5735,6 +5724,13 @@ const TaskBoardSource = Schema$1.Literals(["local", "hub"]);
5735
5724
  const ServerSettings = Schema$1.Struct({
5736
5725
  enableAssistantStreaming: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
5737
5726
  enableProviderUpdateChecks: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
5727
+ /**
5728
+ * The Claude SDK preset system prompt omits the interactive CLI's
5729
+ * terminal-tone sections, so the model tends to open a turn with a tool call
5730
+ * instead of saying what it is about to do. This appends that instruction
5731
+ * back for sessions started by the Claude provider.
5732
+ */
5733
+ enableToolCallNarration: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
5738
5734
  automaticGitFetchInterval: Schema$1.DurationFromMillis.pipe(Schema$1.withDecodingDefault(Effect.succeed(Duration.toMillis(DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL)))),
5739
5735
  defaultThreadEnvMode: ThreadEnvMode.pipe(Schema$1.withDecodingDefault(Effect.succeed("local"))),
5740
5736
  newWorktreesStartFromOrigin: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
@@ -5838,8 +5834,7 @@ const ClaudeSettingsPatch = Schema$1.Struct({
5838
5834
  binaryPath: Schema$1.optionalKey(TrimmedString),
5839
5835
  homePath: Schema$1.optionalKey(TrimmedString),
5840
5836
  customModels: Schema$1.optionalKey(Schema$1.Array(Schema$1.String)),
5841
- launchArgs: Schema$1.optionalKey(TrimmedString),
5842
- narrateBeforeTools: Schema$1.optionalKey(Schema$1.Boolean)
5837
+ launchArgs: Schema$1.optionalKey(TrimmedString)
5843
5838
  });
5844
5839
  const CursorSettingsPatch = Schema$1.Struct({
5845
5840
  enabled: Schema$1.optionalKey(Schema$1.Boolean),
@@ -5862,6 +5857,7 @@ const OpenCodeSettingsPatch = Schema$1.Struct({
5862
5857
  const ServerSettingsPatch = Schema$1.Struct({
5863
5858
  enableAssistantStreaming: Schema$1.optionalKey(Schema$1.Boolean),
5864
5859
  enableProviderUpdateChecks: Schema$1.optionalKey(Schema$1.Boolean),
5860
+ enableToolCallNarration: Schema$1.optionalKey(Schema$1.Boolean),
5865
5861
  automaticGitFetchInterval: Schema$1.optionalKey(Schema$1.DurationFromMillis),
5866
5862
  defaultThreadEnvMode: Schema$1.optionalKey(ThreadEnvMode),
5867
5863
  newWorktreesStartFromOrigin: Schema$1.optionalKey(Schema$1.Boolean),
@@ -31482,6 +31478,287 @@ const make$39 = Effect.gen(function* () {
31482
31478
  });
31483
31479
  const layer$34 = Layer.effect(CheckpointDiffQuery, make$39);
31484
31480
  //#endregion
31481
+ //#region ../../packages/shared/src/toolCategory.ts
31482
+ const TOOL_CATEGORY_TITLES = {
31483
+ file_read: "File read",
31484
+ file_search: "File search",
31485
+ file_change: "File change",
31486
+ command: "Command run",
31487
+ version_control: "Version control",
31488
+ build_test: "Build or test",
31489
+ mcp_tool: "MCP tool call",
31490
+ subagent: "Subagent task",
31491
+ web_search: "Web search",
31492
+ web_fetch: "Web fetch",
31493
+ task_plan: "Task or plan",
31494
+ image_view: "Image view",
31495
+ tool: "Tool call"
31496
+ };
31497
+ new Set(Object.keys(TOOL_CATEGORY_TITLES));
31498
+ /** Exact tool names, lowercased, matched before the substring heuristics below. */
31499
+ const FILE_READ_TOOL_NAMES = /* @__PURE__ */ new Set([
31500
+ "read",
31501
+ "readfile",
31502
+ "read_file",
31503
+ "view",
31504
+ "viewfile",
31505
+ "view_file",
31506
+ "notebookread",
31507
+ "notebook_read",
31508
+ "openfile",
31509
+ "open_file"
31510
+ ]);
31511
+ const FILE_SEARCH_TOOL_NAMES = /* @__PURE__ */ new Set([
31512
+ "grep",
31513
+ "glob",
31514
+ "search",
31515
+ "filesearch",
31516
+ "file_search",
31517
+ "codebasesearch",
31518
+ "codebase_search",
31519
+ "listdir",
31520
+ "list_dir",
31521
+ "list_directory",
31522
+ "ls"
31523
+ ]);
31524
+ const FILE_CHANGE_TOOL_NAMES = /* @__PURE__ */ new Set([
31525
+ "edit",
31526
+ "write",
31527
+ "multiedit",
31528
+ "multi_edit",
31529
+ "notebookedit",
31530
+ "notebook_edit",
31531
+ "applypatch",
31532
+ "apply_patch",
31533
+ "patch",
31534
+ "createfile",
31535
+ "create_file",
31536
+ "strreplace",
31537
+ "str_replace",
31538
+ "deletefile",
31539
+ "delete_file"
31540
+ ]);
31541
+ const COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set([
31542
+ "bash",
31543
+ "shell",
31544
+ "terminal",
31545
+ "exec",
31546
+ "execute",
31547
+ "execcommand",
31548
+ "exec_command",
31549
+ "runcommand",
31550
+ "run_command",
31551
+ "local_shell"
31552
+ ]);
31553
+ const SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
31554
+ "task",
31555
+ "agent",
31556
+ "subagent",
31557
+ "sub_agent"
31558
+ ]);
31559
+ const WEB_FETCH_TOOL_NAMES = /* @__PURE__ */ new Set([
31560
+ "webfetch",
31561
+ "web_fetch",
31562
+ "fetch",
31563
+ "fetchurl",
31564
+ "fetch_url",
31565
+ "browse",
31566
+ "openurl",
31567
+ "open_url"
31568
+ ]);
31569
+ /**
31570
+ * Matched before the subagent rules, so `task_create` is a board write while the
31571
+ * bare `Task` tool stays a subagent.
31572
+ */
31573
+ const TASK_PLAN_TOOL_NAMES = /* @__PURE__ */ new Set([
31574
+ "todowrite",
31575
+ "todo_write",
31576
+ "todoread",
31577
+ "todo_read",
31578
+ "exitplanmode",
31579
+ "exit_plan_mode",
31580
+ "updateplan",
31581
+ "update_plan",
31582
+ "task_create",
31583
+ "task_update",
31584
+ "task_list",
31585
+ "task_get",
31586
+ "task_current",
31587
+ "task_propose",
31588
+ "taskcreate",
31589
+ "taskupdate",
31590
+ "tasklist",
31591
+ "taskget"
31592
+ ]);
31593
+ /** Shell builtins that say nothing about what the command as a whole does. */
31594
+ const NEUTRAL_SHELL_COMMANDS = /* @__PURE__ */ new Set([
31595
+ "cd",
31596
+ "echo",
31597
+ "printf",
31598
+ "pwd",
31599
+ "true",
31600
+ "false",
31601
+ "time"
31602
+ ]);
31603
+ /** `sed`/`awk` are stream editors (`sed -i` rewrites files), so they stay commands. */
31604
+ const FILE_READ_SHELL_COMMANDS = /* @__PURE__ */ new Set([
31605
+ "cat",
31606
+ "head",
31607
+ "tail",
31608
+ "less",
31609
+ "more",
31610
+ "bat",
31611
+ "nl",
31612
+ "jq",
31613
+ "wc"
31614
+ ]);
31615
+ const FILE_SEARCH_SHELL_COMMANDS = /* @__PURE__ */ new Set([
31616
+ "grep",
31617
+ "egrep",
31618
+ "fgrep",
31619
+ "rg",
31620
+ "ag",
31621
+ "ack",
31622
+ "find",
31623
+ "fd",
31624
+ "ls",
31625
+ "tree",
31626
+ "which"
31627
+ ]);
31628
+ /** `git status` and `git diff` sort here too: the verb is what a reader scans for. */
31629
+ const VERSION_CONTROL_SHELL_COMMANDS = /* @__PURE__ */ new Set([
31630
+ "git",
31631
+ "gh",
31632
+ "jj",
31633
+ "hg",
31634
+ "svn",
31635
+ "glab"
31636
+ ]);
31637
+ /**
31638
+ * Builds, tests, type checks, lint and install share one category: a test run is
31639
+ * what a person scans for when a turn goes long, and the rest is the same noise.
31640
+ */
31641
+ const BUILD_TEST_SHELL_COMMANDS = /* @__PURE__ */ new Set([
31642
+ "pnpm",
31643
+ "npm",
31644
+ "npx",
31645
+ "yarn",
31646
+ "bun",
31647
+ "bunx",
31648
+ "vitest",
31649
+ "jest",
31650
+ "vp",
31651
+ "tsc",
31652
+ "tsgo",
31653
+ "eslint",
31654
+ "oxlint",
31655
+ "biome",
31656
+ "prettier",
31657
+ "ruff",
31658
+ "pytest",
31659
+ "make",
31660
+ "cargo",
31661
+ "gradle",
31662
+ "mvn",
31663
+ "xcodebuild",
31664
+ "swift",
31665
+ "pip",
31666
+ "uv",
31667
+ "poetry"
31668
+ ]);
31669
+ const WEB_FETCH_SHELL_COMMANDS = /* @__PURE__ */ new Set([
31670
+ "curl",
31671
+ "wget",
31672
+ "http",
31673
+ "httpie"
31674
+ ]);
31675
+ const SHELL_SEGMENT_SEPARATOR = /\|\||&&|;|\||\n/u;
31676
+ const LEADING_ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=\S*\s+/u;
31677
+ const REDIRECT_TARGET = /\d?>>?\s*(?<target>[^\s|;&]+)/gu;
31678
+ /** Redirect targets that discard output instead of writing a file. */
31679
+ const DISCARDED_REDIRECT_TARGETS = /* @__PURE__ */ new Set([
31680
+ "/dev/null",
31681
+ "&1",
31682
+ "&2"
31683
+ ]);
31684
+ function toolCategoryTitle(category) {
31685
+ return TOOL_CATEGORY_TITLES[category];
31686
+ }
31687
+ function readCommandInput(toolInput) {
31688
+ const raw = toolInput?.command ?? toolInput?.cmd;
31689
+ if (typeof raw !== "string") return;
31690
+ const trimmed = raw.trim();
31691
+ return trimmed.length > 0 ? trimmed : void 0;
31692
+ }
31693
+ /** `cat x > y` reads and writes; only the write matters for the row heading. */
31694
+ function redirectsToFile(segment) {
31695
+ for (const match of segment.matchAll(REDIRECT_TARGET)) {
31696
+ const target = match.groups?.target;
31697
+ if (target && !DISCARDED_REDIRECT_TARGETS.has(target)) return true;
31698
+ }
31699
+ return false;
31700
+ }
31701
+ function classifyShellSegment(segment) {
31702
+ if (redirectsToFile(segment)) return "command";
31703
+ const head = segment.trim().replace(LEADING_ENV_ASSIGNMENT, "").split(/\s+/u)[0]?.replace(/^.*\//u, "").toLowerCase();
31704
+ if (!head || NEUTRAL_SHELL_COMMANDS.has(head)) return;
31705
+ if (FILE_READ_SHELL_COMMANDS.has(head)) return "file_read";
31706
+ if (FILE_SEARCH_SHELL_COMMANDS.has(head)) return "file_search";
31707
+ if (VERSION_CONTROL_SHELL_COMMANDS.has(head)) return "version_control";
31708
+ if (BUILD_TEST_SHELL_COMMANDS.has(head)) return "build_test";
31709
+ if (WEB_FETCH_SHELL_COMMANDS.has(head)) return "web_fetch";
31710
+ return "command";
31711
+ }
31712
+ /**
31713
+ * A shell call only counts as anything narrower than a command when every
31714
+ * non-neutral segment agrees; anything unrecognized (or write-ish) keeps the
31715
+ * whole call a command, and so does a pipeline that mixes two categories — the
31716
+ * one exception being a read piped into a search, which is still a search.
31717
+ * Quoted separators split segments too, which can only downgrade to "command".
31718
+ */
31719
+ function classifyShellCommand(command) {
31720
+ const categories = new Set(command.split(SHELL_SEGMENT_SEPARATOR).map(classifyShellSegment).filter((category) => category !== void 0));
31721
+ if (categories.size === 0 || categories.has("command")) return "command";
31722
+ const [only] = categories;
31723
+ if (categories.size === 1 && only) return only;
31724
+ return [...categories].every((category) => category === "file_read" || category === "file_search") ? "file_search" : "command";
31725
+ }
31726
+ function classifyToolCategory(input) {
31727
+ const normalized = input.toolName.trim().toLowerCase();
31728
+ if (normalized.startsWith("mcp__") || normalized.includes("mcp")) return "mcp_tool";
31729
+ if (TASK_PLAN_TOOL_NAMES.has(normalized)) return "task_plan";
31730
+ if (WEB_FETCH_TOOL_NAMES.has(normalized)) return "web_fetch";
31731
+ if (SUBAGENT_TOOL_NAMES.has(normalized) || normalized.includes("agent")) return "subagent";
31732
+ if (FILE_READ_TOOL_NAMES.has(normalized)) return "file_read";
31733
+ if (FILE_SEARCH_TOOL_NAMES.has(normalized)) return "file_search";
31734
+ if (FILE_CHANGE_TOOL_NAMES.has(normalized)) return "file_change";
31735
+ if (COMMAND_TOOL_NAMES.has(normalized) || normalized.includes("bash") || normalized.includes("shell") || normalized.includes("terminal") || normalized.includes("command")) {
31736
+ const command = readCommandInput(input.toolInput);
31737
+ return command ? classifyShellCommand(command) : "command";
31738
+ }
31739
+ if (normalized.includes("websearch") || normalized.includes("web_search")) return "web_search";
31740
+ if (normalized.includes("webfetch") || normalized.includes("web_fetch")) return "web_fetch";
31741
+ if (normalized.includes("todo") || normalized.includes("plan")) return "task_plan";
31742
+ if (normalized.includes("edit") || normalized.includes("write") || normalized.includes("patch") || normalized.includes("replace") || normalized.includes("create") || normalized.includes("delete")) return "file_change";
31743
+ if (normalized.includes("read") || normalized.includes("view")) return "file_read";
31744
+ if (normalized.includes("grep") || normalized.includes("glob")) return "file_search";
31745
+ if (normalized.includes("image")) return "image_view";
31746
+ return "tool";
31747
+ }
31748
+ function asRecord$2(value) {
31749
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
31750
+ }
31751
+ /** Classify from a runtime item payload's `data` (`{ toolName, input }`). */
31752
+ function classifyToolCategoryFromToolData(data) {
31753
+ const record = asRecord$2(data);
31754
+ const toolName = record?.toolName;
31755
+ if (typeof toolName !== "string" || toolName.trim().length === 0) return;
31756
+ return classifyToolCategory({
31757
+ toolName,
31758
+ toolInput: asRecord$2(record?.input)
31759
+ });
31760
+ }
31761
+ //#endregion
31485
31762
  //#region src/orchestration/ActivityPayloadProjection.ts
31486
31763
  function asRecord$1(value) {
31487
31764
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -31583,9 +31860,13 @@ function projectActivityPayload(activity) {
31583
31860
  const item = projectCommandData(data);
31584
31861
  if (item) projectedData.item = item;
31585
31862
  if ("command" in data) projectedData.command = data.command;
31863
+ const input = asRecord$1(data.input);
31864
+ if (input && "command" in input) projectedData.input = { command: input.command };
31586
31865
  const changedFiles = [];
31587
31866
  collectChangedFiles(data, changedFiles, /* @__PURE__ */ new Set(), 0);
31588
31867
  if (changedFiles.length > 0) projectedData.files = changedFiles.map((path) => ({ path }));
31868
+ const toolCategory = classifyToolCategoryFromToolData(data);
31869
+ if (toolCategory) projectedData.toolCategory = toolCategory;
31589
31870
  if ("toolCallId" in data) projectedData.toolCallId = data.toolCallId;
31590
31871
  if ("kind" in data) projectedData.kind = data.kind;
31591
31872
  const rawOutput = projectRawOutput(data.rawOutput);
@@ -46483,177 +46764,6 @@ function parseCliArgs(args, options) {
46483
46764
  };
46484
46765
  }
46485
46766
  //#endregion
46486
- //#region ../../packages/shared/src/toolCategory.ts
46487
- const TOOL_CATEGORY_TITLES = {
46488
- file_read: "File read",
46489
- file_search: "File search",
46490
- file_change: "File change",
46491
- command: "Command run",
46492
- mcp_tool: "MCP tool call",
46493
- subagent: "Subagent task",
46494
- web_search: "Web search",
46495
- image_view: "Image view",
46496
- tool: "Tool call"
46497
- };
46498
- /** Exact tool names, lowercased, matched before the substring heuristics below. */
46499
- const FILE_READ_TOOL_NAMES = /* @__PURE__ */ new Set([
46500
- "read",
46501
- "readfile",
46502
- "read_file",
46503
- "view",
46504
- "viewfile",
46505
- "view_file",
46506
- "notebookread",
46507
- "notebook_read",
46508
- "openfile",
46509
- "open_file"
46510
- ]);
46511
- const FILE_SEARCH_TOOL_NAMES = /* @__PURE__ */ new Set([
46512
- "grep",
46513
- "glob",
46514
- "search",
46515
- "filesearch",
46516
- "file_search",
46517
- "codebasesearch",
46518
- "codebase_search",
46519
- "listdir",
46520
- "list_dir",
46521
- "list_directory",
46522
- "ls"
46523
- ]);
46524
- const FILE_CHANGE_TOOL_NAMES = /* @__PURE__ */ new Set([
46525
- "edit",
46526
- "write",
46527
- "multiedit",
46528
- "multi_edit",
46529
- "notebookedit",
46530
- "notebook_edit",
46531
- "applypatch",
46532
- "apply_patch",
46533
- "patch",
46534
- "createfile",
46535
- "create_file",
46536
- "strreplace",
46537
- "str_replace",
46538
- "deletefile",
46539
- "delete_file"
46540
- ]);
46541
- const COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set([
46542
- "bash",
46543
- "shell",
46544
- "terminal",
46545
- "exec",
46546
- "execute",
46547
- "execcommand",
46548
- "exec_command",
46549
- "runcommand",
46550
- "run_command",
46551
- "local_shell"
46552
- ]);
46553
- const SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
46554
- "task",
46555
- "agent",
46556
- "subagent",
46557
- "sub_agent"
46558
- ]);
46559
- /** Shell builtins that say nothing about what the command as a whole does. */
46560
- const NEUTRAL_SHELL_COMMANDS = /* @__PURE__ */ new Set([
46561
- "cd",
46562
- "echo",
46563
- "printf",
46564
- "pwd",
46565
- "true",
46566
- "false",
46567
- "time"
46568
- ]);
46569
- /** `sed`/`awk` are stream editors (`sed -i` rewrites files), so they stay commands. */
46570
- const FILE_READ_SHELL_COMMANDS = /* @__PURE__ */ new Set([
46571
- "cat",
46572
- "head",
46573
- "tail",
46574
- "less",
46575
- "more",
46576
- "bat",
46577
- "nl",
46578
- "jq",
46579
- "wc"
46580
- ]);
46581
- const FILE_SEARCH_SHELL_COMMANDS = /* @__PURE__ */ new Set([
46582
- "grep",
46583
- "egrep",
46584
- "fgrep",
46585
- "rg",
46586
- "ag",
46587
- "ack",
46588
- "find",
46589
- "fd",
46590
- "ls",
46591
- "tree",
46592
- "which"
46593
- ]);
46594
- const SHELL_SEGMENT_SEPARATOR = /\|\||&&|;|\||\n/u;
46595
- const LEADING_ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=\S*\s+/u;
46596
- const REDIRECT_TARGET = /\d?>>?\s*(?<target>[^\s|;&]+)/gu;
46597
- /** Redirect targets that discard output instead of writing a file. */
46598
- const DISCARDED_REDIRECT_TARGETS = /* @__PURE__ */ new Set([
46599
- "/dev/null",
46600
- "&1",
46601
- "&2"
46602
- ]);
46603
- function toolCategoryTitle(category) {
46604
- return TOOL_CATEGORY_TITLES[category];
46605
- }
46606
- function readCommandInput(toolInput) {
46607
- const raw = toolInput?.command ?? toolInput?.cmd;
46608
- if (typeof raw !== "string") return;
46609
- const trimmed = raw.trim();
46610
- return trimmed.length > 0 ? trimmed : void 0;
46611
- }
46612
- /** `cat x > y` reads and writes; only the write matters for the row heading. */
46613
- function redirectsToFile(segment) {
46614
- for (const match of segment.matchAll(REDIRECT_TARGET)) {
46615
- const target = match.groups?.target;
46616
- if (target && !DISCARDED_REDIRECT_TARGETS.has(target)) return true;
46617
- }
46618
- return false;
46619
- }
46620
- function classifyShellSegment(segment) {
46621
- if (redirectsToFile(segment)) return "command";
46622
- const head = segment.trim().replace(LEADING_ENV_ASSIGNMENT, "").split(/\s+/u)[0]?.replace(/^.*\//u, "").toLowerCase();
46623
- if (!head || NEUTRAL_SHELL_COMMANDS.has(head)) return;
46624
- if (FILE_READ_SHELL_COMMANDS.has(head)) return "file_read";
46625
- if (FILE_SEARCH_SHELL_COMMANDS.has(head)) return "file_search";
46626
- return "command";
46627
- }
46628
- /**
46629
- * A shell call only counts as a read or a search when every non-neutral segment
46630
- * agrees; anything unrecognized (or write-ish) keeps the whole call a command.
46631
- * Quoted separators split segments too, which can only downgrade to "command".
46632
- */
46633
- function classifyShellCommand(command) {
46634
- const categories = command.split(SHELL_SEGMENT_SEPARATOR).map(classifyShellSegment).filter((category) => category !== void 0);
46635
- if (categories.length === 0 || categories.includes("command")) return "command";
46636
- return categories.includes("file_search") ? "file_search" : "file_read";
46637
- }
46638
- function classifyToolCategory(input) {
46639
- const normalized = input.toolName.trim().toLowerCase();
46640
- if (normalized.startsWith("mcp__") || normalized.includes("mcp")) return "mcp_tool";
46641
- if (SUBAGENT_TOOL_NAMES.has(normalized) || normalized.includes("agent")) return "subagent";
46642
- if (FILE_READ_TOOL_NAMES.has(normalized)) return "file_read";
46643
- if (FILE_SEARCH_TOOL_NAMES.has(normalized)) return "file_search";
46644
- if (FILE_CHANGE_TOOL_NAMES.has(normalized)) return "file_change";
46645
- if (COMMAND_TOOL_NAMES.has(normalized) || normalized.includes("bash") || normalized.includes("shell") || normalized.includes("terminal") || normalized.includes("command")) {
46646
- const command = readCommandInput(input.toolInput);
46647
- return command ? classifyShellCommand(command) : "command";
46648
- }
46649
- if (normalized.includes("websearch") || normalized.includes("web_search")) return "web_search";
46650
- if (normalized.includes("edit") || normalized.includes("write") || normalized.includes("patch") || normalized.includes("replace") || normalized.includes("create") || normalized.includes("delete")) return "file_change";
46651
- if (normalized.includes("read") || normalized.includes("view")) return "file_read";
46652
- if (normalized.includes("grep") || normalized.includes("glob")) return "file_search";
46653
- if (normalized.includes("image")) return "image_view";
46654
- return "tool";
46655
- }
46656
- //#endregion
46657
46767
  //#region src/provider/askUserQuestionAnswers.ts
46658
46768
  /**
46659
46769
  * What separates the options of one multi-select answer.
@@ -47249,6 +47359,8 @@ const SUPPORTED_CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
47249
47359
  * preset drops the interactive CLI's terminal-tone sections, so without this the
47250
47360
  * model opens a turn with a tool call rather than a line saying what it is doing.
47251
47361
  */
47362
+ /** Emitted by the CLI but absent from the SDK's `SDKMessage` union. */
47363
+ const UNTYPED_COMMAND_LIFECYCLE_MESSAGE = "command_lifecycle";
47252
47364
  const NARRATE_BEFORE_TOOLS_PROMPT = "Before your first tool call in a turn, write one short sentence saying what you are about to do. Keep it to a single line and skip it when you are answering without tools.";
47253
47365
  const CLAUDE_SETTING_SOURCES = [
47254
47366
  "user",
@@ -48694,6 +48806,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
48694
48806
  const handleSdkMessage = Effect.fn("handleSdkMessage")(function* (context, message) {
48695
48807
  yield* logNativeSdkMessage(context, message);
48696
48808
  yield* ensureThreadId(context, message);
48809
+ if (message.type === UNTYPED_COMMAND_LIFECYCLE_MESSAGE) return;
48697
48810
  switch (message.type) {
48698
48811
  case "stream_event":
48699
48812
  yield* handleStreamEvent(context, message);
@@ -49076,6 +49189,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
49076
49189
  };
49077
49190
  const mcpSession = readMcpProviderSession(input.threadId);
49078
49191
  const externalMcpServers = options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers;
49192
+ const narrateBeforeTools = options?.resolveToolCallNarration === void 0 ? DEFAULT_SERVER_SETTINGS.enableToolCallNarration : yield* options.resolveToolCallNarration;
49079
49193
  const queryOptions = {
49080
49194
  ...input.cwd ? { cwd: input.cwd } : {},
49081
49195
  ...apiModelId ? { model: apiModelId } : {},
@@ -49083,7 +49197,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
49083
49197
  systemPrompt: {
49084
49198
  type: "preset",
49085
49199
  preset: "claude_code",
49086
- ...claudeSettings.narrateBeforeTools ? { append: NARRATE_BEFORE_TOOLS_PROMPT } : {}
49200
+ ...narrateBeforeTools ? { append: NARRATE_BEFORE_TOOLS_PROMPT } : {}
49087
49201
  },
49088
49202
  settingSources: [...CLAUDE_SETTING_SOURCES],
49089
49203
  ...effectiveEffort ? { effort: effectiveEffort } : {},
@@ -49125,7 +49239,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
49125
49239
  "claude.query.include_partial_messages": true,
49126
49240
  "claude.query.additional_directories": input.cwd ? [input.cwd] : [],
49127
49241
  "claude.query.setting_sources": [...CLAUDE_SETTING_SOURCES],
49128
- "claude.query.narrate_before_tools": claudeSettings.narrateBeforeTools,
49242
+ "claude.query.narrate_before_tools": narrateBeforeTools,
49129
49243
  "claude.query.settings_json": encodeJsonStringForDiagnostics$2(settings) ?? "",
49130
49244
  "claude.query.extra_args_json": encodeJsonStringForDiagnostics$2(extraArgs) ?? "",
49131
49245
  "claude.query.path_to_executable": claudeBinaryPath
@@ -49536,11 +49650,13 @@ const ClaudeDriver = {
49536
49650
  });
49537
49651
  const mcpRegistry = yield* McpRegistry;
49538
49652
  const resolveDisabledSkills = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => []));
49653
+ const resolveToolCallNarration = serverSettings.getSettings.pipe(Effect.map((settings) => settings.enableToolCallNarration), Effect.orElseSucceed(() => DEFAULT_SERVER_SETTINGS.enableToolCallNarration));
49539
49654
  const adapter = yield* makeClaudeAdapter(effectiveConfig, {
49540
49655
  instanceId,
49541
49656
  environment: processEnv,
49542
49657
  resolveMcpServers: mcpRegistry.resolveForSession,
49543
49658
  resolveDisabledSkills,
49659
+ resolveToolCallNarration,
49544
49660
  ...eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}
49545
49661
  });
49546
49662
  const textGeneration = yield* makeClaudeTextGeneration(effectiveConfig, processEnv);
@@ -82429,6 +82545,31 @@ const make$2 = Effect.gen(function* () {
82429
82545
  },
82430
82546
  createdAt: input.createdAt
82431
82547
  })));
82548
+ /**
82549
+ * A successful interrupt otherwise leaves nothing behind: the only row the
82550
+ * timeline ever got was the failure case above, and the turn fold's "You
82551
+ * stopped after Xs" is invisible while the fold is open and says nothing in
82552
+ * the work log. The turn id comes from the request rather than the provider
82553
+ * because orchestration turn ids are not provider turn ids.
82554
+ */
82555
+ const appendTurnInterruptedActivity = (input) => Effect.all({
82556
+ commandId: serverCommandId("provider-interrupt-activity"),
82557
+ eventId: serverEventId()
82558
+ }).pipe(Effect.flatMap(({ commandId, eventId }) => orchestrationEngine.dispatch({
82559
+ type: "thread.activity.append",
82560
+ commandId,
82561
+ threadId: input.threadId,
82562
+ activity: {
82563
+ id: eventId,
82564
+ tone: "info",
82565
+ kind: "provider.turn.interrupted",
82566
+ summary: "Interrupted by user",
82567
+ payload: {},
82568
+ turnId: input.turnId,
82569
+ createdAt: input.createdAt
82570
+ },
82571
+ createdAt: input.createdAt
82572
+ })));
82432
82573
  const formatFailureDetail = (cause) => {
82433
82574
  const failReason = cause.reasons.find(Cause.isFailReason);
82434
82575
  const providerError = isProviderAdapterRequestError(failReason?.error) ? failReason.error : void 0;
@@ -82815,6 +82956,11 @@ const make$2 = Effect.gen(function* () {
82815
82956
  createdAt: event.payload.createdAt
82816
82957
  });
82817
82958
  yield* providerService.interruptTurn({ threadId: event.payload.threadId });
82959
+ yield* appendTurnInterruptedActivity({
82960
+ threadId: event.payload.threadId,
82961
+ turnId: event.payload.turnId ?? null,
82962
+ createdAt: event.payload.createdAt
82963
+ });
82818
82964
  });
82819
82965
  const processApprovalResponseRequested = Effect.fn("processApprovalResponseRequested")(function* (event) {
82820
82966
  const thread = yield* resolveThread(event.payload.threadId);