zelari-code 0.6.2 → 0.7.5

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 (95) hide show
  1. package/bin/zelari-code.js +1 -1
  2. package/dist/agents/councilApi.js +7 -7
  3. package/dist/agents/councilApi.js.map +1 -1
  4. package/dist/agents/councilDirectives.js +3 -3
  5. package/dist/agents/promoteMember.js +2 -2
  6. package/dist/agents/roles.js +50 -32
  7. package/dist/agents/roles.js.map +1 -1
  8. package/dist/agents/skills/builtin/debugging.js +4 -4
  9. package/dist/agents/skills/builtin/debugging.js.map +1 -1
  10. package/dist/agents/skills/builtin/docs.js +23 -13
  11. package/dist/agents/skills/builtin/docs.js.map +1 -1
  12. package/dist/agents/skills/builtin/git-ops.js +3 -3
  13. package/dist/agents/skills/builtin/git-ops.js.map +1 -1
  14. package/dist/agents/skills/builtin/planning.js +4 -4
  15. package/dist/agents/skills/builtin/planning.js.map +1 -1
  16. package/dist/agents/skills/builtin/refactoring.js +3 -3
  17. package/dist/agents/skills/builtin/refactoring.js.map +1 -1
  18. package/dist/agents/skills/builtin/review.js +9 -9
  19. package/dist/agents/skills/builtin/review.js.map +1 -1
  20. package/dist/agents/skills/builtin/testing.js +3 -3
  21. package/dist/agents/skills/builtin/testing.js.map +1 -1
  22. package/dist/agents/tools.js +2 -9
  23. package/dist/agents/tools.js.map +1 -1
  24. package/dist/cli/app.js +63 -29
  25. package/dist/cli/app.js.map +1 -1
  26. package/dist/cli/components/ChatStream.js +40 -126
  27. package/dist/cli/components/ChatStream.js.map +1 -1
  28. package/dist/cli/components/CollapsibleToolOutput.js +8 -26
  29. package/dist/cli/components/CollapsibleToolOutput.js.map +1 -1
  30. package/dist/cli/components/Header.js +6 -18
  31. package/dist/cli/components/Header.js.map +1 -1
  32. package/dist/cli/components/LiveRegion.js +54 -0
  33. package/dist/cli/components/LiveRegion.js.map +1 -0
  34. package/dist/cli/components/Sidebar.js +33 -31
  35. package/dist/cli/components/Sidebar.js.map +1 -1
  36. package/dist/cli/components/StatusBar.js +42 -0
  37. package/dist/cli/components/StatusBar.js.map +1 -0
  38. package/dist/cli/components/ToolOutput.js +68 -0
  39. package/dist/cli/components/ToolOutput.js.map +1 -0
  40. package/dist/cli/components/toolFormat.js +195 -0
  41. package/dist/cli/components/toolFormat.js.map +1 -0
  42. package/dist/cli/grokOAuth.js +93 -75
  43. package/dist/cli/grokOAuth.js.map +1 -1
  44. package/dist/cli/hooks/chatState.js +139 -0
  45. package/dist/cli/hooks/chatState.js.map +1 -0
  46. package/dist/cli/hooks/eventsToMessages.js +1 -1
  47. package/dist/cli/hooks/eventsToMessages.js.map +1 -1
  48. package/dist/cli/hooks/messageHelpers.js +11 -4
  49. package/dist/cli/hooks/messageHelpers.js.map +1 -1
  50. package/dist/cli/hooks/useBatchedMessages.js +14 -10
  51. package/dist/cli/hooks/useBatchedMessages.js.map +1 -1
  52. package/dist/cli/hooks/useChatTurn.js +400 -87
  53. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  54. package/dist/cli/hooks/useSession.js +23 -11
  55. package/dist/cli/hooks/useSession.js.map +1 -1
  56. package/dist/cli/hooks/useSlashDispatch.js +9 -1
  57. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  58. package/dist/cli/main.bundled.js +16972 -15277
  59. package/dist/cli/main.bundled.js.map +4 -4
  60. package/dist/cli/main.js +35 -1
  61. package/dist/cli/main.js.map +1 -1
  62. package/dist/cli/mcp/mcpClient.js +152 -0
  63. package/dist/cli/mcp/mcpClient.js.map +1 -0
  64. package/dist/cli/mcp/mcpManager.js +139 -0
  65. package/dist/cli/mcp/mcpManager.js.map +1 -0
  66. package/dist/cli/refreshRegistry.js +9 -6
  67. package/dist/cli/refreshRegistry.js.map +1 -1
  68. package/dist/cli/skillsMd.js +155 -0
  69. package/dist/cli/skillsMd.js.map +1 -0
  70. package/dist/cli/slashHandlers/skills.js +16 -0
  71. package/dist/cli/slashHandlers/skills.js.map +1 -1
  72. package/dist/cli/toolRegistry.js +31 -0
  73. package/dist/cli/toolRegistry.js.map +1 -1
  74. package/dist/cli/wizard/index.js +1 -1
  75. package/dist/cli/workspace/paths.js +20 -13
  76. package/dist/cli/workspace/paths.js.map +1 -1
  77. package/dist/cli/workspace/storage.js +7 -3
  78. package/dist/cli/workspace/storage.js.map +1 -1
  79. package/dist/cli/workspace/stubs.js +122 -44
  80. package/dist/cli/workspace/stubs.js.map +1 -1
  81. package/dist/cli/workspace/workspaceSummary.js +255 -0
  82. package/dist/cli/workspace/workspaceSummary.js.map +1 -0
  83. package/dist/main/core/AgentHarness.js +36 -7
  84. package/dist/main/core/AgentHarness.js.map +1 -1
  85. package/dist/main/core/tools/builtin/listFiles.js +43 -5
  86. package/dist/main/core/tools/builtin/listFiles.js.map +1 -1
  87. package/dist/main/core/tools/builtin/search.js +28 -120
  88. package/dist/main/core/tools/builtin/search.js.map +1 -1
  89. package/package.json +2 -2
  90. package/dist/cli/oauthCallbackServer.js +0 -207
  91. package/dist/cli/oauthCallbackServer.js.map +0 -1
  92. package/dist/main/core/tools/builtin/_walk.js +0 -129
  93. package/dist/main/core/tools/builtin/_walk.js.map +0 -1
  94. package/dist/main/core/tools/builtin/diff.js +0 -477
  95. package/dist/main/core/tools/builtin/diff.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ChatStream.js","sourceRoot":"","sources":["../../../src/cli/components/ChatStream.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AA8CnE;;;;;;;;;GASG;AACH,SAAS,qBAAqB,CAAC,CAAc,EAAE,KAAa;IAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtB,kEAAkE;QAClE,0DAA0D;QAC1D,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACrE,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;YACvC,oEAAoE;YACpE,2EAA2E;YAC3E,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;YAC7C,IAAI,IAAI,CAAC,CAAC;YACV,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5C,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,oCAAoC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgC,EAChC,MAAc,EACd,KAAa;IAEb,MAAM,eAAe,GAAkB,EAAE,CAAC;IAC1C,IAAI,eAAe,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,0BAA0B;IAE5D,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,OAAO,GAAG,qBAAqB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAChD,IAAI,eAAe,GAAG,OAAO,IAAI,CAAC,EAAE,CAAC;YACnC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC3B,eAAe,IAAI,OAAO,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,+DAA+D;YAC/D,4DAA4D;YAC5D,8DAA8D;YAC9D,6DAA6D;YAC7D,sDAAsD;YACtD,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACtB,sDAAsD;gBACtD,6DAA6D;gBAC7D,qDAAqD;gBACrD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBACzC,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;gBACrE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC;gBACnE,IAAI,WAAW,IAAI,eAAe,EAAE,CAAC;oBACnC,0DAA0D;oBAC1D,kDAAkD;oBAClD,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,GAAG,CAAC,CAAC;oBAC9C,KAAK,KAAK,CAAC;oBACX,eAAe,CAAC,OAAO,CAAC,SAAwB,CAAC,CAAC;gBACpD,CAAC;gBACD,6DAA6D;gBAC7D,+CAA+C;YACjD,CAAC;iBAAM,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBACzC,MAAM,WAAW,GAAG,eAAe,GAAG,CAAC,CAAC,CAAC,yBAAyB;gBAClE,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACpC,MAAM,cAAc,GAAa,EAAE,CAAC;gBACpC,IAAI,WAAW,GAAG,CAAC,CAAC;gBACpB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;oBACjE,IAAI,WAAW,GAAG,QAAQ,IAAI,WAAW,EAAE,CAAC;wBAC1C,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;wBAC7B,WAAW,IAAI,QAAQ,CAAC;oBAC1B,CAAC;yBAAM,CAAC;wBACN,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;wBAC1C,MAAM;oBACR,CAAC;gBACH,CAAC;gBACD,eAAe,CAAC,OAAO,CAAC;oBACtB,GAAG,CAAC;oBACJ,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;iBACnC,CAAC,CAAC;YACL,CAAC;YACD,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAS,cAAc,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAmB;IAClE,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CACnC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAClD,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAC1B,CAAC;IAEF,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAC,QAAQ,IACpF,eAAe,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAC9B,oBAAC,IAAI,IAAC,QAAQ,qEAAsE,CACrF,CAAC,CAAC,CAAC,CACF,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CACL,oBAAC,qBAAqB,IACpB,GAAG,EAAE,CAAC,CAAC,EAAE,EACT,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,MAAM,EAC9B,OAAO,EAAE,CAAC,CAAC,OAAO,EAClB,IAAI,EAAE,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,EAC/B,EAAE,EAAE,CAAC,CAAC,MAAM,EACZ,UAAU,EAAE,CAAC,CAAC,cAAc,EAC5B,eAAe,EAAE,CAAC,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,GACrD,CACH,CAAC;QACJ,CAAC;QACD,OAAO,CACL,oBAAC,GAAG,IAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,aAAa,EAAC,QAAQ,EAAC,YAAY,EAAE,CAAC;YACpD,oBAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI;gBACxF,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;;gBAAG,CAAC,CAAC,IAAI;gBACrE,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CACxC,oBAAC,IAAI,IAAC,KAAK,EAAC,SAAS;;oBAAK,CAAC,CAAC,UAAU,CAAQ,CAC/C,CAAC,CAAC,CAAC,IAAI,CACH;YACP,oBAAC,GAAG,IAAC,UAAU,EAAE,CAAC;gBAChB,oBAAC,IAAI,QAAE,CAAC,CAAC,OAAO,CAAQ,CACpB,CACF,CACP,CAAC;IACJ,CAAC,CAAC,CACH,CACG,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"ChatStream.js","sourceRoot":"","sources":["../../../src/cli/components/ChatStream.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAwC7C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAAC,CAAc,EAAE,IAAI,GAAG,KAAK;IACxD,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CACL,oBAAC,UAAU,IACT,GAAG,EAAE,CAAC,CAAC,EAAE,EACT,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,MAAM,EAC9B,OAAO,EAAE,CAAC,CAAC,OAAO,EAClB,IAAI,EAAE,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,EAC/B,EAAE,EAAE,CAAC,CAAC,MAAM,EACZ,UAAU,EAAE,CAAC,CAAC,cAAc,EAC5B,IAAI,EAAE,IAAI,GACV,CACH,CAAC;IACJ,CAAC;IACD,OAAO,CACL,oBAAC,GAAG,IAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,aAAa,EAAC,QAAQ,EAAC,YAAY,EAAE,CAAC;QACpD,oBAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI;YACxF,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;;YAAG,CAAC,CAAC,IAAI;YACrE,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CACxC,oBAAC,IAAI,IAAC,KAAK,EAAC,SAAS;;gBAAK,CAAC,CAAC,UAAU,CAAQ,CAC/C,CAAC,CAAC,CAAC,IAAI,CACH;QACP,oBAAC,GAAG,IAAC,UAAU,EAAE,CAAC;YAChB,oBAAC,IAAI,QAAE,CAAC,CAAC,OAAO,CAAQ,CACpB,CACF,CACP,CAAC;AACJ,CAAC;AAiBD,SAAS,cAAc,CAAC,EAAE,QAAQ,EAAmB;IACnD,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IACjD,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACvB,oBAAC,IAAI,IAAC,QAAQ,qEAAsE,CACrF,CAAC,CAAC,CAAC,CACF,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CACtC,CACG,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AAErD,8EAA8E;AAC9E,4EAA4E;AAC5E,0EAA0E;AAC1E,8EAA8E;AAC9E,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgC,EAChC,OAAe,EACf,MAAc;IAEd,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvB,CAAC"}
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  function borderColor(toolName, ok) {
4
4
  if (ok === false)
@@ -12,26 +12,13 @@ function borderColor(toolName, ok) {
12
12
  return 'red';
13
13
  return 'cyan';
14
14
  }
15
- function CollapsibleToolOutputImpl(props) {
15
+ export function CollapsibleToolOutput(props) {
16
16
  const { toolName, summary, body, durationMs, ok, defaultExpanded = false } = props;
17
17
  const isControlled = typeof props.expanded === 'boolean';
18
18
  const [localExpanded, setLocalExpanded] = useState(defaultExpanded);
19
- // Sync uncontrolled state when the controlled prop is undefined and
20
- // defaultExpanded changes post-mount (rare but possible on session
21
- // resume, where the restored message carries defaultExpanded=true).
22
- useEffect(() => {
23
- if (!isControlled)
24
- setLocalExpanded(defaultExpanded);
25
- // eslint-disable-next-line react-hooks/exhaustive-deps
26
- }, [defaultExpanded, isControlled]);
27
19
  const expanded = isControlled ? props.expanded : localExpanded;
28
20
  const color = borderColor(toolName, ok);
29
- // Status glyph: pending (no verdict yet) / ok / error. Check `ok` first
30
- // so undefined-with-defined-duration (rare but possible) shows ⋯ not ✓.
31
- // v0.6.2 audit LOW-4.
32
- const status = ok === true ? '✓' : ok === false ? '✗' : '⋯';
33
21
  const summaryLine = [
34
- status,
35
22
  `[${toolName}]`,
36
23
  summary,
37
24
  durationMs !== undefined ? `(${durationMs}ms)` : '',
@@ -40,18 +27,13 @@ function CollapsibleToolOutputImpl(props) {
40
27
  return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
41
28
  React.createElement(Box, null,
42
29
  React.createElement(Text, { color: color }, summaryLine)),
43
- expanded && (React.createElement(Box, { flexDirection: "column", marginLeft: 2, borderStyle: "single", borderColor: color, paddingX: 1 },
44
- React.createElement(Text, { dimColor: ok === false }, body)))));
30
+ expanded && (React.createElement(Box, { flexDirection: "column", marginLeft: 2, borderStyle: "single", borderColor: color, paddingX: 1 }, body.split('\n').map((line, idx) => (
31
+ // B12 fix index keys caused "Encountered two children with the
32
+ // same key" warnings when adjacent lines were identical (e.g.
33
+ // empty separators in multi-line tool output). Use line content
34
+ // with an idx fallback for empty lines.
35
+ React.createElement(Text, { key: line || `__blank-${idx}`, dimColor: ok === false }, line)))))));
45
36
  }
46
- export const CollapsibleToolOutput = React.memo(CollapsibleToolOutputImpl, (prev, next) => {
47
- return (prev.toolName === next.toolName &&
48
- prev.summary === next.summary &&
49
- prev.body === next.body &&
50
- prev.durationMs === next.durationMs &&
51
- prev.ok === next.ok &&
52
- prev.expanded === next.expanded &&
53
- prev.defaultExpanded === next.defaultExpanded);
54
- });
55
37
  /**
56
38
  * Pure helper: classify tool color (exported for unit tests).
57
39
  */
@@ -1 +1 @@
1
- {"version":3,"file":"CollapsibleToolOutput.js","sourceRoot":"","sources":["../../../src/cli/components/CollapsibleToolOutput.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CollapsibleToolOutput.js","sourceRoot":"","sources":["../../../src/cli/components/CollapsibleToolOutput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAqChC,SAAS,WAAW,CAAC,QAAgB,EAAE,EAAY;IACjD,IAAI,EAAE,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,OAAO,CAAC;IACpH,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvE,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5E,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAiC;IACrE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,eAAe,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC;IACnF,MAAM,YAAY,GAAG,OAAO,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC;IACzD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAE,KAAK,CAAC,QAAoB,CAAC,CAAC,CAAC,aAAa,CAAC;IAC5E,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAExC,MAAM,WAAW,GAAG;QAClB,IAAI,QAAQ,GAAG;QACf,OAAO;QACP,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,UAAU,KAAK,CAAC,CAAC,CAAC,EAAE;QACnD,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;KACrB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE5B,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC;QACvC,oBAAC,GAAG;YACF,oBAAC,IAAI,IAAC,KAAK,EAAE,KAAK,IAAG,WAAW,CAAQ,CACpC;QACL,QAAQ,IAAI,CACX,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,EAAE,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,IAC5F,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;QACnC,iEAAiE;QACjE,8DAA8D;QAC9D,gEAAgE;QAChE,wCAAwC;QACxC,oBAAC,IAAI,IAAC,GAAG,EAAE,IAAI,IAAI,WAAW,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE,KAAK,KAAK,IAAG,IAAI,CAAQ,CAC3E,CAAC,CACE,CACP,CACG,CACP,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,EAAY;IAC9D,OAAO,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"}
@@ -1,14 +1,10 @@
1
- import React from 'react';
1
+ import React, { memo } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { formatCost, formatTokens } from '../modelPricing.js';
4
4
  /**
5
- * Header barmodel, provider, skill count, session id, cost, tokens.
6
- *
7
- * Performance: wrapped in React.memo with a custom comparator. The Header
8
- * re-renders only when one of its primitive props actually changes. This is
9
- * critical because the parent App re-renders on EVERY streaming token delta
10
- * (≈20-50/sec during LLM response); without memo the Header would redraw its
11
- * border 50×/sec, causing visible flicker on the top edge of the terminal.
5
+ * B11 fixwrapped in React.memo so the header doesn't re-render on every
6
+ * input keystroke or streaming delta. It only re-renders when its props
7
+ * (model, provider, cost, token count, etc.) actually change.
12
8
  */
13
9
  function HeaderImpl({ model, provider, skillCount, sessionActive, sessionId, totalTokens, totalCostUsd }) {
14
10
  const hasCost = typeof totalCostUsd === 'number' && totalCostUsd > 0;
@@ -37,14 +33,6 @@ function HeaderImpl({ model, provider, skillCount, sessionActive, sessionId, tot
37
33
  tokStr,
38
34
  " tok")));
39
35
  }
40
- export const Header = React.memo(HeaderImpl, (prev, next) => {
41
- // Custom shallow-equal for primitives — avoids Object.is on every render.
42
- return (prev.model === next.model &&
43
- prev.provider === next.provider &&
44
- prev.skillCount === next.skillCount &&
45
- prev.sessionActive === next.sessionActive &&
46
- prev.sessionId === next.sessionId &&
47
- prev.totalTokens === next.totalTokens &&
48
- prev.totalCostUsd === next.totalCostUsd);
49
- });
36
+ /** B11 memoized export. Shallow equality suffices (all props are primitives). */
37
+ export const Header = memo(HeaderImpl);
50
38
  //# sourceMappingURL=Header.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Header.js","sourceRoot":"","sources":["../../../src/cli/components/Header.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAc9D;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAe;IACnH,MAAM,OAAO,GAAG,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,GAAG,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAC9C,OAAO,CACL,oBAAC,GAAG,IAAC,WAAW,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAC,KAAK;QAC1E,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,kBAAmB;QAC1C,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,QAAE,KAAK,CAAQ;QACpB,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,QAAE,QAAQ,CAAQ;QACvB,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI;YAAE,UAAU;sBAAe;QAChC,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,IAC1C,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CACjC;QACP,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,QAAQ;;YAAU,SAAS,CAAQ;QACzC,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAG,OAAO,CAAQ;QAC1D,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;YAAE,MAAM;mBAAY,CAClC,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;IAC1D,0EAA0E;IAC1E,OAAO,CACL,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;QACzB,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ;QAC/B,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU;QACnC,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa;QACzC,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS;QACjC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW;QACrC,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,YAAY,CACxC,CAAC;AACJ,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"Header.js","sourceRoot":"","sources":["../../../src/cli/components/Header.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAc9D;;;;GAIG;AACH,SAAS,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAe;IACnH,MAAM,OAAO,GAAG,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,GAAG,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAC9C,OAAO,CACL,oBAAC,GAAG,IAAC,WAAW,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAC,KAAK;QAC1E,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,kBAAmB;QAC1C,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,QAAE,KAAK,CAAQ;QACpB,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,QAAE,QAAQ,CAAQ;QACvB,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI;YAAE,UAAU;sBAAe;QAChC,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,IAC1C,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CACjC;QACP,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,QAAQ;;YAAU,SAAS,CAAQ;QACzC,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAG,OAAO,CAAQ;QAC1D,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;YAAE,MAAM;mBAAY,CAClC,CACP,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC"}
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import { renderMessage } from './ChatStream.js';
4
+ /**
5
+ * Max lines of the streaming bubble kept in the dynamic region. The full
6
+ * text is never lost — it lands complete in `<Static>` at finalize. This
7
+ * clamp only bounds the live repaint footprint so the dynamic region fits
8
+ * any terminal height, eliminating full-screen repaints by construction.
9
+ */
10
+ const LIVE_STREAM_TAIL_LINES = 10;
11
+ /**
12
+ * LiveRegion — the small dynamic region Ink repaints every frame.
13
+ *
14
+ * Contains:
15
+ * - the currently-streaming assistant bubble (clamped to the last
16
+ * `LIVE_STREAM_TAIL_LINES` lines so the region stays short), and
17
+ * - one `⋯ [tool] summary` line per pending tool invocation.
18
+ *
19
+ * Because this region is always at most ~10 + N lines tall, it can never
20
+ * exceed the terminal height → Ink never falls back to clear-screen + full
21
+ * repaint → no flicker, by construction. Finalized messages live in the
22
+ * terminal's native scrollback via `<Static>` (printed exactly once).
23
+ */
24
+ export function LiveRegion({ live, busy }) {
25
+ const { streaming, runningTools } = live;
26
+ if (!streaming && runningTools.length === 0)
27
+ return null;
28
+ return (React.createElement(Box, { flexDirection: "column", paddingX: 1 },
29
+ streaming && streaming.role === 'assistant' && (React.createElement(StreamingTail, { id: streaming.id, content: streaming.content, ts: streaming.ts, memberName: streaming.memberName, memberId: streaming.memberId })),
30
+ runningTools.map((t) => (React.createElement(Box, { key: t.id, flexDirection: "column" }, renderMessage(t, true)))),
31
+ busy && runningTools.length === 0 && !streaming && (React.createElement(Text, { dimColor: true }, " \u22EF working\u2026"))));
32
+ }
33
+ /**
34
+ * Render the streaming bubble, clamped to the last N content lines. A leading
35
+ * `…` marker is shown when the content was truncated so the user knows there
36
+ * is more above (in the scrollback once finalized). The bubble uses the same
37
+ * header style as `renderMessage` so it is visually continuous when it
38
+ * crosses from live → finalized.
39
+ */
40
+ function StreamingTail(m) {
41
+ const lines = m.content.split('\n');
42
+ const truncated = lines.length > LIVE_STREAM_TAIL_LINES;
43
+ const tail = truncated ? lines.slice(-LIVE_STREAM_TAIL_LINES) : lines;
44
+ return (React.createElement(Box, { key: m.id, flexDirection: "column", marginBottom: 1 },
45
+ React.createElement(Text, { color: "green", bold: true },
46
+ "\u25C6 assistant",
47
+ m.memberName ? React.createElement(Text, { color: "magenta" },
48
+ " \u00B7 ",
49
+ m.memberName) : null),
50
+ React.createElement(Box, { marginLeft: 2, flexDirection: "column" },
51
+ truncated && React.createElement(Text, { dimColor: true }, "\u2026"),
52
+ React.createElement(Text, null, tail.join('\n')))));
53
+ }
54
+ //# sourceMappingURL=LiveRegion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LiveRegion.js","sourceRoot":"","sources":["../../../src/cli/components/LiveRegion.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAOlC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAmB;IACxD,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAEzC,IAAI,CAAC,SAAS,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzD,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC;QACpC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,WAAW,IAAI,CAC9C,oBAAC,aAAa,IACZ,EAAE,EAAE,SAAS,CAAC,EAAE,EAChB,OAAO,EAAE,SAAS,CAAC,OAAO,EAC1B,EAAE,EAAE,SAAS,CAAC,EAAE,EAChB,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,QAAQ,EAAE,SAAS,CAAC,QAAQ,GAC5B,CACH;QACA,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACvB,oBAAC,GAAG,IAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,aAAa,EAAC,QAAQ,IACnC,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,CACnB,CACP,CAAC;QACD,IAAI,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAClD,oBAAC,IAAI,IAAC,QAAQ,kCAAmB,CAClC,CACG,CACP,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,CAMtB;IACC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,sBAAsB,CAAC;IACxD,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACtE,OAAO,CACL,oBAAC,GAAG,IAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,aAAa,EAAC,QAAQ,EAAC,YAAY,EAAE,CAAC;QACpD,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI;;YAErB,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,oBAAC,IAAI,IAAC,KAAK,EAAC,SAAS;;gBAAK,CAAC,CAAC,UAAU,CAAQ,CAAC,CAAC,CAAC,IAAI,CAChE;QACP,oBAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ;YACvC,SAAS,IAAI,oBAAC,IAAI,IAAC,QAAQ,mBAAS;YACrC,oBAAC,IAAI,QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAQ,CAC1B,CACF,CACP,CAAC;AACJ,CAAC"}
@@ -1,44 +1,46 @@
1
- import React from 'react';
1
+ import React, { memo, useId } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  /**
4
4
  * Sidebar showing: formatted skill list (from formatSkillList), session count,
5
5
  * and an indicator when the user is typing a slash command.
6
6
  *
7
- * Performance: React.memo with custom comparator. sessionCount changes only on
8
- * message append, not on every streaming delta but React.memo's default
9
- * shallow equal still triggers re-render because the parent passes a fresh
10
- * primitive int each render. The custom comparator avoids that. Sidebar also
11
- * rebuilds its visibleSkills slice via useMemo to avoid re-splitting on every
12
- * parent render.
7
+ * B11 fix — wrapped in React.memo so the sidebar doesn't re-render on every
8
+ * App re-render (e.g. every input keystroke or streaming delta). It only
9
+ * re-renders when its props actually change.
10
+ *
11
+ * B20 fix width is now a prop, scaled to the terminal columns instead of
12
+ * being hardcoded to 40. On narrow terminals the sidebar shrinks so it
13
+ * doesn't overlap the chat stream.
13
14
  */
14
- function SidebarImpl({ skillList, sessionCount, isSlashMode, height }) {
15
- // Truncate visible skills list to fit the available height. useMemo so we
16
- // don't re-split on every render (e.g. during streaming token deltas).
17
- const { visibleLines, truncated } = React.useMemo(() => {
18
- const lines = skillList.split('\n');
19
- const headerLines = 3 + (isSlashMode ? 1 : 0);
20
- const maxSkillLines = Math.max(2, height - headerLines - 3); // 3 for border + padding
21
- return {
22
- visibleLines: lines.slice(0, maxSkillLines),
23
- truncated: lines.length > maxSkillLines,
24
- };
25
- }, [skillList, isSlashMode, height]);
26
- return (React.createElement(Box, { flexDirection: "column", width: 40, height: height, borderStyle: "single", borderColor: "gray", paddingX: 1 },
15
+ function SidebarImpl({ skillList, sessionCount, isSlashMode, height, width }) {
16
+ // Stable per-instance prefix to guarantee unique keys across renders and
17
+ // across sibling Sidebar instances (shouldn't exist today, but defensive).
18
+ const uidPrefix = useId();
19
+ const lines = skillList.split('\n');
20
+ const headerLines = 3 + (isSlashMode ? 1 : 0);
21
+ const maxSkillLines = Math.max(2, height - headerLines - 3); // 3 for border + padding
22
+ const visibleSkillLines = lines.slice(0, maxSkillLines);
23
+ const showTruncatedIndicator = lines.length > maxSkillLines;
24
+ if (showTruncatedIndicator) {
25
+ visibleSkillLines.push(' ... (more in /skills)');
26
+ }
27
+ return (React.createElement(Box, { flexDirection: "column", width: width, height: height, borderStyle: "single", borderColor: "gray", paddingX: 1 },
27
28
  React.createElement(Text, { bold: true, color: "cyan" }, "Skills & sessions"),
28
29
  React.createElement(Text, { dimColor: true },
29
30
  "Sessions: ",
30
31
  sessionCount),
31
32
  isSlashMode && React.createElement(Text, { color: "yellow" }, "\u2328 slash command mode"),
32
- React.createElement(Box, { marginTop: 1, flexDirection: "column" },
33
- visibleLines.map((line, idx) => (
34
- // key={idx} is OK here: order is stable, content is what changes.
35
- React.createElement(Text, { key: idx }, line))),
36
- truncated && React.createElement(Text, { dimColor: true }, " ... (more in /skills)"))));
33
+ React.createElement(Box, { marginTop: 1, flexDirection: "column" }, visibleSkillLines.map((line, idx) => (
34
+ // B12 fix — combine React's useId() with idx + line content for a
35
+ // guaranteed-unique key. formatSkillList can legitimately emit
36
+ // multiple blank lines or duplicate entries (e.g. the
37
+ // "... (more in /skills)" footer when the list is short), which
38
+ // would otherwise collide when used as a key directly.
39
+ React.createElement(Text, { key: `${uidPrefix}-${idx}:${line}` }, line))))));
37
40
  }
38
- export const Sidebar = React.memo(SidebarImpl, (prev, next) => {
39
- return (prev.skillList === next.skillList &&
40
- prev.sessionCount === next.sessionCount &&
41
- prev.isSlashMode === next.isSlashMode &&
42
- prev.height === next.height);
43
- });
41
+ /**
42
+ * B11 — memoized export. Custom equality is shallow; props are primitives
43
+ * (strings + numbers + booleans) so React's default shallow compare is fine.
44
+ */
45
+ export const Sidebar = memo(SidebarImpl);
44
46
  //# sourceMappingURL=Sidebar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.js","sourceRoot":"","sources":["../../../src/cli/components/Sidebar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAShC;;;;;;;;;;GAUG;AACH,SAAS,WAAW,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAgB;IACjF,0EAA0E;IAC1E,uEAAuE;IACvE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,yBAAyB;QACtF,OAAO;YACL,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC;YAC3C,SAAS,EAAE,KAAK,CAAC,MAAM,GAAG,aAAa;SACxC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAErC,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAC,MAAM,EAAC,QAAQ,EAAE,CAAC;QACxG,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,wBAA6B;QACpD,oBAAC,IAAI,IAAC,QAAQ;;YAAY,YAAY,CAAQ;QAC7C,WAAW,IAAI,oBAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,gCAA4B;QAChE,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ;YACtC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;YAC/B,kEAAkE;YAClE,oBAAC,IAAI,IAAC,GAAG,EAAE,GAAG,IAAG,IAAI,CAAQ,CAC9B,CAAC;YACD,SAAS,IAAI,oBAAC,IAAI,IAAC,QAAQ,oCAA+B,CACvD,CACF,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;IAC5D,OAAO,CACL,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS;QACjC,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,YAAY;QACvC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW;QACrC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAC5B,CAAC;AACJ,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"Sidebar.js","sourceRoot":"","sources":["../../../src/cli/components/Sidebar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAUhC;;;;;;;;;;;GAWG;AACH,SAAS,WAAW,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAgB;IACxF,yEAAyE;IACzE,2EAA2E;IAC3E,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,yBAAyB;IACtF,MAAM,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IACxD,MAAM,sBAAsB,GAAG,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC;IAC5D,IAAI,sBAAsB,EAAE,CAAC;QAC3B,iBAAiB,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAC,MAAM,EAAC,QAAQ,EAAE,CAAC;QAC3G,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,wBAA6B;QACpD,oBAAC,IAAI,IAAC,QAAQ;;YAAY,YAAY,CAAQ;QAC7C,WAAW,IAAI,oBAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,gCAA4B;QAChE,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,IACtC,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;QACpC,kEAAkE;QAClE,+DAA+D;QAC/D,sDAAsD;QACtD,gEAAgE;QAChE,uDAAuD;QACvD,oBAAC,IAAI,IAAC,GAAG,EAAE,GAAG,SAAS,IAAI,GAAG,IAAI,IAAI,EAAE,IAAG,IAAI,CAAQ,CACxD,CAAC,CACE,CACF,CACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC"}
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import { formatCost, formatTokens } from '../modelPricing.js';
4
+ /**
5
+ * StatusBar — single one-line status bar replacing the persistent v0.6
6
+ * `Header`. It lives in the dynamic region (repainted by Ink) and condenses:
7
+ * provider · model · session · tokens · cost · queue · busy.
8
+ *
9
+ * Why one line: the static-scrollback model (v0.7.0) needs the dynamic
10
+ * region as short as possible. A single status line + the input bar + the
11
+ * streaming tail is always well under a screen, so no full repaint.
12
+ */
13
+ export function StatusBar({ model, provider, sessionId, sessionActive, totalTokens, totalCostUsd, queueCount = 0, busy = false, }) {
14
+ const hasCost = typeof totalCostUsd === 'number' && totalCostUsd > 0;
15
+ const costStr = hasCost ? formatCost(totalCostUsd) : '$0.0000';
16
+ const tokStr = formatTokens(totalTokens ?? 0);
17
+ return (React.createElement(Box, { paddingX: 1 },
18
+ React.createElement(Text, { color: sessionActive ? 'green' : 'gray' }, sessionActive ? '●' : '○'),
19
+ React.createElement(Text, { dimColor: true }, " "),
20
+ React.createElement(Text, { bold: true, color: "cyan" }, provider),
21
+ React.createElement(Text, { dimColor: true }, " \u00B7 "),
22
+ React.createElement(Text, null, model),
23
+ React.createElement(Text, { dimColor: true }, " \u00B7 "),
24
+ React.createElement(Text, { dimColor: true },
25
+ "session ",
26
+ sessionId),
27
+ React.createElement(Text, { dimColor: true }, " \u00B7 "),
28
+ React.createElement(Text, { color: "gray" },
29
+ tokStr,
30
+ " tok"),
31
+ React.createElement(Text, { dimColor: true }, " \u00B7 "),
32
+ React.createElement(Text, { color: hasCost ? 'yellow' : 'gray' }, costStr),
33
+ queueCount > 0 ? (React.createElement(React.Fragment, null,
34
+ React.createElement(Text, { dimColor: true }, " \u00B7 "),
35
+ React.createElement(Text, { color: "magenta" },
36
+ "queue ",
37
+ queueCount))) : null,
38
+ busy ? (React.createElement(React.Fragment, null,
39
+ React.createElement(Text, { dimColor: true }, " \u00B7 "),
40
+ React.createElement(Text, { color: "yellow" }, "working"))) : null));
41
+ }
42
+ //# sourceMappingURL=StatusBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../../src/cli/components/StatusBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAa9D;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,GAAG,CAAC,EACd,IAAI,GAAG,KAAK,GACG;IACf,MAAM,OAAO,GAAG,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,GAAG,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAC9C,OAAO,CACL,oBAAC,GAAG,IAAC,QAAQ,EAAE,CAAC;QACd,oBAAC,IAAI,IAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,IAC1C,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CACrB;QACP,oBAAC,IAAI,IAAC,QAAQ,cAAS;QACvB,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,IAAE,QAAQ,CAAQ;QACzC,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,QAAE,KAAK,CAAQ;QACpB,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,QAAQ;;YAAU,SAAS,CAAQ;QACzC,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;YAAE,MAAM;mBAAY;QACtC,oBAAC,IAAI,IAAC,QAAQ,qBAAW;QACzB,oBAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAG,OAAO,CAAQ;QACzD,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAChB;YACE,oBAAC,IAAI,IAAC,QAAQ,qBAAW;YACzB,oBAAC,IAAI,IAAC,KAAK,EAAC,SAAS;;gBAAQ,UAAU,CAAQ,CAC9C,CACJ,CAAC,CAAC,CAAC,IAAI;QACP,IAAI,CAAC,CAAC,CAAC,CACN;YACE,oBAAC,IAAI,IAAC,QAAQ,qBAAW;YACzB,oBAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,cAAe,CAClC,CACJ,CAAC,CAAC,CAAC,IAAI,CACJ,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import { useStdout } from 'ink';
4
+ import { formatToolResult } from './toolFormat.js';
5
+ function borderColor(toolName, ok) {
6
+ if (ok === false)
7
+ return 'red';
8
+ const lower = toolName.toLowerCase();
9
+ if (lower.includes('read') || lower === 'cat' || lower.includes('grep') || lower.includes('search'))
10
+ return 'green';
11
+ if (lower.includes('write') || lower.includes('edit'))
12
+ return 'yellow';
13
+ if (lower === 'bash' || lower === 'shell' || lower === 'exec')
14
+ return 'red';
15
+ return 'cyan';
16
+ }
17
+ function ToolOutputImpl(props) {
18
+ const { toolName, summary, body, ok, durationMs, live = false } = props;
19
+ const color = borderColor(toolName, ok);
20
+ const { stdout } = useStdout();
21
+ // B3: clamp bordered box width so boxes don't stretch to full terminal and
22
+ // produce the mixed-width wall seen in v0.7.0.
23
+ const termWidth = stdout?.columns ?? 80;
24
+ const boxWidth = Math.min(Math.max(40, termWidth - 6), 100);
25
+ // Pending (live region): single line, no body, no duration.
26
+ if (live || ok === undefined) {
27
+ const summaryLine = ['⋯', `[${toolName}]`, summary].filter(Boolean).join(' ');
28
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
29
+ React.createElement(Box, null,
30
+ React.createElement(Text, { color: color }, summaryLine))));
31
+ }
32
+ // Finalized — format the body per tool policy (B1).
33
+ const isError = ok === false;
34
+ const formatted = formatToolResult(toolName, body);
35
+ const status = ok ? '✓' : '✗';
36
+ const summaryLine = [
37
+ status,
38
+ `[${toolName}]`,
39
+ summary,
40
+ durationMs !== undefined ? `(${durationMs}ms)` : '',
41
+ ].filter(Boolean).join(' ');
42
+ // B3: one-line results (write_file/edit_file success) print inline — no box.
43
+ if (formatted.oneLine && !isError) {
44
+ const inline = formatted.lines[0] ?? '';
45
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
46
+ React.createElement(Box, null,
47
+ React.createElement(Text, { color: color }, summaryLine)),
48
+ React.createElement(Box, { marginLeft: 2 },
49
+ React.createElement(Text, { dimColor: true }, inline))));
50
+ }
51
+ const bodyText = formatted.lines.join('\n');
52
+ const metaSuffix = formatted.meta ? `\n${formatted.meta}` : '';
53
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
54
+ React.createElement(Box, null,
55
+ React.createElement(Text, { color: color }, summaryLine)),
56
+ React.createElement(Box, { flexDirection: "column", marginLeft: 2, width: boxWidth, borderStyle: "single", borderColor: color, paddingX: 1 },
57
+ React.createElement(Text, { dimColor: isError },
58
+ bodyText,
59
+ metaSuffix))));
60
+ }
61
+ export const ToolOutput = React.memo(ToolOutputImpl);
62
+ /**
63
+ * Pure helper: classify tool color (exported for unit tests).
64
+ */
65
+ export function classifyToolColor(toolName, ok) {
66
+ return borderColor(toolName, ok);
67
+ }
68
+ //# sourceMappingURL=ToolOutput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolOutput.js","sourceRoot":"","sources":["../../../src/cli/components/ToolOutput.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * toolFormat.ts — v0.7.1 pure formatters for tool rendering (plan B1+B2+B3).
3
+ *
4
+ * The v0.6/v0.7.0 renderer dumped the raw JSON result envelope into a bordered
5
+ * box: escaped `\n`, full-terminal-width borders with inconsistent widths per
6
+ * box, and summary lines that were raw JSON args truncated mid-string. These
7
+ * pure functions replace that with human-readable output keyed by tool name.
8
+ *
9
+ * Two formatters:
10
+ * - {@link formatToolSummary}: the one-line summary (replaces raw-JSON args).
11
+ * - {@link formatToolResult}: the multi-line body (replaces the raw envelope).
12
+ *
13
+ * Both are pure (no React, no Ink) so they are trivially unit-testable and
14
+ * usable by any renderer (the current ToolOutput, the Static path, /last-tool).
15
+ *
16
+ * Truncation is line-based (default 8, `ZELARI_TOOL_OUTPUT_LINES`) with a
17
+ * `… (+K lines)` tail, replacing the 600-char mid-string cut that split JSON
18
+ * strings awkwardly.
19
+ */
20
+ import path from 'node:path';
21
+ /** Default line cap for the printed result body. */
22
+ const DEFAULT_TOOL_OUTPUT_LINES = 8;
23
+ function toolOutputLineCap() {
24
+ const raw = process.env.ZELARI_TOOL_OUTPUT_LINES;
25
+ const n = raw ? Number.parseInt(raw, 10) : DEFAULT_TOOL_OUTPUT_LINES;
26
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_TOOL_OUTPUT_LINES;
27
+ }
28
+ /**
29
+ * Best-effort JSON parse. Returns the parsed value or `null` (caller falls
30
+ * back to treating the input as a plain string).
31
+ */
32
+ function tryParseJson(s) {
33
+ try {
34
+ return JSON.parse(s);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ /** Truncate an array of lines to the cap, appending a `… (+K lines)` marker. */
41
+ function truncateLines(lines) {
42
+ const cap = toolOutputLineCap();
43
+ if (lines.length <= cap)
44
+ return lines;
45
+ return [...lines.slice(0, cap), `… (+${lines.length - cap} lines)`];
46
+ }
47
+ /** Human-readable byte size (KB / MB). */
48
+ function formatBytes(n) {
49
+ if (n >= 1024 * 1024)
50
+ return `${(n / (1024 * 1024)).toFixed(1)} MB`;
51
+ if (n >= 1024)
52
+ return `${(n / 1024).toFixed(1)} KB`;
53
+ return `${n} B`;
54
+ }
55
+ /**
56
+ * Format the body of a tool result for display.
57
+ *
58
+ * @param toolName tool name (determines the per-tool shape handling)
59
+ * @param resultStr the raw result string (JSON envelope from the harness, or
60
+ * a plain string for non-JSON tools / errors)
61
+ */
62
+ export function formatToolResult(toolName, resultStr) {
63
+ const lower = toolName.toLowerCase();
64
+ // bash / shell — envelope: { stdout, stderr, exitCode, hint? }
65
+ if (lower === 'bash' || lower === 'shell' || lower === 'exec') {
66
+ const parsed = tryParseJson(resultStr);
67
+ if (parsed && typeof parsed === 'object') {
68
+ const stdout = parsed.stdout ?? '';
69
+ const lines = stdout.length > 0 ? stdout.replace(/\r\n/g, '\n').split('\n') : [];
70
+ const metaParts = [];
71
+ if (parsed.stderr && parsed.stderr.trim().length > 0) {
72
+ metaParts.push(`stderr: ${parsed.stderr.trim().split('\n')[0]}`);
73
+ }
74
+ if (typeof parsed.exitCode === 'number' && parsed.exitCode !== 0) {
75
+ metaParts.push(`exit ${parsed.exitCode}`);
76
+ }
77
+ // v0.7.3: interactive-prompt detection (see shell.ts INTERACTIVE_HINT).
78
+ if (typeof parsed.hint === 'string' && parsed.hint.length > 0) {
79
+ metaParts.push('⚠ interactive prompt cancelled — needs non-interactive flags or manual file creation');
80
+ }
81
+ return {
82
+ lines: truncateLines(lines),
83
+ meta: metaParts.length > 0 ? metaParts.join(' · ') : undefined,
84
+ };
85
+ }
86
+ }
87
+ // read_file — envelope: { path, content, ... }
88
+ if (lower.includes('read') || lower === 'cat') {
89
+ const parsed = tryParseJson(resultStr);
90
+ if (parsed && typeof parsed === 'object' && typeof parsed.content === 'string') {
91
+ return { lines: truncateLines(parsed.content.replace(/\r\n/g, '\n').split('\n')) };
92
+ }
93
+ }
94
+ // write_file / edit_file — success is a single line, no box (B3).
95
+ if (lower.includes('write') || lower.includes('edit')) {
96
+ const parsed = tryParseJson(resultStr);
97
+ if (parsed && typeof parsed === 'object') {
98
+ const p = parsed.path ? rel(parsed.path) : '';
99
+ if (typeof parsed.bytesWritten === 'number') {
100
+ return { lines: [`wrote ${formatBytes(parsed.bytesWritten)} → ${p}`], oneLine: true };
101
+ }
102
+ if (typeof parsed.occurrencesReplaced === 'number') {
103
+ return {
104
+ lines: [`replaced ${parsed.occurrencesReplaced} occurrence(s) in ${p}`],
105
+ oneLine: true,
106
+ };
107
+ }
108
+ }
109
+ }
110
+ // list_files — envelope: { dir, entries: [{name, type}], truncated }
111
+ if (lower.includes('list') || lower.includes('ls')) {
112
+ const parsed = tryParseJson(resultStr);
113
+ if (parsed && Array.isArray(parsed.entries)) {
114
+ const names = parsed.entries.slice(0, 10).map((e) => e.name);
115
+ const head = truncateLines(names);
116
+ const count = parsed.entries.length;
117
+ const dir = parsed.dir ? rel(parsed.dir) : '';
118
+ const metaParts = [];
119
+ if (dir)
120
+ metaParts.push(`${count} entries in ${dir}`);
121
+ else
122
+ metaParts.push(`${count} entries`);
123
+ if (parsed.truncated)
124
+ metaParts.push(`(truncated)`);
125
+ return { lines: head, meta: metaParts.join(' ') };
126
+ }
127
+ }
128
+ // grep_content / search — envelope: { matches/results: [...] }
129
+ if (lower.includes('grep') || lower.includes('search')) {
130
+ const parsed = tryParseJson(resultStr);
131
+ const arr = parsed?.matches ?? parsed?.results;
132
+ if (Array.isArray(arr)) {
133
+ return {
134
+ lines: truncateLines(arr.map((m) => (typeof m === 'string' ? m : JSON.stringify(m)))),
135
+ meta: `${arr.length} match(es)`,
136
+ };
137
+ }
138
+ }
139
+ // Fallback: treat as plain text (split on real newlines; if it's JSON we
140
+ // already failed to match a known shape, so show it verbatim).
141
+ return { lines: truncateLines(resultStr.replace(/\r\n/g, '\n').split('\n')) };
142
+ }
143
+ /** Make a path relative to cwd when possible (shorter display). */
144
+ function rel(p) {
145
+ try {
146
+ const r = path.relative(process.cwd(), p);
147
+ return r && !r.startsWith('..') ? r : p;
148
+ }
149
+ catch {
150
+ return p;
151
+ }
152
+ }
153
+ /**
154
+ * Format the one-line summary for a tool invocation (replaces raw-JSON args).
155
+ *
156
+ * @param toolName tool name
157
+ * @param args the args object/value as received from the model
158
+ * @param maxWidth optional column cap; the summary is truncated (with `…`) to
159
+ * fit so it never wraps mid-token. When omitted, a sane default
160
+ * is applied.
161
+ */
162
+ export function formatToolSummary(toolName, args, maxWidth) {
163
+ const cap = maxWidth && maxWidth > 10 ? maxWidth : 100;
164
+ const lower = toolName.toLowerCase();
165
+ const a = (args && typeof args === 'object' ? args : {});
166
+ let summary;
167
+ if (lower === 'bash' || lower === 'shell' || lower === 'exec') {
168
+ summary = String(a['command'] ?? a['cmd'] ?? '');
169
+ }
170
+ else if (lower.includes('read') || lower === 'cat') {
171
+ summary = typeof a['path'] === 'string' ? rel(a['path']) : '';
172
+ }
173
+ else if (lower.includes('write') || lower.includes('edit')) {
174
+ const p = typeof a['path'] === 'string' ? rel(a['path']) : '';
175
+ summary = p;
176
+ }
177
+ else if (lower.includes('list') || lower.includes('ls')) {
178
+ const dir = typeof a['dir'] === 'string' ? rel(a['dir']) : '.';
179
+ const depth = typeof a['maxDepth'] === 'number' ? ` (depth ${a['maxDepth']})` : '';
180
+ summary = `${dir}${depth}`;
181
+ }
182
+ else if (lower.includes('grep') || lower.includes('search')) {
183
+ const pattern = String(a['pattern'] ?? a['query'] ?? '');
184
+ const p = typeof a['path'] === 'string' ? ` in ${rel(a['path'])}` : '';
185
+ summary = `${pattern}${p}`;
186
+ }
187
+ else {
188
+ // Fallback: compact JSON, but truncated at the column cap (not mid-string
189
+ // like the old slice(0,120) which cut content values arbitrarily).
190
+ const json = JSON.stringify(args) ?? '';
191
+ summary = json;
192
+ }
193
+ return summary.length > cap ? `${summary.slice(0, cap - 1)}…` : summary;
194
+ }
195
+ //# sourceMappingURL=toolFormat.js.map