@astralyn/pi 1.0.1 → 1.0.3

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 (53) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/bundle/chunks/{chunk-4TT2QGFL.js → chunk-N2LEX5CU.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-H2N2THAO.js → chunk-Q7TDQ5LT.js} +20 -14
  4. package/dist/bundle/chunks/{virtual-modules-YFKU7JZF.js → virtual-modules-UUHBXSJB.js} +1 -1
  5. package/dist/bundle/cli.js +1 -1
  6. package/dist/bundle/index.js +1 -1
  7. package/dist/bundle/rpc-entry.js +1 -1
  8. package/dist/core/tasks/runtime.d.ts +1 -0
  9. package/dist/core/tasks/runtime.d.ts.map +1 -1
  10. package/dist/core/tasks/runtime.js +4 -0
  11. package/dist/core/tasks/runtime.js.map +1 -1
  12. package/dist/core/tasks/types.d.ts +2 -0
  13. package/dist/core/tasks/types.d.ts.map +1 -1
  14. package/dist/core/tasks/types.js.map +1 -1
  15. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  16. package/dist/core/tools/renderers/bash.js +4 -3
  17. package/dist/core/tools/renderers/bash.js.map +1 -1
  18. package/dist/core/tools/shell-tool.d.ts.map +1 -1
  19. package/dist/core/tools/shell-tool.js +9 -2
  20. package/dist/core/tools/shell-tool.js.map +1 -1
  21. package/dist/extensions/deepwiki/execute.d.ts +0 -3
  22. package/dist/extensions/deepwiki/execute.d.ts.map +1 -1
  23. package/dist/extensions/deepwiki/execute.js.map +1 -1
  24. package/dist/extensions/deepwiki/render.d.ts.map +1 -1
  25. package/dist/extensions/deepwiki/render.js +0 -5
  26. package/dist/extensions/deepwiki/render.js.map +1 -1
  27. package/dist/extensions/question/render.d.ts.map +1 -1
  28. package/dist/extensions/question/render.js +3 -7
  29. package/dist/extensions/question/render.js.map +1 -1
  30. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  31. package/dist/modes/interactive/interactive-mode.js +19 -1
  32. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  33. package/dist/modes/interactive/tasks/index.d.ts.map +1 -1
  34. package/dist/modes/interactive/tasks/index.js +10 -10
  35. package/dist/modes/interactive/tasks/index.js.map +1 -1
  36. package/dist/modes/interactive/tasks/layout.js +4 -5
  37. package/dist/modes/interactive/tasks/layout.js.map +1 -1
  38. package/dist/modes/interactive/tasks/manager.d.ts.map +1 -1
  39. package/dist/modes/interactive/tasks/manager.js +2 -0
  40. package/dist/modes/interactive/tasks/manager.js.map +1 -1
  41. package/dist/modes/interactive/tool-view/chat.d.ts +2 -3
  42. package/dist/modes/interactive/tool-view/chat.d.ts.map +1 -1
  43. package/dist/modes/interactive/tool-view/chat.js +18 -5
  44. package/dist/modes/interactive/tool-view/chat.js.map +1 -1
  45. package/dist/modes/interactive/tool-view/style.d.ts +2 -1
  46. package/dist/modes/interactive/tool-view/style.d.ts.map +1 -1
  47. package/dist/modes/interactive/tool-view/style.js +2 -2
  48. package/dist/modes/interactive/tool-view/style.js.map +1 -1
  49. package/docs/bundled/tasks.md +5 -3
  50. package/docs/bundled/tool-presentation.md +4 -3
  51. package/docs/extensions.md +1 -0
  52. package/npm-shrinkwrap.json +2 -2
  53. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","sourceRoot":"","sources":["../../../../src/modes/interactive/tool-view/style.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAkB,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAmB,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAuB3D,MAAM,CAAC,MAAM,SAAS,GAAc;IACnC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IACzF,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE;IAClC,GAAG,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;IACpC,SAAS,EAAE,EAAE,UAAU,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE;IACrF,iBAAiB,EAAE,GAAG;CACtB,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,KAA+C;IACzE,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAkB;IACjD,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,SAAS;IACjB,OAAO,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,WAAW;IAC1B,OAAO,SAAS,EAAE,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa,EAAE,KAAiB;IACnD,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;AACxF,CAAC;AAED,sGAAsG;AACtG,SAAS,WAAW,CAAC,IAAY;IAChC,OAAO,IAAI,KAAK,EAAE,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AACrD,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAe;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,GAAG,KAAK,CAAC,MAAM,IAAI,WAAW,CAAC,KAAK,CAAC,KAAK,CAAE,CAAC;QAAE,KAAK,EAAE,CAAC;IACnE,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAK3B,YAAY,SAAoB,EAAE,IAAuB,EAAE,WAA6B;QACvF,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,WAAW,EAAE,CAAC,CAAC,CAAC;QAC3E,+FAA+F;QAC/F,iGAAiG;QACjG,MAAM,KAAK,GACV,IAAI,CAAC,IAAI,KAAK,MAAM;YACnB,CAAC,CAAC,qBAAqB,CAAC,QAAQ,CAAC;YACjC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACpB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,GAAG,qBAAqB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7D,CAAC,CAAC,QAAQ,CAAC;QACd,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YAChC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3C,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC;YAC3E,CAAC;YACD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QACxD,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,UAAU;QACT,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;CACD","sourcesContent":["/**\n * The look of a tool block, defined in one place.\n *\n * A tool block is a marker line plus a rail:\n *\n * ● grep /useState/ in src marker: the tool's state\n * │ src/a.tsx:12: useState() rail: everything that belongs to the same tool\n *\n * Consecutive tool blocks sit directly under each other; text keeps a blank line. Change the look\n * by editing `toolStyle` or `FramedComponent`; nothing else in the transcript hard-codes it.\n */\n\nimport { type Component, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { stripAnsi } from \"../../../utils/ansi.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\n\nexport type ToolStatus = \"pending\" | \"success\" | \"error\";\n\nexport interface ToolStyle {\n\tmarker: { glyph: string; color: Record<ToolStatus, ThemeColor> };\n\trail: { glyph: string; color: ThemeColor };\n\t/** Blank lines above a tool block, by what precedes it. */\n\tgap: { afterTool: number; afterOther: number };\n\tcollapsed: {\n\t\t/**\n\t\t * Tools whose successful result stays hidden until expanded. A failure always shows. The shell\n\t\t * folds only the built-in result renderer, matched by identity, so list only tools whose\n\t\t * renderers are module-level constants: `bash` and `powershell` build fresh ones and never match.\n\t\t */\n\t\theaderOnly: ReadonlySet<string>;\n\t\t/** Output tail kept for a tool that has no result renderer. */\n\t\tfallbackLines: number;\n\t};\n\t/** Width budget for the argument summary a tool without a call renderer shows. */\n\tfallbackArgsWidth: number;\n}\n\nexport const toolStyle: ToolStyle = {\n\tmarker: { glyph: \"●\", color: { pending: \"warning\", success: \"success\", error: \"error\" } },\n\trail: { glyph: \"│\", color: \"dim\" },\n\tgap: { afterTool: 0, afterOther: 1 },\n\tcollapsed: { headerOnly: new Set([\"read\", \"grep\", \"find\", \"ls\"]), fallbackLines: 10 },\n\tfallbackArgsWidth: 120,\n};\n\nexport function toolStatus(state: { isPartial: boolean; isError: boolean }): ToolStatus {\n\treturn state.isPartial ? \"pending\" : state.isError ? \"error\" : \"success\";\n}\n\nexport function toolMarkerColor(status: ToolStatus): ThemeColor {\n\treturn toolStyle.marker.color[status];\n}\n\nfunction cellWidth(): number {\n\treturn Math.max(visibleWidth(toolStyle.marker.glyph), visibleWidth(toolStyle.rail.glyph));\n}\n\n/** Columns the marker or rail takes, including the space before the content. */\nexport function gutterWidth(): number {\n\treturn cellWidth() + 1;\n}\n\nfunction gutterCell(glyph: string, color: ThemeColor): string {\n\treturn `${theme.fg(color, glyph)}${\" \".repeat(cellWidth() - visibleWidth(glyph) + 1)}`;\n}\n\n/** A line that shows nothing: empty, or only spaces once styling is removed (Text pads its lines). */\nfunction isBlankLine(line: string): boolean {\n\treturn line === \"\" || stripAnsi(line).trim() === \"\";\n}\n\nfunction dropLeadingBlankLines(lines: string[]): string[] {\n\tlet start = 0;\n\twhile (start < lines.length && isBlankLine(lines[start]!)) start++;\n\treturn lines.slice(start);\n}\n\n/**\n * Hangs a component's lines off the marker (header) or the rail (body). A header's first line\n * carries the marker; every other line, and every body line, carries the rail, and a blank line\n * keeps a bare rail so the block stays connected.\n */\nexport class FramedComponent implements Component {\n\tprivate component: Component;\n\tprivate kind: \"header\" | \"body\";\n\tprivate markerColor: () => ThemeColor;\n\n\tconstructor(component: Component, kind: \"header\" | \"body\", markerColor: () => ThemeColor) {\n\t\tthis.component = component;\n\t\tthis.kind = kind;\n\t\tthis.markerColor = markerColor;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst rendered = this.component.render(Math.max(1, width - gutterWidth()));\n\t\t// A body starts at its first visible line, and a header keeps its second line snug against the\n\t\t// marker line: renderers written for padded cards separate a title from its preview with blanks.\n\t\tconst lines =\n\t\t\tthis.kind === \"body\"\n\t\t\t\t? dropLeadingBlankLines(rendered)\n\t\t\t\t: rendered.length > 1\n\t\t\t\t\t? [rendered[0]!, ...dropLeadingBlankLines(rendered.slice(1))]\n\t\t\t\t\t: rendered;\n\t\tif (lines.length === 0) return [];\n\t\tconst rail = gutterCell(toolStyle.rail.glyph, toolStyle.rail.color);\n\t\tconst bareRail = theme.fg(toolStyle.rail.color, toolStyle.rail.glyph);\n\t\treturn lines.map((line, index) => {\n\t\t\tif (index === 0 && this.kind === \"header\") {\n\t\t\t\treturn `${gutterCell(toolStyle.marker.glyph, this.markerColor())}${line}`;\n\t\t\t}\n\t\t\treturn isBlankLine(line) ? bareRail : `${rail}${line}`;\n\t\t});\n\t}\n\n\tinvalidate(): void {\n\t\tthis.component.invalidate();\n\t}\n}\n"]}
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../../../src/modes/interactive/tool-view/style.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The look of a tool block, defined in one place.\n *\n * A tool block is a marker line plus a rail:\n *\n * ● grep /useState/ in src marker: the tool's state\n * │ src/a.tsx:12: useState() rail: everything that belongs to the same tool\n *\n * Single-line tool blocks sit directly under each other; multiline blocks and text keep a blank line. Change the look\n * by editing `toolStyle` or `FramedComponent`; nothing else in the transcript hard-codes it.\n */\n\nimport { type Component, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { stripAnsi } from \"../../../utils/ansi.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\n\nexport type ToolStatus = \"pending\" | \"success\" | \"error\";\n\nexport interface ToolStyle {\n\tmarker: { glyph: string; color: Record<ToolStatus, ThemeColor> };\n\trail: { glyph: string; color: ThemeColor };\n\t/** Blank lines above a tool block, by what precedes it. */\n\tgap: { afterTool: number; afterMultilineTool: number; afterOther: number };\n\tcollapsed: {\n\t\t/**\n\t\t * Tools whose successful result stays hidden until expanded. A failure always shows. The shell\n\t\t * folds only the built-in result renderer, matched by identity, so list only tools whose\n\t\t * renderers are module-level constants: `bash` and `powershell` build fresh ones and never match.\n\t\t */\n\t\theaderOnly: ReadonlySet<string>;\n\t\t/** Output tail kept for a tool that has no result renderer. */\n\t\tfallbackLines: number;\n\t};\n\t/** Width budget for the argument summary a tool without a call renderer shows. */\n\tfallbackArgsWidth: number;\n}\n\nexport const toolStyle: ToolStyle = {\n\tmarker: { glyph: \"●\", color: { pending: \"warning\", success: \"success\", error: \"error\" } },\n\trail: { glyph: \"│\", color: \"dim\" },\n\tgap: { afterTool: 0, afterMultilineTool: 1, afterOther: 1 },\n\tcollapsed: { headerOnly: new Set([\"read\", \"grep\", \"find\", \"ls\"]), fallbackLines: 10 },\n\tfallbackArgsWidth: 120,\n};\n\nexport function toolStatus(state: { isPartial: boolean; isError: boolean }): ToolStatus {\n\treturn state.isPartial ? \"pending\" : state.isError ? \"error\" : \"success\";\n}\n\nexport function toolMarkerColor(status: ToolStatus): ThemeColor {\n\treturn toolStyle.marker.color[status];\n}\n\nfunction cellWidth(): number {\n\treturn Math.max(visibleWidth(toolStyle.marker.glyph), visibleWidth(toolStyle.rail.glyph));\n}\n\n/** Columns the marker or rail takes, including the space before the content. */\nexport function gutterWidth(): number {\n\treturn cellWidth() + 1;\n}\n\nfunction gutterCell(glyph: string, color: ThemeColor): string {\n\treturn `${theme.fg(color, glyph)}${\" \".repeat(cellWidth() - visibleWidth(glyph) + 1)}`;\n}\n\n/** A line that shows nothing: empty, or only spaces once styling is removed (Text pads its lines). */\nfunction isBlankLine(line: string): boolean {\n\treturn line === \"\" || stripAnsi(line).trim() === \"\";\n}\n\nfunction dropLeadingBlankLines(lines: string[]): string[] {\n\tlet start = 0;\n\twhile (start < lines.length && isBlankLine(lines[start]!)) start++;\n\treturn lines.slice(start);\n}\n\n/**\n * Hangs a component's lines off the marker (header) or the rail (body). A header's first line\n * carries the marker; every other line, and every body line, carries the rail, and a blank line\n * keeps a bare rail so the block stays connected.\n */\nexport class FramedComponent implements Component {\n\tprivate component: Component;\n\tprivate kind: \"header\" | \"body\";\n\tprivate markerColor: () => ThemeColor;\n\n\tconstructor(component: Component, kind: \"header\" | \"body\", markerColor: () => ThemeColor) {\n\t\tthis.component = component;\n\t\tthis.kind = kind;\n\t\tthis.markerColor = markerColor;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst rendered = this.component.render(Math.max(1, width - gutterWidth()));\n\t\t// A body starts at its first visible line, and a header keeps its second line snug against the\n\t\t// marker line: renderers written for padded cards separate a title from its preview with blanks.\n\t\tconst lines =\n\t\t\tthis.kind === \"body\"\n\t\t\t\t? dropLeadingBlankLines(rendered)\n\t\t\t\t: rendered.length > 1\n\t\t\t\t\t? [rendered[0]!, ...dropLeadingBlankLines(rendered.slice(1))]\n\t\t\t\t\t: rendered;\n\t\tif (lines.length === 0) return [];\n\t\tconst rail = gutterCell(toolStyle.rail.glyph, toolStyle.rail.color);\n\t\tconst bareRail = theme.fg(toolStyle.rail.color, toolStyle.rail.glyph);\n\t\treturn lines.map((line, index) => {\n\t\t\tif (index === 0 && this.kind === \"header\") {\n\t\t\t\treturn `${gutterCell(toolStyle.marker.glyph, this.markerColor())}${line}`;\n\t\t\t}\n\t\t\treturn isBlankLine(line) ? bareRail : `${rail}${line}`;\n\t\t});\n\t}\n\n\tinvalidate(): void {\n\t\tthis.component.invalidate();\n\t}\n}\n"]}
@@ -12,7 +12,7 @@ Start Bash through the native `bash` tool (the optional native Windows `powershe
12
12
 
13
13
  Omitting `background` keeps the normal foreground wait. A background submission returns an execution reference, not a successful final outcome. Up to eight background executions run at once; a ninth background submission is rejected without starting. Foreground commands are never limited by background work. `bg create` has been removed; its old stored results and background notifications show their saved text in transcripts.
14
14
 
15
- In interactive mode, **Ctrl+B** moves eligible foreground managed tasks to the background, oldest first, while background slots remain; when every slot is taken it says so instead. Once a foreground execution has run for ten seconds and can still move, the statusline shows `Ctrl+B to background`. It works even when `/tasks` owns focus. The same execution continues: no cancellation, restart or timeout reset. It does not detach ordinary file tools, or user `!` shell commands.
15
+ In interactive mode, **Ctrl+B** moves eligible foreground managed tasks to the background, oldest first, while background slots remain; when every slot is taken it says so instead. Once a foreground execution has run for ten seconds and can still move, the statusline shows `Ctrl+B to run in background`, even when no background tasks exist. The hint is hidden while every background slot is taken, background execution is disabled, or no eligible foreground execution remains. It uses the configured shortcut and disappears if that shortcut is disabled. The shortcut works even when `/tasks` owns focus. The same execution continues: no cancellation, restart or timeout reset. The handoff result names the path the work took, because the model cannot see it: a command submitted with `background: true` reports a handoff from an empty log, while one moved mid-run reports that its execution continued unchanged, that the log already holds output, and that a supplied timeout is still measured from command startup. It does not detach ordinary file tools, or user `!` shell commands.
16
16
 
17
17
  When nothing is eligible, the key is not consumed: it falls through to other bindings instead of reporting a no-op.
18
18
 
@@ -80,7 +80,7 @@ Bash and PowerShell show live plain-text output. Other executors show their publ
80
80
  | Escape | Close the current search, help or confirmation first; otherwise close the panel |
81
81
  | Ctrl+B | Detach **all** eligible foreground executions through the host |
82
82
 
83
- Click a pane to focus it, a task row to select it, or a tab label to display it. The mouse wheel scrolls the pane under the pointer without changing keyboard focus or task selection. Search, help and confirmation consume their own input without activating the panel behind them.
83
+ Click a pane to focus it, a task row to select it, or a tab label to display it. The mouse wheel scrolls the pane under the pointer without changing keyboard focus or task selection. Footer hints are clickable as well, including while a transient confirmation owns that row: the click dismisses it and performs the hint's action. Search, help and confirmation consume their own input without activating the panel behind them.
84
84
 
85
85
  Shell output initially follows the tail, marked **Live** next to the output. Scrolling up enters **Browsing**, which holds a bounded output snapshot while status continues updating. In Output, press `f`, End, or scroll down to the bottom to resume following. If the task finishes while browsing, the panel offers to load its final output. Closing or scrolling never pauses, restarts or cancels execution.
86
86
 
@@ -88,7 +88,9 @@ The panel polls only selected tail output once per second, up to 48 KiB per read
88
88
 
89
89
  Controls use the `app.tasks.*` bindings: `search`, `previousTab`, `nextTab`, `nextFocus`, `previousFocus`, `top`, `bottom`, `follow`, `kill`, `confirmStop`, `detachSelected`, `detach` and `help`. List paging uses `tui.select.pageUp` / `pageDown`; Details and Output paging use `tui.editor.pageUp` / `pageDown`. All are configurable. The former view and direct-focus bindings have been removed.
90
90
 
91
- The statusline counts all active managed work and points to `/tasks`. When only retained background results remain, it labels that scope explicitly. The model-facing `tasks list` keeps its background-only scope and foreground omission count.
91
+ The `/hotkeys` table lists the task bindings next to the editor ones, and the panel's `?` help lists every control, so both detach scopes are discoverable without reading these docs.
92
+
93
+ The statusline counts only queued, running and stopping background tasks: `1 background task · /tasks to view` or `2 background tasks · /tasks to view`. Foreground tasks and retained results do not contribute to the count; the summary disappears when no active background tasks remain. Finished results remain available in `/tasks`. The independent detach hint can appear alone or after the summary: `1 background task · /tasks to view · Ctrl+B to run in background`. The count uses normal text, `/tasks` and the shortcut use the accent color, and descriptions and separators are muted. The model-facing `tasks list` keeps its background-only scope and foreground omission count.
92
94
 
93
95
  ## Completion notifications
94
96
 
@@ -13,6 +13,7 @@ I'll look at the code.
13
13
  │ 1 const a = 0
14
14
  │ -2 const b = 1
15
15
  │ +2 const b = 2
16
+
16
17
  ● $ npm test
17
18
  │ ... (14 earlier lines, ctrl+o to expand)
18
19
  │ FAIL a.test.ts
@@ -26,19 +27,19 @@ The tests failed.
26
27
 
27
28
  - The dot is the tool's state: the warning color while the call is pending or running, green after success, and red after failure.
28
29
  - The rail continues through every visual line that belongs to the same tool. A blank output line renders as a bare `│`.
29
- - Tool blocks sit directly under each other with no blank line. Text before or after a run of tools keeps one blank line.
30
+ - Consecutive single-line tool blocks sit directly under each other. A multiline tool block keeps one blank line before the next tool; wrapped titles count as multiline too. Spacing follows the current width and updates when tools expand, collapse, or receive output. Hidden tool rows add no spacing. Text before or after a run of tools keeps one blank line.
30
31
  - `read`, `grep`, `find`, and `ls` show only their header until expanded. A failed call always shows its error.
31
32
  - Consecutive calls never merge into a group. Every call keeps its own row, and the configured expand-tools key (`Ctrl+O` by default) or a click on the row shows its complete result.
32
33
 
33
34
  The bash renderer shows its own `Elapsed` line while a command runs and a fixed `Took` line after it settles. The shell adds no progress row, and other renderers that refresh over time schedule their own repaints: they arm a timer in renderer state, call the render context's `invalidate()`, and clear the timer on the first settled render. The shell calls `state.dispose()` when a row is disposed, so a timer never outlives its row.
34
35
 
35
- The `Ctrl+B` background hint is not part of a tool row. Once a foreground managed execution has run for ten seconds and can still move, the built-in Tasks UI shows `Ctrl+B to background` in the statusline, next to the `tasks N active` count.
36
+ The `Ctrl+B` background hint is not part of a tool row. Once a foreground managed execution has run for ten seconds and can still move, the built-in Tasks UI shows `Ctrl+B to run in background` in the statusline. It appears on its own when only foreground work exists, or after `1 background task · /tasks to view` (pluralized for multiple tasks) while background work is active. Finished tasks do not keep the summary visible.
36
37
 
37
38
  ## Implementation boundary
38
39
 
39
40
  The look is defined in one place, `src/modes/interactive/tool-view/style.ts`: the marker and rail glyphs, their colors, the spacing between blocks, and which tools fold their result. `FramedComponent` in the same file hangs a component's lines off the marker or the rail and derives the gutter width from the glyphs.
40
41
 
41
- `tool-view/tool-execution.ts` owns one call's lifecycle: pending, success and error states, expansion, image placement and Kitty conversion, and renderer disposal. `tool-view/chat.ts` owns the chat container that removes the blank line between consecutive tool rows and disposes rows when the chat clears. Extension tools and built-in tools use the same shell, so a third-party tool gets the same dot and rail without any change.
42
+ `tool-view/tool-execution.ts` owns one call's lifecycle: pending, success and error states, expansion, image placement and Kitty conversion, and renderer disposal. `tool-view/chat.ts` owns the chat container that measures tool blocks for spacing and disposes rows when the chat clears. Extension tools and built-in tools use the same shell, so a third-party tool gets the same dot and rail without any change.
42
43
 
43
44
  Built-in renderers remain responsible for semantic content such as file paths, syntax highlighting, search results, Diff previews, and command output. They return plain components; the shell frames them.
44
45
 
@@ -1164,6 +1164,7 @@ Session-bound `TasksContext` supervises native and extension-owned execution. Fo
1164
1164
  | `wait(id, timeoutMs?, signal?)` | Observation only; timeout or cancellation ends the wait, not execution |
1165
1165
  | `kill(id)` | Requests cancellation; `stopping` is not a terminal status |
1166
1166
  | `detach(id)` / `detachForeground()` | Hands off one task, or eligible foreground tasks oldest first while background slots remain, without restarting them; throws when every background slot is taken |
1167
+ | `canDetach(id)` | Reports whether an execution can move now, including host policy and available background slots; does not change the execution |
1167
1168
  | `subscribe(listener)` | Observes task state and progress |
1168
1169
  | `retain(id)` | Returns an idempotent release function protecting retained data, without delaying notifications |
1169
1170
  | `holdDelivery(id)` | Explicitly retains data and delays automatic delivery until released |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@astralyn/pi",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@astralyn/pi",
9
- "version": "1.0.1",
9
+ "version": "1.0.3",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@earendil-works/chord": "1.0.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astralyn/pi",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {